diff options
-rw-r--r-- | interface-definitions/interfaces-openvpn.xml.in | 41 | ||||
-rw-r--r-- | op-mode-definitions/openvpn.xml.in | 28 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-openvpn.py | 12 |
3 files changed, 75 insertions, 6 deletions
diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 1a07e7d91..b9575595c 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -648,32 +648,71 @@ <leafNode name="slop"> <properties> <help>Maximum allowed clock slop in seconds (default: 180)</help> + <valueHelp> + <format>1-65535</format> + <description>Seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> </properties> <defaultValue>180</defaultValue> </leafNode> <leafNode name="t0"> <properties> <help>time drift in seconds (default: 0)</help> + <valueHelp> + <format>1-65535</format> + <description>Seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> </properties> <defaultValue>0</defaultValue> </leafNode> <leafNode name="step"> <properties> <help>Step value for TOTP in seconds (default: 30)</help> + <valueHelp> + <format>1-65535</format> + <description>Seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> </properties> <defaultValue>30</defaultValue> </leafNode> <leafNode name="digits"> <properties> <help>Number of digits to use from TOTP hash (default: 6)</help> + <valueHelp> + <format>1-65535</format> + <description>Seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> </properties> <defaultValue>6</defaultValue> </leafNode> <leafNode name="challenge"> <properties> <help>expect password as result of a challenge response protocol (default: enabled)</help> + <completionHelp> + <list>disable enable</list> + </completionHelp> + <valueHelp> + <format>disable</format> + <description>Disable challenge response (default)</description> + </valueHelp> + <valueHelp> + <format>enable</format> + <description>Enable chalenge response (default)</description> + </valueHelp> <constraint> - <regex>^(enable|disable)$</regex> + <regex>^(disable|enable)$</regex> </constraint> </properties> <defaultValue>enable</defaultValue> diff --git a/op-mode-definitions/openvpn.xml.in b/op-mode-definitions/openvpn.xml.in index 781fbdc9d..ee3b073b5 100644 --- a/op-mode-definitions/openvpn.xml.in +++ b/op-mode-definitions/openvpn.xml.in @@ -55,6 +55,34 @@ </properties> <command>${vyos_op_scripts_dir}/show_interfaces.py --intf=$4</command> <children> + <tagNode name="user"> + <properties> + <help>Show OpenVPN interface users</help> + <completionHelp> + <script>sudo ${vyos_completion_dir}/list_openvpn_users.py --interface "$4"</script> + </completionHelp> + </properties> + <children> + <leafNode name="2fa secret"> + <properties> + <help>Show 2fa authentication secret</help> + </properties> + <command>${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$4" --intf="$6" --action=sercret</command> + </leafNode> + <leafNode name="2fa otpauth uri"> + <properties> + <help>Show 2fa otpauth uri</help> + </properties> + <command>${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$4" --intf="$6" --action=uri</command> + </leafNode> + <leafNode name="2fa QR code"> + <properties> + <help>Show 2fa QR code</help> + </properties> + <command>${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$4" --intf="$6" --action=qrcode</command> + </leafNode> + </children> + </tagNode> <leafNode name="brief"> <properties> <help>Show summary of specified OpenVPN interface information</help> diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index f19804910..8ccfee6ef 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -61,6 +61,9 @@ group = 'openvpn' cfg_dir = '/run/openvpn' cfg_file = '/run/openvpn/{ifname}.conf' +otp_path = '/config/auth/openvpn' +otp_file = '/config/auth/openvpn/{ifname}-otp-secrets' +secret_chars = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') def get_config(config=None): """ @@ -310,7 +313,6 @@ def verify(openvpn): if 'is_bridge_member' not in openvpn: raise ConfigError('Must specify "server subnet" or add interface to bridge in server mode') - for client_k, client_v in (dict_search('server.client', openvpn).items() or []): if (client_v.get('ip') and len(client_v['ip']) > 1) or (client_v.get('ipv6_ip') and len(client_v['ipv6_ip']) > 1): raise ConfigError(f'Server client "{client_k}": cannot specify more than 1 IPv4 and 1 IPv6 IP') @@ -362,10 +364,11 @@ def verify(openvpn): print(f'Warning: Client "{client["name"]}" IP {client["ipv6_ip"][0]} is in server IP pool, it is not reserved for this client.') if dict_search('server.2fa.totp', openvpn): - if not Path(otp_file).is_file(): - Path(otp_file).touch() + if not Path(otp_file.format(**openvpn)).is_file(): + Path(otp_path).mkdir(parents=True, exist_ok=True) + Path(otp_file.format(**openvpn)).touch() for client in (dict_search('server.client', openvpn) or []): - with open(otp_file, "r+") as f: + with open(otp_file.format(**openvpn), "r+") as f: users = f.readlines() exists = None for user in users: @@ -377,7 +380,6 @@ def verify(openvpn): totp_secret = ''.join(random.choice(secret_chars) for _ in range(16)) f.write("{0} otp totp:sha1:base32:{1}::xxx *\n".format(client, totp_secret)) - else: # checks for both client and site-to-site go here if dict_search('server.reject_unconfigured_clients', openvpn): |