summaryrefslogtreecommitdiff
path: root/src/conf_mode/interfaces-openvpn.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode/interfaces-openvpn.py')
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py111
1 files changed, 77 insertions, 34 deletions
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 836deb64b..029bc1d69 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -25,12 +25,12 @@ from time import sleep
from shutil import rmtree
from vyos.config import Config
+from vyos.configdict import list_diff
from vyos.ifconfig import VTunIf
-from vyos.util import call, is_bridge_member, chown, chmod_600, chmod_755
-from vyos.validate import is_addr_assigned
-from vyos import ConfigError
from vyos.template import render
-
+from vyos.util import call, chown, chmod_600, chmod_755
+from vyos.validate import is_addr_assigned, is_bridge_member, is_ipv4
+from vyos import ConfigError
user = 'openvpn'
group = 'openvpn'
@@ -39,6 +39,7 @@ default_config_data = {
'address': [],
'auth_user': '',
'auth_pass': '',
+ 'auth_user_pass_file': '',
'auth': False,
'bridge_member': [],
'compress_lzo': False,
@@ -50,11 +51,13 @@ default_config_data = {
'hash': '',
'intf': '',
'ipv6_autoconf': 0,
- 'ipv6_eui64_prefix': '',
+ 'ipv6_eui64_prefix': [],
+ 'ipv6_eui64_prefix_remove': [],
'ipv6_forwarding': 1,
'ipv6_dup_addr_detect': 1,
'ipv6_local_address': [],
'ipv6_remote_address': [],
+ 'is_bridge_member': False,
'ping_restart': '60',
'ping_interval': '10',
'local_address': [],
@@ -66,6 +69,7 @@ default_config_data = {
'options': [],
'persistent_tunnel': False,
'protocol': 'udp',
+ 'protocol_real': '',
'redirect_gateway': '',
'remote_address': [],
'remote_host': [],
@@ -193,10 +197,13 @@ def get_config():
raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified')
openvpn['intf'] = os.environ['VYOS_TAGNODE_VALUE']
+ openvpn['auth_user_pass_file'] = f"/run/openvpn/{openvpn['intf']}.pw"
# Check if interface instance has been removed
if not conf.exists('interfaces openvpn ' + openvpn['intf']):
openvpn['deleted'] = True
+ # check if interface is member if a bridge
+ openvpn['is_bridge_member'] = is_bridge_member(conf, openvpn['intf'])
return openvpn
# Check if we belong to any bridge interface
@@ -309,9 +316,21 @@ def get_config():
if conf.exists('ipv6 address autoconf'):
openvpn['ipv6_autoconf'] = 1
- # Get prefix for IPv6 addressing based on MAC address (EUI-64)
+ # Get prefixes for IPv6 addressing based on MAC address (EUI-64)
if conf.exists('ipv6 address eui64'):
- openvpn['ipv6_eui64_prefix'] = conf.return_value('ipv6 address eui64')
+ openvpn['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
+
+ # Determine currently effective EUI64 addresses - to determine which
+ # address is no longer valid and needs to be removed
+ eff_addr = conf.return_effective_values('ipv6 address eui64')
+ openvpn['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, openvpn['ipv6_eui64_prefix'])
+
+ # Remove the default link-local address if set.
+ if conf.exists('ipv6 address no-default-link-local'):
+ openvpn['ipv6_eui64_prefix_remove'].append('fe80::/64')
+ else:
+ # add the link-local by default to make IPv6 work
+ openvpn['ipv6_eui64_prefix'].append('fe80::/64')
# Disable IPv6 forwarding on this interface
if conf.exists('ipv6 disable-forwarding'):
@@ -553,6 +572,23 @@ def get_config():
if openvpn['mode'] == 'server' and not openvpn['server_topology']:
openvpn['server_topology'] = 'net30'
+ # Convert protocol to real protocol used by openvpn.
+ # To make openvpn listen on both IPv4 and IPv6 we must use *6 protocols
+ # (https://community.openvpn.net/openvpn/ticket/360), unless local is IPv4
+ # in which case it must use the standard protocols.
+ # Note: this will break openvpn if IPv6 is disabled on the system.
+ # This currently isn't supported, a check can be added in the future.
+ if openvpn['protocol'] == 'tcp-active':
+ openvpn['protocol_real'] = 'tcp6-client'
+ elif openvpn['protocol'] == 'tcp-passive':
+ openvpn['protocol_real'] = 'tcp6-server'
+ else:
+ openvpn['protocol_real'] = 'udp6'
+
+ if is_ipv4(openvpn['local_host']):
+ # takes out the '6'
+ openvpn['protocol_real'] = openvpn['protocol_real'][:3] + openvpn['protocol_real'][4:]
+
# Set defaults where necessary.
# If any of the input parameters are wrong,
# this will return False and no defaults will be set.
@@ -598,13 +634,11 @@ def get_config():
def verify(openvpn):
if openvpn['deleted']:
- interface = openvpn['intf']
- is_member, bridge = is_bridge_member(interface)
- if is_member:
- # can not use a f'' formatted-string here as bridge would not get
- # expanded in the print statement
- raise ConfigError('Can not delete interface "{0}" as it ' \
- 'is a member of bridge "{1}"!'.format(interface, bridge))
+ if openvpn['is_bridge_member']:
+ interface = openvpn['intf']
+ bridge = openvpn['is_bridge_member']
+ raise ConfigError(f'Interface "{interface}" can not be deleted as it belongs to bridge "{bridge}"!')
+
return None
@@ -917,18 +951,18 @@ def verify(openvpn):
return None
def generate(openvpn):
- if openvpn['deleted'] or openvpn['disable']:
- return None
-
interface = openvpn['intf']
directory = os.path.dirname(get_config_name(interface))
- # we can't know in advance which clients have been,
- # remove all client configs
+ # we can't know in advance which clients have been removed,
+ # thus all client configs will be removed and re-added on demand
ccd_dir = os.path.join(directory, 'ccd', interface)
if os.path.isdir(ccd_dir):
rmtree(ccd_dir, ignore_errors=True)
+ if openvpn['deleted'] or openvpn['disable']:
+ return None
+
# create config directory on demand
directories = []
directories.append(f'{directory}/status')
@@ -944,17 +978,16 @@ def generate(openvpn):
fix_permissions.append(openvpn['tls_key'])
# Generate User/Password authentication file
- user_auth_file = f'/tmp/openvpn-{interface}-pw'
if openvpn['auth']:
- with open(user_auth_file, 'w') as f:
+ with open(openvpn['auth_user_pass_file'], 'w') as f:
f.write('{}\n{}'.format(openvpn['auth_user'], openvpn['auth_pass']))
# also change permission on auth file
- fix_permissions.append(user_auth_file)
+ fix_permissions.append(openvpn['auth_user_pass_file'])
else:
# delete old auth file if present
- if os.path.isfile(user_auth_file):
- os.remove(user_auth_file)
+ if os.path.isfile(openvpn['auth_user_pass_file']):
+ os.remove(openvpn['auth_user_pass_file'])
# Generate client specific configuration
for client in openvpn['client']:
@@ -980,15 +1013,14 @@ def apply(openvpn):
# Do some cleanup when OpenVPN is disabled/deleted
if openvpn['deleted'] or openvpn['disable']:
- # cleanup old configuration file
- if os.path.isfile(get_config_name(interface)):
- os.remove(get_config_name(interface))
+ # cleanup old configuration files
+ cleanup = []
+ cleanup.append(get_config_name(interface))
+ cleanup.append(openvpn['auth_user_pass_file'])
- # cleanup client config dir
- directory = os.path.dirname(get_config_name(interface))
- ccd_dir = os.path.join(directory, 'ccd', interface)
- if os.path.isdir(ccd_dir):
- rmtree(ccd_dir, ignore_errors=True)
+ for file in cleanup:
+ if os.path.isfile(file):
+ os.unlink(file)
return None
@@ -1025,13 +1057,24 @@ def apply(openvpn):
o.set_alias(openvpn['description'])
# IPv6 address autoconfiguration
o.set_ipv6_autoconf(openvpn['ipv6_autoconf'])
- # IPv6 EUI-based address
- o.set_ipv6_eui64_address(openvpn['ipv6_eui64_prefix'])
# IPv6 forwarding
o.set_ipv6_forwarding(openvpn['ipv6_forwarding'])
# IPv6 Duplicate Address Detection (DAD) tries
o.set_ipv6_dad_messages(openvpn['ipv6_dup_addr_detect'])
+ # IPv6 EUI-based addresses - only in TAP mode (TUN's have no MAC)
+ # If MAC has changed, old EUI64 addresses won't get deleted,
+ # but this isn't easy to solve, so leave them.
+ # This is even more difficult as openvpn uses a random MAC for the
+ # initial interface creation, unless set by 'lladdr'.
+ # NOTE: right now the interface is always deleted. For future
+ # compatibility when tap's are not deleted, leave the del_ in
+ if openvpn['mode'] == 'tap':
+ for addr in openvpn['ipv6_eui64_prefix_remove']:
+ o.del_ipv6_eui64_address(addr)
+ for addr in openvpn['ipv6_eui64_prefix']:
+ o.add_ipv6_eui64_address(addr)
+
except:
pass