Page MenuHomeVyOS Platform

No OneTemporary

Size
91 KB
Referenced Files
None
Subscribers
None
diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl
index d7e7faf61..c5d665c0b 100644
--- a/data/templates/openvpn/server.conf.tmpl
+++ b/data/templates/openvpn/server.conf.tmpl
@@ -1,275 +1,273 @@
### Autogenerated by interfaces-openvpn.py ###
#
# See https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
# for individual keyword definition
#
# {{ description if description is defined and description is not none }}
#
verb 3
user {{ daemon_user }}
group {{ daemon_group }}
dev-type {{ device_type }}
dev {{ ifname }}
persist-key
{% if protocol == 'tcp-active' %}
proto tcp-client
{% elif protocol == 'tcp-passive' %}
proto tcp-server
{% else %}
proto udp
{% endif %}
{% if local_host is defined and local_host is not none %}
local {{ local_host }}
{% endif %}
{% if mode is defined and mode == 'server' and protocol == 'udp' and local_host is not defined %}
multihome
{% endif %}
{% if local_port is defined and local_port is not none %}
lport {{ local_port }}
{% endif %}
{% if remote_port is defined and remote_port is not none %}
rport {{ remote_port }}
{% endif %}
{% if remote_host is defined and remote_host is not none %}
{% for remote in remote_host %}
remote {{ remote }}
{% endfor %}
{% endif %}
{% if shared_secret_key_file is defined and shared_secret_key_file is not none %}
secret {{ shared_secret_key_file }}
{% endif %}
{% if persistent_tunnel is defined %}
persist-tun
{% endif %}
{% if replace_default_route is defined and replace_default_route.local is defined %}
push "redirect-gateway local def1"
{% elif replace_default_route is defined %}
push "redirect-gateway def1"
{% endif %}
{% if use_lzo_compression is defined %}
compress lzo
{% endif %}
{% if mode == 'client' %}
#
# OpenVPN Client mode
#
client
nobind
{% elif mode == 'server' %}
#
# OpenVPN Server mode
#
mode server
tls-server
{% if server is defined and server is not none %}
{% if server.subnet is defined and server.subnet is not none %}
{% if server.topology is defined and server.topology == 'point-to-point' %}
topology p2p
{% elif server.topology is defined and server.topology is not none %}
topology {{ server.topology }}
{% endif %}
{% for subnet in server.subnet %}
{% if subnet | is_ipv4 %}
server {{ subnet | address_from_cidr }} {{ subnet | netmask_from_cidr }} nopool
{# OpenVPN assigns the first IP address to its local interface so the pool used #}
{# in net30 topology - where each client receives a /30 must start from the second subnet #}
{% if server.topology is defined and server.topology == 'net30' %}
ifconfig-pool {{ subnet | inc_ip('4') }} {{ subnet | last_host_address | dec_ip('1') }} {{ subnet | netmask_from_cidr if device_type == 'tap' else '' }}
{% else %}
{# OpenVPN assigns the first IP address to its local interface so the pool must #}
{# start from the second address and end on the last address #}
ifconfig-pool {{ subnet | first_host_address | inc_ip('1') }} {{ subnet | last_host_address | dec_ip('1') }} {{ subnet | netmask_from_cidr if device_type == 'tun' else '' }}
{% endif %}
{% elif subnet | is_ipv6 %}
server-ipv6 {{ subnet }}
{% endif %}
{% endfor %}
{% endif %}
{% if server.client_ip_pool is defined and server.client_ip_pool is not none and server.client_ip_pool.disable is not defined %}
ifconfig-pool {{ server.client_ip_pool.start }} {{ server.client_ip_pool.stop }}{{ server.client_ip_pool.subnet_mask if server.client_ip_pool.subnet_mask is defined and server.client_ip_pool.subnet_mask is not none }}
{% endif %}
{% if server.max_connections is defined and server.max_connections is not none %}
max-clients {{ server.max_connections }}
{% endif %}
{% if server.client is defined and server.client is not none %}
client-config-dir /run/openvpn/ccd/{{ ifname }}
{% endif %}
{% endif %}
keepalive {{ keep_alive.interval }} {{ keep_alive.interval|int * keep_alive.failure_count|int }}
management /run/openvpn/openvpn-mgmt-intf unix
{% if server is defined and server is not none %}
{% if server.reject_unconfigured_clients is defined %}
ccd-exclusive
{% endif %}
{% if server.push_route is defined and server.push_route is not none %}
{% for route in server.push_route %}
{% if route | is_ipv4 %}
push "route {{ route | address_from_cidr }} {{ route | netmask_from_cidr }}"
{% elif route | is_ipv6 %}
push "route-ipv6 {{ route }}"
{% endif %}
{% endfor %}
{% endif %}
{% if server.name_server is defined and server.name_server is not none %}
{% for nameserver in server.name_server %}
{% if nameserver | is_ipv4 %}
push "dhcp-option DNS {{ nameserver }}"
{% elif nameserver | is_ipv6 %}
push "dhcp-option DNS6 {{ nameserver }}"
{% endif %}
{% endfor %}
{% endif %}
{% if server.domain_name is defined and server.domain_name is not none %}
push "dhcp-option DOMAIN {{ server.domain_name }}"
{% endif %}
{% endif %}
{% else %}
#
# OpenVPN site-2-site mode
#
ping {{ keep_alive.interval }}
ping-restart {{ keep_alive.failure_count }}
{% if device_type == 'tap' %}
{% for laddr, laddr_conf in local_address.items() if laddr | is_ipv4 %}
{% if laddr_conf is defined and laddr_conf.subnet_mask is defined and laddr_conf.subnet_mask is not none %}
ifconfig {{ laddr }} {{ laddr_conf.subnet_mask }}
{% endif %}
{% endfor %}
{% else %}
{% for laddr in local_address if laddr | is_ipv4 %}
{% for raddr in remote_address if raddr | is_ipv4 %}
ifconfig {{ laddr }} {{ raddr }}
{% endfor %}
{% endfor %}
{% for laddr in local_address if laddr | is_ipv6 %}
{% for raddr in remote_address if raddr | is_ipv6 %}
ifconfig-ipv6 {{ laddr }} {{ raddr }}
{% endfor %}
{% endfor %}
{% endif %}
{% endif %}
{% if tls is defined and tls is not none %}
# TLS options
{% if tls.ca_cert_file is defined and tls.ca_cert_file is not none %}
ca {{ tls.ca_cert_file }}
{% endif %}
{% if tls.cert_file is defined and tls.cert_file is not none %}
cert {{ tls.cert_file }}
{% endif %}
{% if tls.key_file is defined and tls.key_file is not none %}
key {{ tls.key_file }}
{% endif %}
{% if tls.crypt_file is defined and tls.crypt_file is not none %}
tls-crypt {{ tls.crypt_file }}
{% endif %}
{% if tls.crl_file is defined and tls.crl_file is not none %}
crl-verify {{ tls.crl_file }}
{% endif %}
{% if tls.tls_version_min is defined and tls.tls_version_min is not none %}
tls-version-min {{ tls.tls_version_min }}
{% endif %}
{% if tls.dh_file is defined and tls.dh_file is not none %}
dh {{ tls.dh_file }}
{% endif %}
{% if tls.auth_file is defined and tls.auth_file is not none %}
{% if mode == 'client' %}
tls-auth {{ tls.auth_file }} 1
{% elif mode == 'server' %}
tls-auth {{ tls.auth_file }} 0
{% endif %}
{% endif %}
{% if tls.role is defined and tls.role is not none %}
{% if tls.role == 'active' %}
tls-client
{% elif tls.role == 'passive' %}
tls-server
{% endif %}
{% endif %}
{% endif %}
# Encryption options
{% if encryption is defined and encryption is not none %}
{% if encryption.cipher is defined and encryption.cipher is not none %}
{% if encryption.cipher == 'none' %}
cipher none
{% elif encryption.cipher == 'des' %}
cipher des-cbc
{% elif encryption.cipher == '3des' %}
cipher des-ede3-cbc
{% elif encryption.cipher == 'bf128' %}
cipher bf-cbc
keysize 128
{% elif encryption.cipher == 'bf256' %}
cipher bf-cbc
keysize 25
{% elif encryption.cipher == 'aes128gcm' %}
cipher aes-128-gcm
{% elif encryption.cipher == 'aes128' %}
cipher aes-128-cbc
{% elif encryption.cipher == 'aes192gcm' %}
cipher aes-192-gcm
{% elif encryption.cipher == 'aes192' %}
cipher aes-192-cbc
{% elif encryption.cipher == 'aes256gcm' %}
cipher aes-256-gcm
{% elif encryption.cipher == 'aes256' %}
cipher aes-256-cbc
{% endif %}
{% endif %}
{% if encryption.ncp_ciphers is defined and encryption.ncp_ciphers is not none %}
{% set cipher_list = [] %}
{% for cipher in encryption.ncp_ciphers %}
{% if cipher == 'none' %}
{% set cipher_list = cipher_list.append('none') %}
{% elif cipher == 'des' %}
{% set cipher_list = cipher_list.append('des-cbc') %}
{% elif cipher == '3des' %}
{% set cipher_list = cipher_list.append('des-ede3-cbc') %}
{% elif cipher == 'aes128' %}
{% set cipher_list = cipher_list.append('aes-128-cbc') %}
{% elif cipher == 'aes128gcm' %}
{% set cipher_list = cipher_list.append('aes-128-gcm') %}
{% elif cipher == 'aes192' %}
{% set cipher_list = cipher_list.append('aes-192-cbc') %}
{% elif cipher == 'aes192gcm' %}
{% set cipher_list = cipher_list.append('aes-192-gcm') %}
{% elif cipher == 'aes256' %}
{% set cipher_list = cipher_list.append('aes-256-cbc') %}
{% elif cipher == 'aes256gcm' %}
{% set cipher_list = cipher_list.append('aes-256-gcm') %}
{% endif %}
{% endfor %}
ncp-ciphers {{ cipher_list | join(':') }}:{{ cipher_list | join(':') | upper }}
-{% elif encryption.disable_ncp is defined %}
-ncp-disable
{% endif %}
{% endif %}
{% if hash is defined and hash is not none %}
auth {{ hash }}
{% endif %}
{% if authentication is defined and authentication is not none %}
auth-user-pass {{ auth_user_pass_file }}
auth-retry nointeract
{% endif %}
{% if openvpn_option is defined and openvpn_option is not none %}
#
# Custom options added by user (not validated)
#
{% for option in openvpn_option %}
{% for argument in option.split('--') %}
{% if argument is defined and argument != '' %}
--{{ argument }}
{% endif %}
{% endfor %}
{% endfor %}
{% endif %}
diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in
index effbdd674..681290570 100644
--- a/interface-definitions/interfaces-openvpn.xml.in
+++ b/interface-definitions/interfaces-openvpn.xml.in
@@ -1,769 +1,763 @@
<?xml version="1.0"?>
<interfaceDefinition>
<node name="interfaces">
<children>
<tagNode name="openvpn" owner="${vyos_conf_scripts_dir}/interfaces-openvpn.py">
<properties>
<help>OpenVPN Tunnel Interface</help>
<priority>460</priority>
<constraint>
<regex>^vtun[0-9]+$</regex>
</constraint>
<constraintErrorMessage>OpenVPN tunnel interface must be named vtunN</constraintErrorMessage>
<valueHelp>
<format>vtunN</format>
<description>OpenVPN interface name</description>
</valueHelp>
</properties>
<children>
<node name="authentication">
<properties>
<help>Authentication options</help>
</properties>
<children>
<leafNode name="password">
<properties>
<help>OpenVPN password used for authentication</help>
</properties>
</leafNode>
<leafNode name="username">
<properties>
<help>OpenVPN username used for authentication</help>
</properties>
</leafNode>
</children>
</node>
#include <include/interface/interface-description.xml.i>
<leafNode name="device-type">
<properties>
<help>OpenVPN interface device-type (default: tun)</help>
<completionHelp>
<list>tun tap</list>
</completionHelp>
<valueHelp>
<format>tun</format>
<description>TUN device, required for OSI layer 3</description>
</valueHelp>
<valueHelp>
<format>tap</format>
<description>TAP device, required for OSI layer 2</description>
</valueHelp>
<constraint>
<regex>^(tun|tap)$</regex>
</constraint>
</properties>
<defaultValue>tun</defaultValue>
</leafNode>
#include <include/interface/interface-disable.xml.i>
<node name="encryption">
<properties>
<help>Data Encryption settings</help>
</properties>
<children>
<leafNode name="cipher">
<properties>
<help>Standard Data Encryption Algorithm</help>
<completionHelp>
<list>none des 3des bf128 bf256 aes128 aes128gcm aes192 aes192gcm aes256 aes256gcm</list>
</completionHelp>
<valueHelp>
<format>none</format>
<description>Disable encryption</description>
</valueHelp>
<valueHelp>
<format>des</format>
<description>DES algorithm</description>
</valueHelp>
<valueHelp>
<format>3des</format>
<description>DES algorithm with triple encryption</description>
</valueHelp>
<valueHelp>
<format>bf128</format>
<description>Blowfish algorithm with 128-bit key</description>
</valueHelp>
<valueHelp>
<format>bf256</format>
<description>Blowfish algorithm with 256-bit key</description>
</valueHelp>
<valueHelp>
<format>aes128</format>
<description>AES algorithm with 128-bit key CBC</description>
</valueHelp>
<valueHelp>
<format>aes128gcm</format>
<description>AES algorithm with 128-bit key GCM</description>
</valueHelp>
<valueHelp>
<format>aes192</format>
<description>AES algorithm with 192-bit key CBC</description>
</valueHelp>
<valueHelp>
<format>aes192gcm</format>
<description>AES algorithm with 192-bit key GCM</description>
</valueHelp>
<valueHelp>
<format>aes256</format>
<description>AES algorithm with 256-bit key CBC</description>
</valueHelp>
<valueHelp>
<format>aes256gcm</format>
<description>AES algorithm with 256-bit key GCM</description>
</valueHelp>
<constraint>
<regex>^(none|des|3des|bf128|bf256|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm)$</regex>
</constraint>
</properties>
</leafNode>
<leafNode name="ncp-ciphers">
<properties>
<help>Cipher negotiation list for use in server or client mode</help>
<completionHelp>
<list>none des 3des aes128 aes128gcm aes192 aes192gcm aes256 aes256gcm</list>
</completionHelp>
<valueHelp>
<format>none</format>
<description>Disable encryption</description>
</valueHelp>
<valueHelp>
<format>des</format>
<description>DES algorithm</description>
</valueHelp>
<valueHelp>
<format>3des</format>
<description>DES algorithm with triple encryption</description>
</valueHelp>
<valueHelp>
<format>aes128</format>
<description>AES algorithm with 128-bit key CBC</description>
</valueHelp>
<valueHelp>
<format>aes128gcm</format>
<description>AES algorithm with 128-bit key GCM</description>
</valueHelp>
<valueHelp>
<format>aes192</format>
<description>AES algorithm with 192-bit key CBC</description>
</valueHelp>
<valueHelp>
<format>aes192gcm</format>
<description>AES algorithm with 192-bit key GCM</description>
</valueHelp>
<valueHelp>
<format>aes256</format>
<description>AES algorithm with 256-bit key CBC</description>
</valueHelp>
<valueHelp>
<format>aes256gcm</format>
<description>AES algorithm with 256-bit key GCM</description>
</valueHelp>
<constraint>
<regex>^(none|des|3des|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm)$</regex>
</constraint>
<multi/>
</properties>
</leafNode>
- <leafNode name="disable-ncp">
- <properties>
- <help>Disable support for ncp-ciphers</help>
- <valueless/>
- </properties>
- </leafNode>
</children>
</node>
#include <include/interface/interface-ipv6-options.xml.i>
<leafNode name="hash">
<properties>
<help>Hashing Algorithm</help>
<completionHelp>
<list>md5 sha1 sha256 sha384 sha512</list>
</completionHelp>
<valueHelp>
<format>md5</format>
<description>MD5 algorithm</description>
</valueHelp>
<valueHelp>
<format>sha1</format>
<description>SHA-1 algorithm</description>
</valueHelp>
<valueHelp>
<format>sha256</format>
<description>SHA-256 algorithm</description>
</valueHelp>
<valueHelp>
<format>sha384</format>
<description>SHA-384 algorithm</description>
</valueHelp>
<valueHelp>
<format>sha512</format>
<description>SHA-512 algorithm</description>
</valueHelp>
<constraint>
<regex>^(md5|sha1|sha256|sha384|sha512)$</regex>
</constraint>
</properties>
</leafNode>
<node name="keep-alive">
<properties>
<help>Keepalive helper options</help>
</properties>
<children>
<leafNode name="failure-count">
<properties>
<help>Maximum number of keepalive packet failures (default: 60)</help>
<valueHelp>
<format>0-1000</format>
<description>Maximum number of keepalive packet failures</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-1000"/>
</constraint>
</properties>
<defaultValue>60</defaultValue>
</leafNode>
<leafNode name="interval">
<properties>
<help>Keepalive packet interval in seconds (default: 10)</help>
<valueHelp>
<format>0-600</format>
<description>Keepalive packet interval (seconds)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-600"/>
</constraint>
</properties>
<defaultValue>10</defaultValue>
</leafNode>
</children>
</node>
<tagNode name="local-address">
<properties>
<help>Local IP address of tunnel (IPv4 or IPv6)</help>
<constraint>
<validator name="ip-address"/>
</constraint>
</properties>
<children>
<leafNode name="subnet-mask">
<properties>
<help>Subnet-mask for local IP address of tunnel (IPv4 only)</help>
<constraint>
<validator name="ipv4-address"/>
</constraint>
</properties>
</leafNode>
</children>
</tagNode>
<leafNode name="local-host">
<properties>
<help>Local IP address to accept connections (all if not set)</help>
<valueHelp>
<format>ipv4</format>
<description>Local IPv4 address</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
<description>Local IPv6 address</description>
</valueHelp>
<constraint>
<validator name="ip-address"/>
</constraint>
</properties>
</leafNode>
<leafNode name="local-port">
<properties>
<help>Local port number to accept connections</help>
<valueHelp>
<format>1-65535</format>
<description>Numeric IP port</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
</constraint>
</properties>
</leafNode>
<leafNode name="mode">
<properties>
<help>OpenVPN mode of operation</help>
<completionHelp>
<list>site-to-site client server</list>
</completionHelp>
<valueHelp>
<format>site-to-site</format>
<description>Site-to-site mode</description>
</valueHelp>
<valueHelp>
<format>client</format>
<description>Client in client-server mode</description>
</valueHelp>
<valueHelp>
<format>server</format>
<description>Server in client-server mode</description>
</valueHelp>
<constraint>
<regex>^(site-to-site|client|server)$</regex>
</constraint>
</properties>
</leafNode>
<leafNode name="openvpn-option">
<properties>
<help>Additional OpenVPN options. You must
use the syntax of openvpn.conf in this text-field. Using this
without proper knowledge may result in a crashed OpenVPN server.
Check system log to look for errors.</help>
<multi/>
</properties>
</leafNode>
<leafNode name="persistent-tunnel">
<properties>
<help>Do not close and reopen interface (TUN/TAP device) on client restarts</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="protocol">
<properties>
<help>OpenVPN communication protocol</help>
<completionHelp>
<list>udp tcp-passive tcp-active</list>
</completionHelp>
<valueHelp>
<format>udp</format>
<description>UDP</description>
</valueHelp>
<valueHelp>
<format>tcp-passive</format>
<description>TCP and accepts connections passively</description>
</valueHelp>
<valueHelp>
<format>tcp-active</format>
<description>TCP and initiates connections actively</description>
</valueHelp>
<constraint>
<regex>^(udp|tcp-passive|tcp-active)$</regex>
</constraint>
</properties>
<defaultValue>udp</defaultValue>
</leafNode>
<leafNode name="remote-address">
<properties>
<help>IP address of remote end of tunnel</help>
<valueHelp>
<format>ipv4</format>
<description>Remote end IPv4 address</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
<description>Remote end IPv6 address</description>
</valueHelp>
<constraint>
<validator name="ipv4-address"/>
<validator name="ipv6-address"/>
</constraint>
<multi/>
</properties>
</leafNode>
<leafNode name="remote-host">
<properties>
<help>Remote host to connect to (dynamic if not set)</help>
<valueHelp>
<format>ipv4</format>
<description>IPv4 address of remote host</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
<description>IPv6 address of remote host</description>
</valueHelp>
<valueHelp>
<format>txt</format>
<description>Hostname of remote host</description>
</valueHelp>
<multi/>
</properties>
</leafNode>
<leafNode name="remote-port">
<properties>
<help>Remote port number to connect to</help>
<valueHelp>
<format>1-65535</format>
<description>Numeric IP port</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
</constraint>
</properties>
</leafNode>
<node name="replace-default-route">
<properties>
<help>OpenVPN tunnel to be used as the default route</help>
</properties>
<children>
<leafNode name="local">
<properties>
<help>Tunnel endpoints are on the same subnet</help>
</properties>
</leafNode>
</children>
</node>
<node name="server">
<properties>
<help>Server-mode options</help>
</properties>
<children>
<tagNode name="client">
<properties>
<help>Client-specific settings</help>
<valueHelp>
<format>name</format>
<description>Client common-name in the certificate</description>
</valueHelp>
</properties>
<children>
#include <include/generic-disable-node.xml.i>
<leafNode name="ip">
<properties>
<help>IP address of the client</help>
<valueHelp>
<format>ipv4</format>
<description>Client IPv4 address</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
<description>Client IPv6 address</description>
</valueHelp>
<constraint>
<validator name="ip-address"/>
</constraint>
<multi/>
</properties>
</leafNode>
<leafNode name="push-route">
<properties>
<help>Route to be pushed to the client</help>
<valueHelp>
<format>ipv4net</format>
<description>IPv4 network and prefix length</description>
</valueHelp>
<valueHelp>
<format>ipv6net</format>
<description>IPv6 network and prefix length</description>
</valueHelp>
<constraint>
<validator name="ip-prefix"/>
</constraint>
<multi/>
</properties>
</leafNode>
<leafNode name="subnet">
<properties>
<help>Subnet belonging to the client (iroute)</help>
<valueHelp>
<format>ipv4net</format>
<description>IPv4 network and prefix length belonging to the client</description>
</valueHelp>
<valueHelp>
<format>ipv6net</format>
<description>IPv6 network and prefix length belonging to the client</description>
</valueHelp>
<constraint>
<validator name="ip-prefix"/>
</constraint>
<multi/>
</properties>
</leafNode>
</children>
</tagNode>
<node name="client-ip-pool">
<properties>
<help>Pool of client IPv4 addresses</help>
</properties>
<children>
#include <include/generic-disable-node.xml.i>
<leafNode name="start">
<properties>
<help>First IP address in the pool</help>
<constraint>
<validator name="ipv4-address"/>
</constraint>
<valueHelp>
<format>ipv4</format>
<description>IPv4 address</description>
</valueHelp>
</properties>
</leafNode>
<leafNode name="stop">
<properties>
<help>Last IP address in the pool</help>
<constraint>
<validator name="ipv4-address"/>
</constraint>
<valueHelp>
<format>ipv4</format>
<description>IPv4 address</description>
</valueHelp>
</properties>
</leafNode>
<leafNode name="subnet-mask">
<properties>
<help>Subnet mask pushed to dynamic clients.
If not set the server subnet mask will be used.
Only used with topology subnet or device type tap.
Not used with bridged interfaces.</help>
<constraint>
<validator name="ipv4-address"/>
</constraint>
<valueHelp>
<format>ipv4</format>
<description>IPv4 subnet mask</description>
</valueHelp>
</properties>
</leafNode>
</children>
</node>
<node name="client-ipv6-pool">
<properties>
<help>Pool of client IPv6 addresses</help>
</properties>
<children>
<leafNode name="base">
<properties>
<help>Client IPv6 pool base address with optional prefix length</help>
<valueHelp>
<format>ipv6net</format>
<description>Client IPv6 pool base address with optional prefix length (defaults: base = server subnet + 0x1000, prefix length = server prefix length)</description>
</valueHelp>
<constraint>
<validator name="ipv6"/>
</constraint>
</properties>
</leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</node>
<leafNode name="domain-name">
<properties>
<help>DNS suffix to be pushed to all clients</help>
<valueHelp>
<format>txt</format>
<description>Domain Name Server suffix</description>
</valueHelp>
</properties>
</leafNode>
<leafNode name="max-connections">
<properties>
<help>Number of maximum client connections</help>
<valueHelp>
<format>1-4096</format>
<description>Number of concurrent clients</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-4096"/>
</constraint>
</properties>
</leafNode>
<leafNode name="name-server">
<properties>
<help>Domain Name Server (DNS)</help>
<valueHelp>
<format>ipv4</format>
<description>DNS server IPv4 address</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
<description>DNS server IPv6 address</description>
</valueHelp>
<constraint>
<validator name="ip-address"/>
</constraint>
<multi/>
</properties>
</leafNode>
<leafNode name="push-route">
<properties>
<help>Route to be pushed to all clients</help>
<valueHelp>
<format>ipv4net</format>
<description>IPv4 network and prefix length</description>
</valueHelp>
<valueHelp>
<format>ipv6net</format>
<description>IPv6 network and prefix length</description>
</valueHelp>
<constraint>
<validator name="ip-prefix"/>
</constraint>
<multi/>
</properties>
</leafNode>
<leafNode name="reject-unconfigured-clients">
<properties>
<help>Reject connections from clients that are not explicitly configured</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="subnet">
<properties>
<help>Server-mode subnet (from which client IPs are allocated)</help>
<valueHelp>
<format>ipv4net</format>
<description>IPv4 network and prefix length</description>
</valueHelp>
<valueHelp>
<format>ipv6net</format>
<description>IPv6 network and prefix length</description>
</valueHelp>
<constraint>
<validator name="ip-prefix"/>
</constraint>
<multi/>
</properties>
</leafNode>
<leafNode name="topology">
<properties>
<help>Topology for clients (default: net30)</help>
<completionHelp>
<list>net30 point-to-point subnet</list>
</completionHelp>
<valueHelp>
<format>net30</format>
<description>net30 topology (default)</description>
</valueHelp>
<valueHelp>
<format>point-to-point</format>
<description>Point-to-point topology</description>
</valueHelp>
<valueHelp>
<format>subnet</format>
<description>Subnet topology</description>
</valueHelp>
<constraint>
<regex>^(subnet|point-to-point|net30)$</regex>
</constraint>
</properties>
<defaultValue>net30</defaultValue>
</leafNode>
</children>
</node>
<leafNode name="shared-secret-key-file">
<properties>
<help>File containing the secret key shared with remote end of tunnel</help>
<valueHelp>
<format>filename</format>
<description>File in /config/auth directory</description>
</valueHelp>
<constraint>
<validator name="file-exists" argument="--directory /config/auth"/>
</constraint>
</properties>
</leafNode>
<node name="tls">
<properties>
<help>Transport Layer Security (TLS) options</help>
</properties>
<children>
<leafNode name="auth-file">
<properties>
<help>File containing tls static key for tls-auth</help>
<valueHelp>
<format>filename</format>
<description>File in /config/auth directory</description>
</valueHelp>
<constraint>
<validator name="file-exists" argument="--directory /config/auth"/>
</constraint>
</properties>
</leafNode>
#include <include/certificate.xml.i>
#include <include/certificate-ca.xml.i>
<leafNode name="crl-file">
<properties>
<help>File containing certificate revocation list (CRL) for this host</help>
<valueHelp>
<format>filename</format>
<description>File in /config/auth directory</description>
</valueHelp>
<constraint>
<validator name="file-exists" argument="--directory /config/auth"/>
</constraint>
</properties>
</leafNode>
<leafNode name="dh-file">
<properties>
<help>File containing Diffie Hellman parameters (server only)</help>
<valueHelp>
<format>filename</format>
<description>File in /config/auth directory</description>
</valueHelp>
<constraint>
<validator name="file-exists" argument="--directory /config/auth"/>
</constraint>
</properties>
</leafNode>
#include <include/certificate-key.xml.i>
<leafNode name="crypt-file">
<properties>
<help>File containing encryption key to authenticate control channel</help>
<valueHelp>
<format>filename</format>
<description>File in /config/auth directory</description>
</valueHelp>
<constraint>
<validator name="file-exists" argument="--directory /config/auth"/>
</constraint>
</properties>
</leafNode>
<leafNode name="tls-version-min">
<properties>
<help>Specify the minimum required TLS version</help>
<completionHelp>
<list>1.0 1.1 1.2</list>
</completionHelp>
<valueHelp>
<format>1.0</format>
<description>TLS v1.0</description>
</valueHelp>
<valueHelp>
<format>1.1</format>
<description>TLS v1.1</description>
</valueHelp>
<valueHelp>
<format>1.2</format>
<description>TLS v1.2</description>
</valueHelp>
<constraint>
<regex>^(1.0|1.1|1.2)$</regex>
</constraint>
</properties>
</leafNode>
<leafNode name="role">
<properties>
<help>TLS negotiation role</help>
<completionHelp>
<list>active passive</list>
</completionHelp>
<valueHelp>
<format>active</format>
<description>Initiate TLS negotiation actively</description>
</valueHelp>
<valueHelp>
<format>passive</format>
<description>Wait for incoming TLS connection</description>
</valueHelp>
<constraint>
<regex>^(active|passive)$</regex>
</constraint>
</properties>
</leafNode>
</children>
</node>
<leafNode name="use-lzo-compression">
<properties>
<help>Use fast LZO compression on this TUN/TAP interface</help>
<valueless/>
</properties>
</leafNode>
#include <include/interface/interface-vrf.xml.i>
</children>
</tagNode>
</children>
</node>
</interfaceDefinition>
diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py
index 655ee770d..68c61b98c 100755
--- a/smoketest/scripts/cli/test_interfaces_openvpn.py
+++ b/smoketest/scripts/cli/test_interfaces_openvpn.py
@@ -1,653 +1,645 @@
#!/usr/bin/env python3
#
# Copyright (C) 2020 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import unittest
from glob import glob
from ipaddress import IPv4Network
from netifaces import interfaces
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from vyos.util import process_named_running
from vyos.util import read_file
from vyos.template import address_from_cidr
from vyos.template import dec_ip
from vyos.template import inc_ip
from vyos.template import last_host_address
from vyos.template import netmask_from_cidr
PROCESS_NAME = 'openvpn'
base_path = ['interfaces', 'openvpn']
ca_cert = '/config/auth/ovpn_test_ca.pem'
ssl_cert = '/config/auth/ovpn_test_server.pem'
ssl_key = '/config/auth/ovpn_test_server.key'
dh_pem = '/config/auth/ovpn_test_dh.pem'
s2s_key = '/config/auth/ovpn_test_site2site.key'
auth_key = '/config/auth/ovpn_test_tls_auth.key'
remote_port = '1194'
protocol = 'udp'
path = []
interface = ''
remote_host = ''
vrf_name = 'orange'
dummy_if = 'dum1301'
def get_vrf(interface):
for upper in glob(f'/sys/class/net/{interface}/upper*'):
# an upper interface could be named: upper_bond0.1000.1100, thus
# we need top drop the upper_ prefix
tmp = os.path.basename(upper)
tmp = tmp.replace('upper_', '')
return tmp
class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase):
def setUp(self):
self.cli_set(['interfaces', 'dummy', dummy_if, 'address', '192.0.2.1/32'])
self.cli_set(['vrf', 'name', vrf_name, 'table', '12345'])
def tearDown(self):
self.cli_delete(base_path)
self.cli_delete(['interfaces', 'dummy', dummy_if])
self.cli_delete(['vrf'])
self.cli_commit()
def test_openvpn_client_verify(self):
# Create OpenVPN client interface and test verify() steps.
interface = 'vtun2000'
path = base_path + [interface]
self.cli_set(path + ['mode', 'client'])
-
- # check validate() - cannot specify both "encryption disable-ncp" and
- # "encryption ncp-ciphers" at the same time
- self.cli_set(path + ['encryption', 'disable-ncp'])
self.cli_set(path + ['encryption', 'ncp-ciphers', 'aes192gcm'])
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- self.cli_delete(path + ['encryption', 'ncp-ciphers'])
-
# check validate() - cannot specify local-port in client mode
self.cli_set(path + ['local-port', '5000'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['local-port'])
# check validate() - cannot specify local-host in client mode
self.cli_set(path + ['local-host', '127.0.0.1'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['local-host'])
# check validate() - cannot specify protocol tcp-passive in client mode
self.cli_set(path + ['protocol', 'tcp-passive'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['protocol'])
# check validate() - remote-host must be set in client mode
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(path + ['remote-host', '192.0.9.9'])
# check validate() - cannot specify "tls dh-file" in client mode
self.cli_set(path + ['tls', 'dh-file', dh_pem])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['tls'])
# check validate() - must specify one of "shared-secret-key-file" and "tls"
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(path + ['shared-secret-key-file', s2s_key])
# check validate() - must specify one of "shared-secret-key-file" and "tls"
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['shared-secret-key-file', s2s_key])
self.cli_set(path + ['tls', 'ca-cert-file', ca_cert])
self.cli_set(path + ['tls', 'cert-file', ssl_cert])
self.cli_set(path + ['tls', 'key-file', ssl_key])
# check validate() - can not have auth username without a password
self.cli_set(path + ['authentication', 'username', 'vyos'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(path + ['authentication', 'password', 'vyos'])
# client commit must pass
self.cli_commit()
self.assertTrue(process_named_running(PROCESS_NAME))
self.assertIn(interface, interfaces())
def test_openvpn_client_interfaces(self):
# Create OpenVPN client interfaces connecting to different
# server IP addresses. Validate configuration afterwards.
num_range = range(10, 15)
for ii in num_range:
interface = f'vtun{ii}'
remote_host = f'192.0.2.{ii}'
path = base_path + [interface]
auth_hash = 'sha1'
self.cli_set(path + ['device-type', 'tun'])
self.cli_set(path + ['encryption', 'cipher', 'aes256'])
self.cli_set(path + ['hash', auth_hash])
self.cli_set(path + ['mode', 'client'])
self.cli_set(path + ['persistent-tunnel'])
self.cli_set(path + ['protocol', protocol])
self.cli_set(path + ['remote-host', remote_host])
self.cli_set(path + ['remote-port', remote_port])
self.cli_set(path + ['tls', 'ca-cert-file', ca_cert])
self.cli_set(path + ['tls', 'cert-file', ssl_cert])
self.cli_set(path + ['tls', 'key-file', ssl_key])
self.cli_set(path + ['vrf', vrf_name])
self.cli_set(path + ['authentication', 'username', interface+'user'])
self.cli_set(path + ['authentication', 'password', interface+'secretpw'])
self.cli_commit()
for ii in num_range:
interface = f'vtun{ii}'
remote_host = f'192.0.2.{ii}'
config_file = f'/run/openvpn/{interface}.conf'
pw_file = f'/run/openvpn/{interface}.pw'
config = read_file(config_file)
self.assertIn(f'dev {interface}', config)
self.assertIn(f'dev-type tun', config)
self.assertIn(f'persist-key', config)
self.assertIn(f'proto {protocol}', config)
self.assertIn(f'rport {remote_port}', config)
self.assertIn(f'remote {remote_host}', config)
self.assertIn(f'persist-tun', config)
self.assertIn(f'auth {auth_hash}', config)
self.assertIn(f'cipher aes-256-cbc', config)
# TLS options
self.assertIn(f'ca {ca_cert}', config)
self.assertIn(f'cert {ssl_cert}', config)
self.assertIn(f'key {ssl_key}', config)
self.assertTrue(process_named_running(PROCESS_NAME))
self.assertEqual(get_vrf(interface), vrf_name)
self.assertIn(interface, interfaces())
pw = cmd(f'sudo cat {pw_file}')
self.assertIn(f'{interface}user', pw)
self.assertIn(f'{interface}secretpw', pw)
# check that no interface remained after deleting them
self.cli_delete(base_path)
self.cli_commit()
for ii in num_range:
interface = f'vtun{ii}'
self.assertNotIn(interface, interfaces())
def test_openvpn_server_verify(self):
# Create one OpenVPN server interface and check required verify() stages
interface = 'vtun5000'
path = base_path + [interface]
# check validate() - must speciy operating mode
self.cli_set(path)
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(path + ['mode', 'server'])
# check validate() - cannot specify protocol tcp-active in server mode
self.cli_set(path + ['protocol', 'tcp-active'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['protocol'])
# check validate() - cannot specify local-port in client mode
self.cli_set(path + ['remote-port', '5000'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['remote-port'])
# check validate() - cannot specify local-host in client mode
self.cli_set(path + ['remote-host', '127.0.0.1'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['remote-host'])
# check validate() - must specify "tls dh-file" when not using EC keys
# in server mode
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(path + ['tls', 'dh-file', dh_pem])
# check validate() - must specify "server subnet" or add interface to
# bridge in server mode
with self.assertRaises(ConfigSessionError):
self.cli_commit()
# check validate() - server client-ip-pool is too large
# [100.64.0.4 -> 100.127.255.251 = 4194295], maximum is 65536 addresses.
self.cli_set(path + ['server', 'subnet', '100.64.0.0/10'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
# check validate() - cannot specify more than 1 IPv4 and 1 IPv6 server subnet
self.cli_set(path + ['server', 'subnet', '100.64.0.0/20'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['server', 'subnet', '100.64.0.0/10'])
# check validate() - must specify "tls ca-cert-file"
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(path + ['tls', 'ca-cert-file', ca_cert])
# check validate() - must specify "tls cert-file"
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(path + ['tls', 'cert-file', ssl_cert])
# check validate() - must specify "tls key-file"
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(path + ['tls', 'key-file', ssl_key])
# check validate() - cannot specify "tls role" in client-server mode'
self.cli_set(path + ['tls', 'role', 'active'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
# check validate() - cannot specify "tls role" in client-server mode'
self.cli_set(path + ['tls', 'auth-file', auth_key])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
# check validate() - cannot specify "tcp-passive" when "tls role" is "active"
self.cli_set(path + ['protocol', 'tcp-passive'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['protocol'])
# check validate() - cannot specify "tls dh-file" when "tls role" is "active"
self.cli_set(path + ['tls', 'dh-file', dh_pem])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['tls', 'dh-file'])
# Now test the other path with tls role passive
self.cli_set(path + ['tls', 'role', 'passive'])
# check validate() - cannot specify "tcp-active" when "tls role" is "passive"
self.cli_set(path + ['protocol', 'tcp-active'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['protocol'])
# check validate() - must specify "tls dh-file" when "tls role" is "passive"
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(path + ['tls', 'dh-file', dh_pem])
self.cli_commit()
self.assertTrue(process_named_running(PROCESS_NAME))
self.assertIn(interface, interfaces())
def test_openvpn_server_subnet_topology(self):
# Create OpenVPN server interfaces using different client subnets.
# Validate configuration afterwards.
auth_hash = 'sha256'
num_range = range(20, 25)
port = ''
client1_routes = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
for ii in num_range:
interface = f'vtun{ii}'
subnet = f'192.0.{ii}.0/24'
client_ip = inc_ip(subnet, '5')
path = base_path + [interface]
port = str(2000 + ii)
self.cli_set(path + ['device-type', 'tun'])
self.cli_set(path + ['encryption', 'cipher', 'aes192'])
self.cli_set(path + ['hash', auth_hash])
self.cli_set(path + ['mode', 'server'])
self.cli_set(path + ['local-port', port])
self.cli_set(path + ['server', 'subnet', subnet])
self.cli_set(path + ['server', 'topology', 'subnet'])
self.cli_set(path + ['keep-alive', 'failure-count', '5'])
self.cli_set(path + ['keep-alive', 'interval', '5'])
# clients
self.cli_set(path + ['server', 'client', 'client1', 'ip', client_ip])
for route in client1_routes:
self.cli_set(path + ['server', 'client', 'client1', 'subnet', route])
self.cli_set(path + ['replace-default-route'])
self.cli_set(path + ['tls', 'ca-cert-file', ca_cert])
self.cli_set(path + ['tls', 'cert-file', ssl_cert])
self.cli_set(path + ['tls', 'key-file', ssl_key])
self.cli_set(path + ['tls', 'dh-file', dh_pem])
self.cli_set(path + ['vrf', vrf_name])
self.cli_commit()
for ii in num_range:
interface = f'vtun{ii}'
subnet = f'192.0.{ii}.0/24'
start_addr = inc_ip(subnet, '2')
stop_addr = last_host_address(subnet)
client_ip = inc_ip(subnet, '5')
client_netmask = netmask_from_cidr(subnet)
port = str(2000 + ii)
config_file = f'/run/openvpn/{interface}.conf'
client_config_file = f'/run/openvpn/ccd/{interface}/client1'
config = read_file(config_file)
self.assertIn(f'dev {interface}', config)
self.assertIn(f'dev-type tun', config)
self.assertIn(f'persist-key', config)
self.assertIn(f'proto udp', config) # default protocol
self.assertIn(f'auth {auth_hash}', config)
self.assertIn(f'cipher aes-192-cbc', config)
self.assertIn(f'topology subnet', config)
self.assertIn(f'lport {port}', config)
self.assertIn(f'push "redirect-gateway def1"', config)
self.assertIn(f'keepalive 5 25', config)
# TLS options
self.assertIn(f'ca {ca_cert}', config)
self.assertIn(f'cert {ssl_cert}', config)
self.assertIn(f'key {ssl_key}', config)
self.assertIn(f'dh {dh_pem}', config)
# IP pool configuration
netmask = IPv4Network(subnet).netmask
network = IPv4Network(subnet).network_address
self.assertIn(f'server {network} {netmask} nopool', config)
# Verify client
client_config = read_file(client_config_file)
self.assertIn(f'ifconfig-push {client_ip} {client_netmask}', client_config)
for route in client1_routes:
self.assertIn('iroute {} {}'.format(address_from_cidr(route), netmask_from_cidr(route)), client_config)
self.assertTrue(process_named_running(PROCESS_NAME))
self.assertEqual(get_vrf(interface), vrf_name)
self.assertIn(interface, interfaces())
# check that no interface remained after deleting them
self.cli_delete(base_path)
self.cli_commit()
for ii in num_range:
interface = f'vtun{ii}'
self.assertNotIn(interface, interfaces())
def test_openvpn_server_net30_topology(self):
# Create OpenVPN server interfaces (net30) using different client
# subnets. Validate configuration afterwards.
auth_hash = 'sha256'
num_range = range(20, 25)
port = ''
for ii in num_range:
interface = f'vtun{ii}'
subnet = f'192.0.{ii}.0/24'
path = base_path + [interface]
port = str(2000 + ii)
self.cli_set(path + ['device-type', 'tun'])
self.cli_set(path + ['encryption', 'cipher', 'aes192'])
self.cli_set(path + ['hash', auth_hash])
self.cli_set(path + ['mode', 'server'])
self.cli_set(path + ['local-port', port])
self.cli_set(path + ['server', 'subnet', subnet])
self.cli_set(path + ['server', 'topology', 'net30'])
self.cli_set(path + ['replace-default-route'])
self.cli_set(path + ['keep-alive', 'failure-count', '10'])
self.cli_set(path + ['keep-alive', 'interval', '5'])
self.cli_set(path + ['tls', 'ca-cert-file', ca_cert])
self.cli_set(path + ['tls', 'cert-file', ssl_cert])
self.cli_set(path + ['tls', 'key-file', ssl_key])
self.cli_set(path + ['tls', 'dh-file', dh_pem])
self.cli_set(path + ['vrf', vrf_name])
self.cli_commit()
for ii in num_range:
interface = f'vtun{ii}'
subnet = f'192.0.{ii}.0/24'
start_addr = inc_ip(subnet, '4')
stop_addr = dec_ip(last_host_address(subnet), '1')
port = str(2000 + ii)
config_file = f'/run/openvpn/{interface}.conf'
config = read_file(config_file)
self.assertIn(f'dev {interface}', config)
self.assertIn(f'dev-type tun', config)
self.assertIn(f'persist-key', config)
self.assertIn(f'proto udp', config) # default protocol
self.assertIn(f'auth {auth_hash}', config)
self.assertIn(f'cipher aes-192-cbc', config)
self.assertIn(f'topology net30', config)
self.assertIn(f'lport {port}', config)
self.assertIn(f'push "redirect-gateway def1"', config)
self.assertIn(f'keepalive 5 50', config)
# TLS options
self.assertIn(f'ca {ca_cert}', config)
self.assertIn(f'cert {ssl_cert}', config)
self.assertIn(f'key {ssl_key}', config)
self.assertIn(f'dh {dh_pem}', config)
# IP pool configuration
netmask = IPv4Network(subnet).netmask
network = IPv4Network(subnet).network_address
self.assertIn(f'server {network} {netmask} nopool', config)
self.assertIn(f'ifconfig-pool {start_addr} {stop_addr}', config)
self.assertTrue(process_named_running(PROCESS_NAME))
self.assertEqual(get_vrf(interface), vrf_name)
self.assertIn(interface, interfaces())
# check that no interface remained after deleting them
self.cli_delete(base_path)
self.cli_commit()
for ii in num_range:
interface = f'vtun{ii}'
self.assertNotIn(interface, interfaces())
def test_openvpn_site2site_verify(self):
# Create one OpenVPN site2site interface and check required
# verify() stages
interface = 'vtun5000'
path = base_path + [interface]
self.cli_set(path + ['mode', 'site-to-site'])
# check validate() - encryption ncp-ciphers cannot be specified in site-to-site mode
self.cli_set(path + ['encryption', 'ncp-ciphers', 'aes192gcm'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['encryption'])
# check validate() - must specify "local-address" or add interface to bridge
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(path + ['local-address', '10.0.0.1'])
self.cli_set(path + ['local-address', '2001:db8:1::1'])
# check validate() - cannot specify more than 1 IPv4 local-address
self.cli_set(path + ['local-address', '10.0.0.2'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['local-address', '10.0.0.2'])
# check validate() - cannot specify more than 1 IPv6 local-address
self.cli_set(path + ['local-address', '2001:db8:1::2'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['local-address', '2001:db8:1::2'])
# check validate() - IPv4 "local-address" requires IPv4 "remote-address"
# or IPv4 "local-address subnet"
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(path + ['remote-address', '192.168.0.1'])
self.cli_set(path + ['remote-address', '2001:db8:ffff::1'])
# check validate() - Cannot specify more than 1 IPv4 "remote-address"
self.cli_set(path + ['remote-address', '192.168.0.2'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['remote-address', '192.168.0.2'])
# check validate() - Cannot specify more than 1 IPv6 "remote-address"
self.cli_set(path + ['remote-address', '2001:db8:ffff::2'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['remote-address', '2001:db8:ffff::2'])
# check validate() - Must specify one of "shared-secret-key-file" and "tls"
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(path + ['shared-secret-key-file', s2s_key])
self.cli_commit()
def test_openvpn_site2site_interfaces_tun(self):
# Create two OpenVPN site-to-site interfaces
num_range = range(30, 35)
port = ''
local_address = ''
remote_address = ''
for ii in num_range:
interface = f'vtun{ii}'
local_address = f'192.0.{ii}.1'
local_address_subnet = '255.255.255.252'
remote_address = f'172.16.{ii}.1'
path = base_path + [interface]
port = str(3000 + ii)
self.cli_set(path + ['local-address', local_address])
# even numbers use tun type, odd numbers use tap type
if ii % 2 == 0:
self.cli_set(path + ['device-type', 'tun'])
else:
self.cli_set(path + ['device-type', 'tap'])
self.cli_set(path + ['local-address', local_address, 'subnet-mask', local_address_subnet])
self.cli_set(path + ['mode', 'site-to-site'])
self.cli_set(path + ['local-port', port])
self.cli_set(path + ['remote-port', port])
self.cli_set(path + ['shared-secret-key-file', s2s_key])
self.cli_set(path + ['remote-address', remote_address])
self.cli_set(path + ['vrf', vrf_name])
self.cli_commit()
for ii in num_range:
interface = f'vtun{ii}'
local_address = f'192.0.{ii}.1'
remote_address = f'172.16.{ii}.1'
port = str(3000 + ii)
config_file = f'/run/openvpn/{interface}.conf'
config = read_file(config_file)
# even numbers use tun type, odd numbers use tap type
if ii % 2 == 0:
self.assertIn(f'dev-type tun', config)
self.assertIn(f'ifconfig {local_address} {remote_address}', config)
else:
self.assertIn(f'dev-type tap', config)
self.assertIn(f'ifconfig {local_address} {local_address_subnet}', config)
self.assertIn(f'dev {interface}', config)
self.assertIn(f'secret {s2s_key}', config)
self.assertIn(f'lport {port}', config)
self.assertIn(f'rport {port}', config)
self.assertTrue(process_named_running(PROCESS_NAME))
self.assertEqual(get_vrf(interface), vrf_name)
self.assertIn(interface, interfaces())
# check that no interface remained after deleting them
self.cli_delete(base_path)
self.cli_commit()
for ii in num_range:
interface = f'vtun{ii}'
self.assertNotIn(interface, interfaces())
if __name__ == '__main__':
# Our SSL certificates need a subject ...
subject = '/C=DE/ST=BY/O=VyOS/localityName=Cloud/commonName=vyos/' \
'organizationalUnitName=VyOS/emailAddress=maintainers@vyos.io/'
if not (os.path.isfile(ssl_key) and os.path.isfile(ssl_cert)):
# Generate mandatory SSL certificate
tmp = f'openssl req -newkey rsa:4096 -new -nodes -x509 -days 3650 '\
f'-keyout {ssl_key} -out {ssl_cert} -subj {subject}'
cmd(tmp)
if not os.path.isfile(ca_cert):
# Generate "CA"
tmp = f'openssl req -new -x509 -key {ssl_key} -out {ca_cert} -subj {subject}'
cmd(tmp)
if not os.path.isfile(dh_pem):
# Generate "DH" key
tmp = f'openssl dhparam -out {dh_pem} 2048'
cmd(tmp)
if not os.path.isfile(s2s_key):
# Generate site-2-site key
tmp = f'openvpn --genkey --secret {s2s_key}'
cmd(tmp)
if not os.path.isfile(auth_key):
# Generate TLS auth key
tmp = f'openvpn --genkey --secret {auth_key}'
cmd(tmp)
for file in [ca_cert, ssl_cert, ssl_key, dh_pem, s2s_key, auth_key]:
cmd(f'sudo chown openvpn:openvpn {file}')
unittest.main(verbosity=2)
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 4afb85526..0256ad62a 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -1,520 +1,514 @@
#!/usr/bin/env python3
#
# Copyright (C) 2019-2020 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import re
from glob import glob
from sys import exit
from ipaddress import IPv4Address
from ipaddress import IPv4Network
from ipaddress import IPv6Address
from ipaddress import IPv6Network
from ipaddress import summarize_address_range
from netifaces import interfaces
from shutil import rmtree
from vyos.config import Config
from vyos.configdict import get_interface_dict
from vyos.configverify import verify_vrf
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_diffie_hellman_length
from vyos.ifconfig import VTunIf
from vyos.template import render
from vyos.template import is_ipv4
from vyos.template import is_ipv6
from vyos.util import call
from vyos.util import chown
from vyos.util import chmod_600
from vyos.util import dict_search
from vyos.validate import is_addr_assigned
from vyos import ConfigError
from vyos import airbag
airbag.enable()
user = 'openvpn'
group = 'openvpn'
cfg_file = '/run/openvpn/{ifname}.conf'
def checkCertHeader(header, filename):
"""
Verify if filename contains specified header.
Returns True if match is found, False if no match or file is not found
"""
if not os.path.isfile(filename):
return False
with open(filename, 'r') as f:
for line in f:
if re.match(header, line):
return True
return False
def get_config(config=None):
"""
Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
interface name will be added or a deleted flag
"""
if config:
conf = config
else:
conf = Config()
base = ['interfaces', 'openvpn']
openvpn = get_interface_dict(conf, base)
openvpn['auth_user_pass_file'] = '/run/openvpn/{ifname}.pw'.format(**openvpn)
openvpn['daemon_user'] = user
openvpn['daemon_group'] = group
return openvpn
def verify(openvpn):
if 'deleted' in openvpn:
verify_bridge_delete(openvpn)
return None
if 'mode' not in openvpn:
raise ConfigError('Must specify OpenVPN operation mode!')
- # Check if we have disabled ncp and at the same time specified ncp-ciphers
- if 'encryption' in openvpn:
- if {'disable_ncp', 'ncp_ciphers'} <= set(openvpn.get('encryption')):
- raise ConfigError('Can not specify both "encryption disable-ncp" '\
- 'and "encryption ncp-ciphers"')
-
#
# OpenVPN client mode - VERIFY
#
if openvpn['mode'] == 'client':
if 'local_port' in openvpn:
raise ConfigError('Cannot specify "local-port" in client mode')
if 'local_host' in openvpn:
raise ConfigError('Cannot specify "local-host" in client mode')
if 'remote_host' not in openvpn:
raise ConfigError('Must specify "remote-host" in client mode')
if openvpn['protocol'] == 'tcp-passive':
raise ConfigError('Protocol "tcp-passive" is not valid in client mode')
if dict_search('tls.dh_file', openvpn):
raise ConfigError('Cannot specify "tls dh-file" in client mode')
#
# OpenVPN site-to-site - VERIFY
#
elif openvpn['mode'] == 'site-to-site':
if 'local_address' not in openvpn and 'is_bridge_member' not in openvpn:
raise ConfigError('Must specify "local-address" or add interface to bridge')
if len([addr for addr in openvpn['local_address'] if is_ipv4(addr)]) > 1:
raise ConfigError('Only one IPv4 local-address can be specified')
if len([addr for addr in openvpn['local_address'] if is_ipv6(addr)]) > 1:
raise ConfigError('Only one IPv6 local-address can be specified')
if openvpn['device_type'] == 'tun':
if 'remote_address' not in openvpn:
raise ConfigError('Must specify "remote-address"')
if 'remote_address' in openvpn:
if len([addr for addr in openvpn['remote_address'] if is_ipv4(addr)]) > 1:
raise ConfigError('Only one IPv4 remote-address can be specified')
if len([addr for addr in openvpn['remote_address'] if is_ipv6(addr)]) > 1:
raise ConfigError('Only one IPv6 remote-address can be specified')
if not 'local_address' in openvpn:
raise ConfigError('"remote-address" requires "local-address"')
v4loAddr = [addr for addr in openvpn['local_address'] if is_ipv4(addr)]
v4remAddr = [addr for addr in openvpn['remote_address'] if is_ipv4(addr)]
if v4loAddr and not v4remAddr:
raise ConfigError('IPv4 "local-address" requires IPv4 "remote-address"')
elif v4remAddr and not v4loAddr:
raise ConfigError('IPv4 "remote-address" requires IPv4 "local-address"')
v6remAddr = [addr for addr in openvpn['remote_address'] if is_ipv6(addr)]
v6loAddr = [addr for addr in openvpn['local_address'] if is_ipv6(addr)]
if v6loAddr and not v6remAddr:
raise ConfigError('IPv6 "local-address" requires IPv6 "remote-address"')
elif v6remAddr and not v6loAddr:
raise ConfigError('IPv6 "remote-address" requires IPv6 "local-address"')
if (v4loAddr == v4remAddr) or (v6remAddr == v4remAddr):
raise ConfigError('"local-address" and "remote-address" cannot be the same')
if dict_search('local_host', openvpn) in dict_search('local_address', openvpn):
raise ConfigError('"local-address" cannot be the same as "local-host"')
if dict_search('remote_host', openvpn) in dict_search('remote_address', openvpn):
raise ConfigError('"remote-address" and "remote-host" can not be the same')
if openvpn['device_type'] == 'tap':
# we can only have one local_address, this is ensured above
v4addr = None
for laddr in openvpn['local_address']:
if is_ipv4(laddr):
v4addr = laddr
break
if v4addr in openvpn['local_address'] and 'subnet_mask' not in openvpn['local_address'][v4addr]:
raise ConfigError('Must specify IPv4 "subnet-mask" for local-address')
if dict_search('encryption.ncp_ciphers', openvpn):
raise ConfigError('NCP ciphers can only be used in client or server mode')
else:
# checks for client-server or site-to-site bridged
if 'local_address' in openvpn or 'remote_address' in openvpn:
raise ConfigError('Cannot specify "local-address" or "remote-address" ' \
'in client/server or bridge mode')
#
# OpenVPN server mode - VERIFY
#
if openvpn['mode'] == 'server':
if openvpn['protocol'] == 'tcp-active':
raise ConfigError('Protocol "tcp-active" is not valid in server mode')
if 'remote_port' in openvpn:
raise ConfigError('Cannot specify "remote-port" in server mode')
if 'remote_host' in openvpn:
raise ConfigError('Cannot specify "remote-host" in server mode')
if 'tls' in openvpn:
if 'dh_file' not in openvpn['tls']:
if 'key_file' in openvpn['tls'] and not checkCertHeader('-----BEGIN EC PRIVATE KEY-----', openvpn['tls']['key_file']):
raise ConfigError('Must specify "tls dh-file" when not using EC keys in server mode')
tmp = dict_search('server.subnet', openvpn)
if tmp:
v4_subnets = len([subnet for subnet in tmp if is_ipv4(subnet)])
v6_subnets = len([subnet for subnet in tmp if is_ipv6(subnet)])
if v4_subnets > 1:
raise ConfigError('Cannot specify more than 1 IPv4 server subnet')
if v6_subnets > 1:
raise ConfigError('Cannot specify more than 1 IPv6 server subnet')
if v6_subnets > 0 and v4_subnets == 0:
raise ConfigError('IPv6 server requires an IPv4 server subnet')
for subnet in tmp:
if is_ipv4(subnet):
subnet = IPv4Network(subnet)
if openvpn['device_type'] == 'tun' and subnet.prefixlen > 29:
raise ConfigError('Server subnets smaller than /29 with device type "tun" are not supported')
elif openvpn['device_type'] == 'tap' and subnet.prefixlen > 30:
raise ConfigError('Server subnets smaller than /30 with device type "tap" are not supported')
for client in (dict_search('client', openvpn) or []):
if client['ip'] and not IPv4Address(client['ip'][0]) in subnet:
raise ConfigError(f'Client "{client["name"]}" IP {client["ip"][0]} not in server subnet {subnet}')
else:
if 'is_bridge_member' not in openvpn:
raise ConfigError('Must specify "server subnet" or add interface to bridge in server mode')
for client in (dict_search('client', openvpn) or []):
if len(client['ip']) > 1 or len(client['ipv6_ip']) > 1:
raise ConfigError(f'Server client "{client["name"]}": cannot specify more than 1 IPv4 and 1 IPv6 IP')
if dict_search('server.client_ip_pool', openvpn):
if not (dict_search('server.client_ip_pool.start', openvpn) and dict_search('server.client_ip_pool.stop', openvpn)):
raise ConfigError('Server client-ip-pool requires both start and stop addresses')
else:
v4PoolStart = IPv4Address(dict_search('server.client_ip_pool.start', openvpn))
v4PoolStop = IPv4Address(dict_search('server.client_ip_pool.stop', openvpn))
if v4PoolStart > v4PoolStop:
raise ConfigError(f'Server client-ip-pool start address {v4PoolStart} is larger than stop address {v4PoolStop}')
v4PoolSize = int(v4PoolStop) - int(v4PoolStart)
if v4PoolSize >= 65536:
raise ConfigError(f'Server client-ip-pool is too large [{v4PoolStart} -> {v4PoolStop} = {v4PoolSize}], maximum is 65536 addresses.')
v4PoolNets = list(summarize_address_range(v4PoolStart, v4PoolStop))
for client in (dict_search('client', openvpn) or []):
if client['ip']:
for v4PoolNet in v4PoolNets:
if IPv4Address(client['ip'][0]) in v4PoolNet:
print(f'Warning: Client "{client["name"]}" IP {client["ip"][0]} is in server IP pool, it is not reserved for this client.')
for subnet in (dict_search('server.subnet', openvpn) or []):
if is_ipv6(subnet):
tmp = dict_search('client_ipv6_pool.base', openvpn)
if tmp:
if not dict_search('server.client_ip_pool', openvpn):
raise ConfigError('IPv6 server pool requires an IPv4 server pool')
if int(tmp.split('/')[1]) >= 112:
raise ConfigError('IPv6 server pool must be larger than /112')
#
# todo - weird logic
#
v6PoolStart = IPv6Address(tmp)
v6PoolStop = IPv6Network((v6PoolStart, openvpn['server_ipv6_pool_prefixlen']), strict=False)[-1] # don't remove the parentheses, it's a 2-tuple
v6PoolSize = int(v6PoolStop) - int(v6PoolStart) if int(openvpn['server_ipv6_pool_prefixlen']) > 96 else 65536
if v6PoolSize < v4PoolSize:
raise ConfigError(f'IPv6 server pool must be at least as large as the IPv4 pool (current sizes: IPv6={v6PoolSize} IPv4={v4PoolSize})')
v6PoolNets = list(summarize_address_range(v6PoolStart, v6PoolStop))
for client in (dict_search('client', openvpn) or []):
if client['ipv6_ip']:
for v6PoolNet in v6PoolNets:
if IPv6Address(client['ipv6_ip'][0]) in v6PoolNet:
print(f'Warning: Client "{client["name"]}" IP {client["ipv6_ip"][0]} is in server IP pool, it is not reserved for this client.')
else:
# checks for both client and site-to-site go here
if dict_search('server.reject_unconfigured_clients', openvpn):
raise ConfigError('Option reject-unconfigured-clients only supported in server mode')
if 'replace_default_route' in openvpn and 'remote_host' not in openvpn:
raise ConfigError('Cannot set "replace-default-route" without "remote-host"')
#
# OpenVPN common verification section
# not depending on any operation mode
#
# verify specified IP address is present on any interface on this system
if 'local_host' in openvpn:
if not is_addr_assigned(openvpn['local_host']):
raise ConfigError('local-host IP address "{local_host}" not assigned' \
' to any interface'.format(**openvpn))
# TCP active
if openvpn['protocol'] == 'tcp-active':
if 'local_port' in openvpn:
raise ConfigError('Cannot specify "local-port" with "tcp-active"')
if 'remote_host' not in openvpn:
raise ConfigError('Must specify "remote-host" with "tcp-active"')
# shared secret and TLS
if not ('shared_secret_key_file' in openvpn or 'tls' in openvpn):
raise ConfigError('Must specify one of "shared-secret-key-file" and "tls"')
if {'shared_secret_key_file', 'tls'} <= set(openvpn):
raise ConfigError('Can only specify one of "shared-secret-key-file" and "tls"')
if openvpn['mode'] in ['client', 'server']:
if 'tls' not in openvpn:
raise ConfigError('Must specify "tls" for server and client mode')
#
# TLS/encryption
#
if 'shared_secret_key_file' in openvpn:
if dict_search('encryption.cipher', openvpn) in ['aes128gcm', 'aes192gcm', 'aes256gcm']:
raise ConfigError('GCM encryption with shared-secret-key-file not supported')
file = dict_search('shared_secret_key_file', openvpn)
if file and not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', file):
raise ConfigError(f'Specified shared-secret-key-file "{file}" is not valid')
if 'tls' in openvpn:
if 'ca_cert_file' not in openvpn['tls']:
raise ConfigError('Must specify "tls ca-cert-file"')
if not (openvpn['mode'] == 'client' and 'auth_file' in openvpn['tls']):
if 'cert_file' not in openvpn['tls']:
raise ConfigError('Missing "tls cert-file"')
if 'key_file' not in openvpn['tls']:
raise ConfigError('Missing "tls key-file"')
if {'auth_file', 'crypt_file'} <= set(openvpn['tls']):
raise ConfigError('TLS auth and crypt are mutually exclusive')
file = dict_search('tls.ca_cert_file', openvpn)
if file and not checkCertHeader('-----BEGIN CERTIFICATE-----', file):
raise ConfigError(f'Specified ca-cert-file "{file}" is invalid')
file = dict_search('tls.auth_file', openvpn)
if file and not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', file):
raise ConfigError(f'Specified auth-file "{file}" is invalid')
file = dict_search('tls.cert_file', openvpn)
if file and not checkCertHeader('-----BEGIN CERTIFICATE-----', file):
raise ConfigError(f'Specified cert-file "{file}" is invalid')
file = dict_search('tls.key_file', openvpn)
if file and not checkCertHeader('-----BEGIN (?:RSA |EC )?PRIVATE KEY-----', file):
raise ConfigError(f'Specified key-file "{file}" is not valid')
file = dict_search('tls.crypt_file', openvpn)
if file and not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', file):
raise ConfigError(f'Specified TLS crypt-file "{file}" is invalid')
file = dict_search('tls.crl_file', openvpn)
if file and not checkCertHeader('-----BEGIN X509 CRL-----', file):
raise ConfigError(f'Specified crl-file "{file} not valid')
file = dict_search('tls.dh_file', openvpn)
if file and not checkCertHeader('-----BEGIN DH PARAMETERS-----', file):
raise ConfigError(f'Specified dh-file "{file}" is not valid')
if file and not verify_diffie_hellman_length(file, 2048):
raise ConfigError(f'Minimum DH key-size is 2048 bits')
tmp = dict_search('tls.role', openvpn)
if tmp:
if openvpn['mode'] in ['client', 'server']:
if not dict_search('tls.auth_file', openvpn):
raise ConfigError('Cannot specify "tls role" in client-server mode')
if tmp == 'active':
if openvpn['protocol'] == 'tcp-passive':
raise ConfigError('Cannot specify "tcp-passive" when "tls role" is "active"')
if dict_search('tls.dh_file', openvpn):
raise ConfigError('Cannot specify "tls dh-file" when "tls role" is "active"')
elif tmp == 'passive':
if openvpn['protocol'] == 'tcp-active':
raise ConfigError('Cannot specify "tcp-active" when "tls role" is "passive"')
if not dict_search('tls.dh_file', openvpn):
raise ConfigError('Must specify "tls dh-file" when "tls role" is "passive"')
file = dict_search('tls.key_file', openvpn)
if file and checkCertHeader('-----BEGIN EC PRIVATE KEY-----', file):
if dict_search('tls.dh_file', openvpn):
print('Warning: using dh-file and EC keys simultaneously will ' \
'lead to DH ciphers being used instead of ECDH')
if dict_search('encryption.cipher', openvpn) == 'none':
print('Warning: "encryption none" was specified!')
print('No encryption will be performed and data is transmitted in ' \
'plain text over the network!')
#
# Auth user/pass
#
if (dict_search('authentication.username', openvpn) and not
dict_search('authentication.password', openvpn)):
raise ConfigError('Password for authentication is missing')
if (dict_search('authentication.password', openvpn) and not
dict_search('authentication.username', openvpn)):
raise ConfigError('Username for authentication is missing')
verify_vrf(openvpn)
return None
def generate(openvpn):
interface = openvpn['ifname']
directory = os.path.dirname(cfg_file.format(**openvpn))
# 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 'deleted' in openvpn or 'disable' in openvpn:
return None
# create client config directory on demand
if not os.path.exists(ccd_dir):
os.makedirs(ccd_dir, 0o755)
chown(ccd_dir, user, group)
# Fix file permissons for keys
fix_permissions = []
tmp = dict_search('shared_secret_key_file', openvpn)
if tmp: fix_permissions.append(openvpn['shared_secret_key_file'])
tmp = dict_search('tls.key_file', openvpn)
if tmp: fix_permissions.append(tmp)
# Generate User/Password authentication file
if 'authentication' in openvpn:
render(openvpn['auth_user_pass_file'], 'openvpn/auth.pw.tmpl', openvpn,
user=user, group=group, permission=0o600)
else:
# delete old auth file if present
if os.path.isfile(openvpn['auth_user_pass_file']):
os.remove(openvpn['auth_user_pass_file'])
# Generate client specific configuration
if dict_search('server.client', openvpn):
for client, client_config in dict_search('server.client', openvpn).items():
client_file = os.path.join(ccd_dir, client)
# Our client need's to know its subnet mask ...
client_config['server_subnet'] = dict_search('server.subnet', openvpn)
render(client_file, 'openvpn/client.conf.tmpl', client_config,
user=user, group=group)
# we need to support quoting of raw parameters from OpenVPN CLI
# see https://phabricator.vyos.net/T1632
render(cfg_file.format(**openvpn), 'openvpn/server.conf.tmpl', openvpn,
formater=lambda _: _.replace("&quot;", '"'), user=user, group=group)
# Fixup file permissions
for file in fix_permissions:
chmod_600(file)
return None
def apply(openvpn):
interface = openvpn['ifname']
call(f'systemctl stop openvpn@{interface}.service')
# Do some cleanup when OpenVPN is disabled/deleted
if 'deleted' in openvpn or 'disable' in openvpn:
for cleanup_file in glob(f'/run/openvpn/{interface}.*'):
if os.path.isfile(cleanup_file):
os.unlink(cleanup_file)
if interface in interfaces():
VTunIf(interface).remove()
return None
# No matching OpenVPN process running - maybe it got killed or none
# existed - nevertheless, spawn new OpenVPN process
call(f'systemctl start openvpn@{interface}.service')
o = VTunIf(**openvpn)
o.update(openvpn)
return None
if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
exit(1)

File Metadata

Mime Type
text/x-diff
Expires
Sat, Sep 26, 2:26 PM (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4285388
Default Alt Text
(91 KB)

Event Timeline