diff --git a/config/aaa.py b/config/aaa.py new file mode 100644 index 0000000000..dbab270f10 --- /dev/null +++ b/config/aaa.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python -u +# -*- coding: utf-8 -*- + +import click +import netaddr +from swsssdk import ConfigDBConnector + + +def is_ipaddress(val): + if not val: + return False + try: + netaddr.IPAddress(str(val)) + except: + return False + return True + + +def add_table_kv(table, entry, key, val): + config_db = ConfigDBConnector() + config_db.connect() + config_db.mod_entry(table, entry, {key:val}) + + +def del_table_key(table, entry, key): + config_db = ConfigDBConnector() + config_db.connect() + data = config_db.get_entry(table, entry) + if data: + if key in data: + del data[key] + config_db.set_entry(table, entry, data) + + +@click.group() +def aaa(): + """AAA command line""" + pass + + +# cmd: aaa authentication +@click.group() +def authentication(): + """User authentication""" + pass +aaa.add_command(authentication) + + +# cmd: aaa authentication failthrough +@click.command() +@click.argument('option', type=click.Choice(["enable", "disable", "default"])) +def failthrough(option): + """Allow AAA fail-through [enable | disable | default]""" + if option == 'default': + del_table_key('AAA', 'authentication', 'failthrough') + else: + if option == 'enable': + add_table_kv('AAA', 'authentication', 'failthrough', True) + elif option == 'disable': + add_table_kv('AAA', 'authentication', 'failthrough', False) +authentication.add_command(failthrough) + + +# cmd: aaa authentication fallback +@click.command() +@click.argument('option', type=click.Choice(["enable", "disable", "default"])) +def fallback(option): + """Allow AAA fallback [enable | disable | default]""" + if option == 'default': + del_table_key('AAA', 'authentication', 'fallback') + else: + if option == 'enable': + add_table_kv('AAA', 'authentication', 'fallback', True) + elif option == 'disable': + add_table_kv('AAA', 'authentication', 'fallback', False) +authentication.add_command(fallback) + + +@click.command() +@click.argument('auth_protocol', nargs=-1, type=click.Choice(["tacacs+", "local", "default"])) +def login(auth_protocol): + """Switch login authentication [ {tacacs+, local} | default ]""" + if len(auth_protocol) is 0: + print 'Not support empty argument' + return + + if 'default' in auth_protocol: + del_table_key('AAA', 'authentication', 'login') + else: + val = auth_protocol[0] + if len(auth_protocol) == 2: + val += ',' + auth_protocol[1] + add_table_kv('AAA', 'authentication', 'login', val) +authentication.add_command(login) + + +@click.group() +def tacacs(): + """TACACS+ server configuration""" + pass + + +@click.group() +@click.pass_context +def default(ctx): + """set its default configuration""" + ctx.obj = 'default' +tacacs.add_command(default) + + +@click.command() +@click.argument('second', metavar='', type=click.IntRange(0, 60), required=False) +@click.pass_context +def timeout(ctx, second): + """Specify TACACS+ server global timeout <0 - 60>""" + if ctx.obj == 'default': + del_table_key('TACPLUS', 'global', 'timeout') + elif second: + add_table_kv('TACPLUS', 'global', 'timeout', second) + else: + click.echo('Not support empty argument') +tacacs.add_command(timeout) +default.add_command(timeout) + + +@click.command() +@click.argument('type', metavar='', type=click.Choice(["chap", "pap", "mschap"]), required=False) +@click.pass_context +def authtype(ctx, type): + """Specify TACACS+ server global auth_type [chap | pap | mschap]""" + if ctx.obj == 'default': + del_table_key('TACPLUS', 'global', 'auth_type') + elif type: + add_table_kv('TACPLUS', 'global', 'auth_type', type) + else: + click.echo('Not support empty argument') +tacacs.add_command(authtype) +default.add_command(authtype) + + +@click.command() +@click.argument('secret', metavar='', required=False) +@click.pass_context +def passkey(ctx, secret): + """Specify TACACS+ server global passkey """ + if ctx.obj == 'default': + del_table_key('TACPLUS', 'global', 'passkey') + elif secret: + add_table_kv('TACPLUS', 'global', 'passkey', secret) + else: + click.echo('Not support empty argument') +tacacs.add_command(passkey) +default.add_command(passkey) + + +# cmd: tacacs add --timeout SECOND --key SECRET --type TYPE --port PORT --pri PRIORITY +@click.command() +@click.argument('address', metavar='') +@click.option('-t', '--timeout', help='Transmission timeout interval, default 5', type=int) +@click.option('-k', '--key', help='Shared secret') +@click.option('-a', '--auth_type', help='Authentication type, default pap', type=click.Choice(["chap", "pap", "mschap"])) +@click.option('-o', '--port', help='TCP port range is 1 to 65535, default 49', type=click.IntRange(1, 65535), default=49) +@click.option('-p', '--pri', help="Priority, default 1", type=click.IntRange(1, 64), default=1) +def add(address, timeout, key, auth_type, port, pri): + """Specify a TACACS+ server""" + if not is_ipaddress(address): + click.echo('Invalid ip address') + return + + config_db = ConfigDBConnector() + config_db.connect() + old_data = config_db.get_entry('TACPLUS_SERVER', address) + if old_data != {}: + click.echo('server %s already exists' % address) + else: + data = { + 'tcp_port': str(port), + 'priority': pri + } + if auth_type is not None: + data['auth_type'] = auth_type + if timeout is not None: + data['timeout'] = str(timeout) + if key is not None: + data['passkey'] = key + config_db.set_entry('TACPLUS_SERVER', address, data) +tacacs.add_command(add) + + +# cmd: tacacs delete +# 'del' is keyword, replace with 'delete' +@click.command() +@click.argument('address', metavar='') +def delete(address): + """Delete a TACACS+ server""" + if not is_ipaddress(address): + click.echo('Invalid ip address') + return + + config_db = ConfigDBConnector() + config_db.connect() + config_db.set_entry('TACPLUS_SERVER', address, None) +tacacs.add_command(delete) + + +if __name__ == "__main__": + aaa() + diff --git a/config/main.py b/config/main.py index 7572513a4e..8c48f16c3d 100755 --- a/config/main.py +++ b/config/main.py @@ -9,6 +9,8 @@ from swsssdk import ConfigDBConnector from minigraph import parse_device_desc_xml +import aaa + SONIC_CFGGEN_PATH = "sonic-cfggen" MINIGRAPH_PATH = "/etc/sonic/minigraph.xml" MINIGRAPH_BGP_SESSIONS = "minigraph_bgp" @@ -112,6 +114,8 @@ def cli(): """SONiC command line - 'config' command""" if os.geteuid() != 0: exit("Root privileges are required for this operation") +cli.add_command(aaa.aaa) +cli.add_command(aaa.tacacs) @cli.command() @click.option('-y', '--yes', is_flag=True, callback=_abort_if_false, diff --git a/setup.py b/setup.py index b4b1ae5401..4daa12125a 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ def get_test_suite(): 'scripts/lldpshow', 'scripts/port2alias', 'scripts/portstat', - 'scripts/teamshow', + 'scripts/teamshow' ], data_files=[ ('/etc/bash_completion.d', glob.glob('data/etc/bash_completion.d/*')), diff --git a/show/main.py b/show/main.py index e401c156e4..78126caffa 100755 --- a/show/main.py +++ b/show/main.py @@ -778,6 +778,58 @@ def services(): else: break +@cli.command() +def aaa(): + """Show AAA configuration in ConfigDb""" + config_db = ConfigDBConnector() + config_db.connect() + data = config_db.get_table('AAA') + output = '' + + aaa = { + 'authentication': { + 'login': 'local (default)', + 'failthrough': 'True (default)', + 'fallback': 'True (default)' + } + } + aaa['authentication'].update(data['authentication']) + for row in aaa: + entry = aaa[row] + for key in entry: + output += ('AAA %s %s %s\n' % (row, key, str(entry[key]))) + click.echo(output) + + +@cli.command() +def tacacs(): + """Show TACACS+ configuration""" + config_db = ConfigDBConnector() + config_db.connect() + output = '' + data = config_db.get_table('TACPLUS') + + tacplus = { + 'global': { + 'auth_type': 'pap (default)', + 'timeout': '5 (default)', + 'passkey': ' (default)' + } + } + tacplus['global'].update(data['global']) + for key in tacplus['global']: + output += ('TACPLUS global %s %s\n' % (str(key), str(tacplus['global'][key]))) + + data = config_db.get_table('TACPLUS_SERVER') + if data != {}: + for row in data: + entry = data[row] + output += ('\nTACPLUS_SERVER address %s\n' % row) + for key in entry: + output += (' %s %s\n' % (key, str(entry[key]))) + click.echo(output) + + # # 'session' command ### #