-
Notifications
You must be signed in to change notification settings - Fork 670
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TACACS+]: Add configuration support for TACACS+ #125
Changes from 6 commits
64cad0a
3bd7575
e87ca88
d2d67db
c940633
4aafaa2
8ad615d
cd3e3cf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
#!/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 set_entry(table, entry, data): | ||
config_db = ConfigDBConnector() | ||
config_db.connect() | ||
config_db.set_entry(table, entry, data) | ||
|
||
|
||
def add_table_kv(table, entry, key, val): | ||
config_db = ConfigDBConnector() | ||
config_db.connect() | ||
data = config_db.get_entry(table, entry) | ||
data[key] = val | ||
config_db.set_entry(table, entry, data) | ||
|
||
|
||
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]""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is the default value here? enable/disable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
when it is set to default, what is the actual value? enable or disable? same question for failthrough There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default value means there is no value for this configuration in configdb. For AAA authentication, the default is local authentication. For AAA fail-through, the default is enable. |
||
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='<time_second>', 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>', 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='<secret_string>', required=False) | ||
@click.pass_context | ||
def passkey(ctx, secret): | ||
"""Specify TACACS+ server global passkey <STRING>""" | ||
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 <ip_address> --timeout SECOND --key SECRET --type TYPE --port PORT --pri PRIORITY | ||
@click.command() | ||
@click.argument('address', metavar='<ip_address>') | ||
@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 <ip_address> | ||
# 'del' is keyword, replace with 'delete' | ||
@click.command() | ||
@click.argument('address', metavar='<ip_address>') | ||
def delete(address): | ||
"""Delete a TACACS+ server""" | ||
if not is_ipaddress(address): | ||
click.echo('Invalid ip address') | ||
return | ||
|
||
set_entry('TACPLUS_SERVER', address, None) | ||
tacacs.add_command(delete) | ||
|
||
|
||
if __name__ == "__main__": | ||
aaa() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -778,6 +778,42 @@ 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 = '' | ||
if data != {}: | ||
for row in data: | ||
entry = data[row] | ||
for key in entry: | ||
output += ('AAA %s %s %s\n' % (row, key, str(entry[key]))) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we print out the default values for fallback and fallthough? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I'll change it. |
||
click.echo(output) | ||
|
||
@cli.command() | ||
def tacacs(): | ||
"""Show TACACS+ configuration""" | ||
config_db = ConfigDBConnector() | ||
config_db.connect() | ||
output = '' | ||
data = config_db.get_table('TACPLUS') | ||
if 'global' in data: | ||
entry = data['global'] | ||
for key in entry: | ||
output += ('TACPLUS global %s %s\n' % (str(key), str(entry[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 ### | ||
# | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the semantic of set_entry is changed in this sonic-net/sonic-py-swsssdk@b7861cc.
if you still want to use the existing semantic, you can use mod_entry added in this sonic-net/sonic-py-swsssdk#25
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, it's better to use mod_entry.