summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface-definitions/interfaces-openvpn.xml.in41
-rw-r--r--op-mode-definitions/openvpn.xml.in28
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py12
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):