Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F117520787
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
53 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/interface-definitions/interfaces-openvpn.xml b/interface-definitions/interfaces-openvpn.xml
index 39fa8e6a6..f11f27e23 100644
--- a/interface-definitions/interfaces-openvpn.xml
+++ b/interface-definitions/interfaces-openvpn.xml
@@ -1,600 +1,600 @@
<?xml version="1.0"?>
<interfaceDefinition>
<node name="interfaces">
<children>
<tagNode name="openvpn" owner="${vyos_conf_scripts_dir}/interface-openvpn.py">
<properties>
<help>OpenVPN tunnel interface name</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>
<node name="bridge-group">
<properties>
<help>Interface to be added to a bridge group</help>
</properties>
<children>
<leafNode name="bridge">
<properties>
<help>Interface to a bridge-group</help>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces.py --type bridge</script>
</completionHelp>
</properties>
</leafNode>
<leafNode name="cost">
<properties>
<help>Path cost for this port</help>
<valueHelp>
<format>0-2147483647</format>
<description>Path cost for this port</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-2147483647"/>
</constraint>
</properties>
</leafNode>
<leafNode name="cost">
<properties>
<help>Path priority for this port</help>
<valueHelp>
<format>0-255</format>
<description>Path priority for this port</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-255"/>
</constraint>
</properties>
</leafNode>
</children>
</node>
<leafNode name="description">
<properties>
<help>Description</help>
</properties>
</leafNode>
<leafNode name="device-type">
<properties>
<help>OpenVPN interface device-type</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>
</leafNode>
<leafNode name="disable">
<properties>
<help>Disable interface</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="encryption">
<properties>
<help>Data Encryption Algorithm</help>
<completionHelp>
<list>des 3des bf128 bf256 aes128 aes192 aes256</list>
</completionHelp>
<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</description>
</valueHelp>
<valueHelp>
<format>aes192</format>
<description>AES algorithm with 192-bit key</description>
</valueHelp>
<valueHelp>
<format>aes256</format>
<description>AES algorithm with 256-bit key</description>
</valueHelp>
<constraint>
<regex>(des|3des|bf128|bf256|aes128|aes192|aes256)</regex>
</constraint>
</properties>
</leafNode>
<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 6]</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>
</leafNode>
<leafNode name="interval">
<properties>
<help>Keepalive packet interval (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>
</leafNode>
</children>
</node>
<tagNode name="local-address">
<properties>
<help>Local IP address of tunnel</help>
<constraint>
<validator name="ipv4-address"/>
</constraint>
</properties>
<children>
<leafNode name="subnet-mask">
<properties>
<help>Subnet-mask for local IP address of tunnel</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>
<constraint>
<validator name="ipv4-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>Site-to-site mode</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>
</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>
<constraint>
<validator name="ipv4-address"/>
</constraint>
</properties>
</leafNode>
<leafNode name="remote-host">
<properties>
<help>Remote host to connect to (dynamic if not set)</help>
<valueHelp>
<format>ipv4</format>
<description>IP 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>
<leafNode name="disable">
<properties>
<help>Option to disable client connection</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="ip">
<properties>
<help>IP address of the client</help>
<valueHelp>
<format>ipv4</format>
<description>Client IPv4 address</description>
</valueHelp>
<constraint>
<validator name="ipv4-address"/>
</constraint>
</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>
<constraint>
<validator name="ipv4-prefix"/>
</constraint>
<multi/>
</properties>
</leafNode>
<leafNode name="subnet">
<properties>
<help>Subnet belonging to the client</help>
<valueHelp>
<format>ipv4net</format>
<description>IPv4 network and prefix length belonging to the client</description>
</valueHelp>
<constraint>
<validator name="ipv4-prefix"/>
</constraint>
<multi/>
</properties>
</leafNode>
</children>
</tagNode>
<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>
<constraint>
<validator name="ipv4-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>
<constraint>
<validator name="ipv4-prefix"/>
</constraint>
<multi/>
</properties>
</leafNode>
<leafNode name="reject-unconfigured-clients">
<properties>
<help>Reject connections from clients that are not explicitly configured</help>
</properties>
</leafNode>
<leafNode name="subnet">
<properties>
<help>Server-mode subnet (from which client IPs are allocated)</help>
<valueHelp>
<format>ipv4net</format>
<description>IPv4 address and prefix length</description>
</valueHelp>
<constraint>
<validator name="ipv4-prefix"/>
</constraint>
</properties>
</leafNode>
<leafNode name="topology">
<properties>
<help>Topology for clients</help>
<completionHelp>
<list>point-to-point subnet</list>
</completionHelp>
<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)</regex>
</constraint>
</properties>
</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>file</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="ca-cert-file">
<properties>
<help>File containing certificate for Certificate Authority (CA)</help>
</properties>
</leafNode>
<leafNode name="cert-file">
<properties>
<help>File containing certificate for this host</help>
</properties>
</leafNode>
<leafNode name="crl-file">
<properties>
<help>File containing certificate revocation list (CRL) for this host</help>
</properties>
</leafNode>
<leafNode name="dh-file">
<properties>
<help>File containing Diffie Hellman parameters (server only)</help>
</properties>
</leafNode>
<leafNode name="key-file">
<properties>
<help>File containing this host's private key</help>
</properties>
</leafNode>
- <leafNode name="minimum-tls-version">
+ <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>File containing this host's private key</help>
<completionHelp>
<list>active passive</list>
</completionHelp>
<valueHelp>
<format>active</format>
<description>Initiate TLS negotiation actively</description>
</valueHelp>
<valueHelp>
<format>passive</format>
<description>Waiting for TLS connections passively</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>
</children>
</tagNode>
</children>
</node>
</interfaceDefinition>
diff --git a/src/conf_mode/interface-openvpn.py b/src/conf_mode/interface-openvpn.py
index 495ddfdf5..984410eb1 100755
--- a/src/conf_mode/interface-openvpn.py
+++ b/src/conf_mode/interface-openvpn.py
@@ -1,898 +1,898 @@
#!/usr/bin/env python3
#
# Copyright (C) 2019 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
import sys
import stat
import jinja2
from copy import deepcopy
from grp import getgrnam
from ipaddress import ip_address,ip_network,IPv4Interface
from netifaces import interfaces
from psutil import pid_exists
from pwd import getpwnam
from subprocess import Popen, PIPE
from time import sleep
from vyos.config import Config
from vyos import ConfigError
from vyos.validate import is_addr_assigned
user = 'openvpn'
group = 'openvpn'
# Please be careful if you edit the template.
config_tmpl = """
### Autogenerated by interfaces-openvpn.py ###
#
# See https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
# for individual keyword definition
{% if description %}
# {{ description }}
{% endif %}
verb 3
status /opt/vyatta/etc/openvpn/status/{{ intf }}.status 30
writepid /var/run/openvpn/{{ intf }}.pid
daemon openvpn-{{ intf }}
dev-type {{ type }}
dev {{ intf }}
user {{ uid }}
group {{ gid }}
persist-key
iproute /usr/libexec/vyos/system/unpriv-ip
proto {% if 'tcp-active' in protocol -%}tcp-client{% elif 'tcp-passive' in protocol -%}tcp-server{% else %}udp{% endif %}
{%- if local_host %}
local {{ local_host }}
{% endif %}
{%- if local_port %}
lport {{ local_port }}
{% endif %}
{%- if remote_port %}
rport {{ remote_port }}
{% endif %}
{%- if remote_host %}
{% for remote in remote_host -%}
remote {{ remote }}
{% endfor -%}
{% endif %}
{%- if shared_secret_file %}
secret {{ shared_secret_file }}
{% endif %}
{%- if persistent_tunnel %}
persist-tun
{% endif %}
{%- if mode %}
{%- if 'client' in mode %}
#
# OpenVPN Client mode
#
client
nobind
{%- elif 'server' in mode %}
#
# OpenVPN Server mode
#
mode server
tls-server
keepalive {{ ping_interval }} {{ ping_restart }}
management /tmp/openvpn-mgmt-intf unix
{%- if server_topology %}
topology {% if 'point-to-point' in server_topology %}p2p{% else %}subnet{% endif %}
{% endif %}
{% for ns in server_dns_nameserver -%}
push "dhcp-option DNS {{ ns }}"
{% endfor -%}
{% for route in server_push_route -%}
push "route {{ route }}"
{% endfor -%}
{%- if server_domain %}
push "dhcp-option DOMAIN {{ server_domain }}"
{% endif %}
{%- if server_max_conn %}
max-clients {{ server_max_conn }}
{% endif %}
{%- if bridge_member %}
server-bridge nogw
{%- else %}
server {{ server_subnet }}
{% endif %}
{%- if server_reject_unconfigured %}
ccd-exclusive
{% endif %}
{%- else %}
#
# OpenVPN site-2-site mode
#
ping {{ ping_interval }}
ping-restart {{ ping_restart }}
{%- if local_address_subnet %}
ifconfig {{ local_address }} {{ local_address_subnet }}
{% elif remote_address %}
ifconfig {{ local_address }} {{ remote_address }}
{% endif %}
{% endif %}
{% endif %}
{%- if tls_ca_cert %}
ca {{ tls_ca_cert }}
{% endif %}
{%- if tls_cert %}
cert {{ tls_cert }}
{% endif %}
{%- if tls_key %}
key {{ tls_key }}
{% endif %}
{%- if tls_crl %}
crl-verify {{ tls_crl }}
{% endif %}
{%- if tls_version_min %}
tls-version-min {{tls_version_min}}
{% endif %}
{%- if tls_dh %}
dh {{ tls_dh }}
{% endif %}
{%- if 'active' in tls_role %}
tls-client
{%- elif 'passive' in tls_role %}
tls-server
{% endif %}
{%- if redirect_gateway %}
push "redirect-gateway {{ redirect_gateway }}"
{% endif %}
{%- if compress_lzo %}
compress lzo
{% endif %}
{%- if hash %}
auth {{ hash }}
{% endif %}
{%- if encryption %}
{%- if 'des' in encryption %}
cipher des-cbc
{%- elif '3des' in encryption %}
cipher des-ede3-cbc
{%- elif 'bf128' in encryption %}
cipher bf-cbc
keysize 128
{%- elif 'bf256' in encryption %}
cipher bf-cbc
keysize 25
{%- elif 'aes128' in encryption %}
cipher aes-128-cbc
{%- elif 'aes192' in encryption %}
cipher aes-192-cbc
{%- elif 'aes256' in encryption %}
cipher aes-256-cbc
{% endif %}
{% endif %}
{%- if auth %}
auth-user-pass /tmp/openvpn-{{ intf }}-pw
auth-retry nointeract
{% endif %}
{%- if client %}
client-config-dir /opt/vyatta/etc/openvpn/ccd/{{ intf }}
{% endif %}
{% for option in options -%}
{{ option }}
{% endfor -%}
"""
client_tmpl = """
### Autogenerated by interfaces-openvpn.py ###
ifconfig-push {{ ip }} {{ remote_netmask }}
{% for route in push_route -%}
push "route {{ route }}"
{% endfor -%}
{% for net in subnet -%}
iroute {{ net }}
{% endfor -%}
{%- if disable %}
disable
{% endif %}
"""
default_config_data = {
'address': [],
'auth_user': '',
'auth_pass': '',
'auth': False,
'bridge_member': [],
'compress_lzo': False,
'deleted': False,
'description': '',
'disable': False,
'encryption': '',
'hash': '',
'intf': '',
'ping_restart': '60',
'ping_interval': '10',
'local_address': '',
'local_address_subnet': '',
'local_host': '',
'local_port': '',
'mode': '',
'options': [],
'persistent_tunnel': False,
'protocol': '',
'redirect_gateway': '',
'remote_address': '',
'remote_host': [],
'remote_port': '',
'client': [],
'server_domain': '',
'server_max_conn': '',
'server_dns_nameserver': [],
'server_push_route': [],
'server_reject_unconfigured': False,
'server_subnet': '',
'server_topology': '',
'shared_secret_file': '',
'tls': False,
'tls_ca_cert': '',
'tls_cert': '',
'tls_crl': '',
'tls_dh': '',
'tls_key': '',
'tls_role': '',
'tls_version_min': '',
'type': 'tun',
'uid': user,
'gid': group,
}
def subprocess_cmd(command):
p = Popen(command, stdout=PIPE, shell=True)
p.communicate()
def get_config_name(intf):
cfg_file = r'/opt/vyatta/etc/openvpn/openvpn-{}.conf'.format(intf)
return cfg_file
def openvpn_mkdir(directory):
# create directory on demand
if not os.path.exists(directory):
os.mkdir(directory)
# fix permissions - corresponds to mode 755
os.chmod(directory, stat.S_IRWXU|stat.S_IRGRP|stat.S_IXGRP|stat.S_IROTH|stat.S_IXOTH)
uid = getpwnam(user).pw_uid
gid = getgrnam(group).gr_gid
os.chown(directory, uid, gid)
def fixup_permission(filename, permission=stat.S_IRUSR):
"""
Check if the given file exists and change ownershit to root/vyattacfg
and appripriate file access permissions - default is user and group readable
"""
if os.path.isfile(filename):
os.chmod(filename, permission)
# make file owned by root / vyattacfg
uid = getpwnam('root').pw_uid
gid = getgrnam('vyattacfg').gr_gid
os.chown(filename, uid, gid)
def checkCertHeader(header, filename):
"""
Verify if filename contains specified header.
Returns True on success or on file not found to not trigger the exceptions
"""
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 True
def get_config():
openvpn = deepcopy(default_config_data)
conf = Config()
# determine tagNode instance
try:
openvpn['intf'] = os.environ['VYOS_TAGNODE_VALUE']
except KeyError as E:
print("Interface not specified")
# Check if interface instance has been removed
if not conf.exists('interfaces openvpn ' + openvpn['intf']):
openvpn['deleted'] = True
return openvpn
# Check if we belong to any bridge interface
for bridge in conf.list_nodes('interfaces bridge'):
for intf in conf.list_nodes('interfaces bridge {} member interface'.format(bridge)):
if intf == openvpn['intf']:
openvpn['bridge_member'].append(intf)
# set configuration level
conf.set_level('interfaces openvpn ' + openvpn['intf'])
# retrieve authentication options - username
if conf.exists('authentication username'):
openvpn['auth_user'] = conf.return_value('authentication username')
openvpn['auth'] = True
# retrieve authentication options - username
if conf.exists('authentication password'):
openvpn['auth_pass'] = conf.return_value('authentication password')
openvpn['auth'] = True
# retrieve interface description
if conf.exists('description'):
openvpn['description'] = conf.return_value('description')
# interface device-type
if conf.exists('device-type'):
openvpn['type'] = conf.return_value('device-type')
# disable interface
if conf.exists('disable'):
openvpn['disable'] = True
# data encryption algorithm
if conf.exists('encryption'):
openvpn['encryption'] = conf.return_value('encryption')
# hash algorithm
if conf.exists('hash'):
openvpn['hash'] = conf.return_value('hash')
# Maximum number of keepalive packet failures
if conf.exists('keep-alive failure-count') and conf.exists('keep-alive interval'):
fail_count = conf.return_value('keep-alive failure-count')
interval = conf.return_value('keep-alive interval')
openvpn['ping_interval' ] = interval
openvpn['ping_restart' ] = int(interval) * int(fail_count)
# Local IP address of tunnel - even as it is a tag node - we can only work
# on the first address
if conf.exists('local-address'):
openvpn['local_address'] = conf.list_nodes('local-address')[0]
if conf.exists('local-address {} subnet-mask'.format(openvpn['local_address'])):
openvpn['local_address_subnet'] = conf.return_value('local-address {} subnet-mask'.format(openvpn['local_address']))
# Local IP address to accept connections
if conf.exists('local-host'):
openvpn['local_host'] = conf.return_value('local-host')
# Local port number to accept connections
if conf.exists('local-port'):
openvpn['local_port'] = conf.return_value('local-port')
# OpenVPN operation mode
if conf.exists('mode'):
mode = conf.return_value('mode')
openvpn['mode'] = mode
# Additional OpenVPN options
if conf.exists('openvpn-option'):
openvpn['options'] = conf.return_values('openvpn-option')
# Do not close and reopen interface
if conf.exists('persistent-tunnel'):
openvpn['persistent_tunnel'] = True
# Communication protocol
if conf.exists('protocol'):
openvpn['protocol'] = conf.return_value('protocol')
# IP address of remote end of tunnel
if conf.exists('remote-address'):
openvpn['remote_address'] = conf.return_value('remote-address')
# Remote host to connect to (dynamic if not set)
if conf.exists('remote-host'):
openvpn['remote_host'] = conf.return_values('remote-host')
# Remote port number to connect to
if conf.exists('remote-port'):
openvpn['remote_port'] = conf.return_value('remote-port')
# OpenVPN tunnel to be used as the default route
# see https://openvpn.net/community-resources/reference-manual-for-openvpn-2-4/
# redirect-gateway flags
if conf.exists('replace-default-route'):
openvpn['redirect_gateway'] = 'def1'
if conf.exists('replace-default-route local'):
openvpn['redirect_gateway'] = 'local def1'
# Topology for clients
if conf.exists('server topology'):
openvpn['server_topology'] = conf.return_value('server topology')
# Server-mode subnet (from which client IPs are allocated)
if conf.exists('server subnet'):
network = conf.return_value('server subnet')
tmp = IPv4Interface(network).with_netmask
# convert the network in format: "192.0.2.0 255.255.255.0" for later use in template
openvpn['server_subnet'] = tmp.replace(r'/', ' ')
# Client-specific settings
for client in conf.list_nodes('server client'):
# set configuration level
conf.set_level('interfaces openvpn ' + openvpn['intf'] + ' server client ' + client)
data = {
'name': client,
'disable': False,
'ip': '',
'push_route': [],
'subnet': [],
'remote_netmask': ''
}
# note: with "topology subnet", this is "<ip> <netmask>".
# with "topology p2p", this is "<ip> <our_ip>".
if openvpn['server_topology'] == 'subnet':
# we are only interested in the netmask portion of server_subnet
data['remote_netmask'] = openvpn['server_subnet'].split(' ')[1]
else:
# we need the server subnet in format 192.0.2.0/255.255.255.0
subnet = openvpn['server_subnet'].replace(' ', r'/')
# get iterator over the usable hosts in the network
tmp = ip_network(subnet).hosts()
# OpenVPN always uses the subnets first available IP address
data['remote_netmask'] = list(tmp)[0]
# Option to disable client connection
if conf.exists('disable'):
data['disable'] = True
# IP address of the client
if conf.exists('ip'):
data['ip'] = conf.return_value('ip')
# Route to be pushed to the client
for network in conf.return_values('push-route'):
tmp = IPv4Interface(network).with_netmask
data['push_route'].append(tmp.replace(r'/', ' '))
# Subnet belonging to the client
for network in conf.return_values('subnet'):
tmp = IPv4Interface(network).with_netmask
data['subnet'].append(tmp.replace(r'/', ' '))
# Append to global client list
openvpn['client'].append(data)
# re-set configuration level
conf.set_level('interfaces openvpn ' + openvpn['intf'])
# DNS suffix to be pushed to all clients
if conf.exists('server domain-name'):
openvpn['server_domain'] = conf.return_value('server domain-name')
# Number of maximum client connections
if conf.exists('server max-connections'):
openvpn['server_max_conn'] = conf.return_value('server max-connections')
# Domain Name Server (DNS)
if conf.exists('server name-server'):
openvpn['server_dns_nameserver'] = conf.return_values('server name-server')
# Route to be pushed to all clients
if conf.exists('server push-route'):
for network in conf.return_values('server push-route'):
tmp = IPv4Interface(network).with_netmask
openvpn['server_push_route'].append(tmp.replace(r'/', ' '))
# Reject connections from clients that are not explicitly configured
if conf.exists('server reject-unconfigured-clients'):
openvpn['server_reject_unconfigured'] = True
# File containing certificate for Certificate Authority (CA)
if conf.exists('tls ca-cert-file'):
openvpn['tls_ca_cert'] = conf.return_value('tls ca-cert-file')
openvpn['tls'] = True
# File containing certificate for this host
if conf.exists('tls cert-file'):
openvpn['tls_cert'] = conf.return_value('tls cert-file')
openvpn['tls'] = True
# File containing certificate revocation list (CRL) for this host
if conf.exists('tls crl-file'):
openvpn['tls_crl'] = conf.return_value('tls crl-file')
openvpn['tls'] = True
# File containing Diffie Hellman parameters (server only)
if conf.exists('tls dh-file'):
openvpn['tls_dh'] = conf.return_value('tls dh-file')
openvpn['tls'] = True
# File containing this host's private key
if conf.exists('tls key-file'):
openvpn['tls_key'] = conf.return_value('tls key-file')
openvpn['tls'] = True
# Role in TLS negotiation
if conf.exists('tls role'):
openvpn['tls_role'] = conf.return_value('tls role')
openvpn['tls'] = True
# Minimum required TLS version
- if conf.exists('tls minimum-tls-version'):
- openvpn['tls_version_min'] = conf.return_value('tls minimum-tls-version')
+ if conf.exists('tls tls-version-min'):
+ openvpn['tls_version_min'] = conf.return_value('tls tls-version-min')
if conf.exists('shared-secret-key-file'):
openvpn['shared_secret_file'] = conf.return_value('shared-secret-key-file')
if conf.exists('use-lzo-compression'):
openvpn['compress_lzo'] = True
return openvpn
def verify(openvpn):
if openvpn['deleted']:
return None
if not openvpn['mode']:
raise ConfigError('Must specify OpenVPN operation mode')
# Checks which need to be performed on interface rmeoval
if openvpn['deleted']:
# OpenVPN interface can not be deleted if it's still member of a bridge
if openvpn['bridge_member']:
raise ConfigError('Can not delete {} as it is a member interface of bridge {}!'.format(openvpn['intf'], bridge))
#
# OpenVPN client mode - VERIFY
#
if openvpn['mode'] == 'client':
if openvpn['local_port']:
raise ConfigError('Cannot specify "local-port" in client mode')
if openvpn['local_host']:
raise ConfigError('Cannot specify "local-host" in client mode')
if openvpn['protocol'] == 'tcp-passive':
raise ConfigError('Protocol "tcp-passive" is not valid in client mode')
if not openvpn['remote_host']:
raise ConfigError('Must specify "remote-host" in client mode')
if openvpn['tls_dh']:
raise ConfigError('Cannot specify "tls dh-file" in client mode')
#
# OpenVPN site-to-site - VERIFY
#
if openvpn['mode'] == 'site-to-site':
if not (openvpn['local_address'] or openvpn['bridge_member']):
raise ConfigError('Must specify "local-address" or "bridge member interface"')
for host in openvpn['remote_host']:
if host == openvpn['remote_address']:
raise ConfigError('"remote-address" cannot be the same as "remote-host"')
if openvpn['type'] == 'tun':
if not openvpn['remote_address']:
raise ConfigError('Must specify "remote-address"')
if openvpn['local_address'] == openvpn['remote_address']:
raise ConfigError('"local-address" and "remote-address" cannot be the same')
if openvpn['local_address'] == openvpn['local_host']:
raise ConfigError('"local-address" cannot be the same as "local-host"')
else:
if openvpn['local_address'] or openvpn['remote_address']:
raise ConfigError('Cannot specify "local-address" or "remote-address" in client-server mode')
elif openvpn['bridge_member']:
raise ConfigError('Cannot specify "local-address" or "remote-address" in 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 openvpn['remote_port']:
raise ConfigError('Cannot specify "remote-port" in server mode')
if openvpn['remote_host']:
raise ConfigError('Cannot specify "remote-host" in server mode')
if openvpn['protocol'] == 'tcp-passive' and len(openvpn['remote_host']) > 1:
raise ConfigError('Cannot specify more than 1 "remote-host" with "tcp-passive"')
if not openvpn['tls_dh']:
raise ConfigError('Must specify "tls dh-file" in server mode')
if not openvpn['server_subnet']:
if not openvpn['bridge_member']:
raise ConfigError('Must specify "server subnet" option in server mode')
else:
# checks for both client and site-to-site go here
if openvpn['server_reject_unconfigured']:
raise ConfigError('reject-unconfigured-clients is only supported in OpenVPN server mode')
if openvpn['server_topology']:
raise ConfigError('The "topology" option is only valid in server mode')
if (not openvpn['remote_host']) and openvpn['redirect_gateway']:
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 openvpn['local_host']:
if not is_addr_assigned(openvpn['local_host']):
raise ConfigError('No interface on system with specified local-host IP address: {}'.format(openvpn['local_host']))
# TCP active
if openvpn['protocol'] == 'tcp-active':
if openvpn['local_port']:
raise ConfigError('Cannot specify "local-port" with "tcp-active"')
if not openvpn['remote_host']:
raise ConfigError('Must specify "remote-host" with "tcp-active"')
# shared secret and TLS
if not (openvpn['shared_secret_file'] or openvpn['tls']):
raise ConfigError('Must specify one of "shared-secret-key-file" and "tls"')
if openvpn['shared_secret_file'] and openvpn['tls']:
raise ConfigError('Can only specify one of "shared-secret-key-file" and "tls"')
if openvpn['mode'] in ['client', 'server']:
if not openvpn['tls']:
raise ConfigError('Must specify "tls" in client-server mode')
#
# TLS/encryption
#
if openvpn['shared_secret_file']:
if not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', openvpn['shared_secret_file']):
raise ConfigError('Specified shared-secret-key-file "{}" is not valid'.format(openvpn['shared_secret_file']))
if openvpn['tls']:
if not openvpn['tls_ca_cert']:
raise ConfigError('Must specify "tls ca-cert-file"')
if not (openvpn['mode'] == 'client' and openvpn['auth']):
if not openvpn['tls_cert']:
raise ConfigError('Must specify "tls cert-file"')
if not openvpn['tls_key']:
raise ConfigError('Must specify "tls key-file"')
if not checkCertHeader('-----BEGIN CERTIFICATE-----', openvpn['tls_ca_cert']):
raise ConfigError('Specified ca-cert-file "{}" is invalid'.format(openvpn['tls_ca_cert']))
if not checkCertHeader('-----BEGIN CERTIFICATE-----', openvpn['tls_cert']):
raise ConfigError('Specified cert-file "{}" is invalid'.format(openvpn['tls_cert']))
if not checkCertHeader('-----BEGIN (?:RSA )?PRIVATE KEY-----', openvpn['tls_key']):
raise ConfigError('Specified key-file "{}" is not valid'.format(openvpn['tls_key']))
if openvpn['tls_crl']:
if not checkCertHeader('-----BEGIN X509 CRL-----', openvpn['tls_crl']):
raise ConfigError('Specified crl-file "{} not valid'.format(openvpn['tls_crl']))
if openvpn['tls_dh']:
if not checkCertHeader('-----BEGIN DH PARAMETERS-----', openvpn['tls_dh']):
raise ConfigError('Specified dh-file "{}" is not valid'.format(openvpn['tls_dh']))
if openvpn['tls_role']:
if openvpn['mode'] in ['client', 'server']:
raise ConfigError('Cannot specify "tls role" in client-server mode')
if openvpn['tls_role'] == 'active':
if openvpn['protocol'] == 'tcp-passive':
raise ConfigError('Cannot specify "tcp-passive" when "tls role" is "active"')
if openvpn['tls_dh']:
raise ConfigError('Cannot specify "tls dh-file" when "tls role" is "active"')
elif openvpn['tls_role'] == 'passive':
if openvpn['protocol'] == 'tcp-active':
raise ConfigError('Cannot specify "tcp-active" when "tls role" is "passive"')
if not openvpn['tls_dh']:
raise ConfigError('Must specify "tls dh-file" when "tls role" is "passive"')
#
# Auth user/pass
#
if openvpn['auth']:
if not openvpn['auth_user']:
raise ConfigError('Username for authentication is missing')
if not openvpn['auth_pass']:
raise ConfigError('Password for authentication is missing')
#
# Client
#
subnet = openvpn['server_subnet'].replace(' ', '/')
for client in openvpn['client']:
if not ip_address(client['ip']) in ip_network(subnet):
raise ConfigError('Client IP "{}" not in server subnet "{}'.format(client['ip'], subnet))
return None
def generate(openvpn):
if openvpn['deleted'] or openvpn['disable']:
return None
interface = openvpn['intf']
directory = os.path.dirname(get_config_name(interface))
# create config directory on demand
openvpn_mkdir(directory)
# create status directory on demand
openvpn_mkdir(directory + '/status')
# create client config dir on demand
openvpn_mkdir(directory + '/ccd')
# crete client config dir per interface on demand
openvpn_mkdir(directory + '/ccd/' + interface)
# Fix file permissons for keys
fixup_permission(openvpn['shared_secret_file'])
fixup_permission(openvpn['tls_key'])
# Generate User/Password authentication file
if openvpn['auth']:
auth_file = '/tmp/openvpn-{}-pw'.format(interface)
with open(auth_file, 'w') as f:
f.write('{}\n{}'.format(openvpn['auth_user'], openvpn['auth_pass']))
fixup_permission(auth_file)
# get numeric uid/gid
uid = getpwnam(user).pw_uid
gid = getgrnam(group).gr_gid
# Generate client specific configuration
for client in openvpn['client']:
client_file = directory + '/ccd/' + interface + '/' + client['name']
tmpl = jinja2.Template(client_tmpl)
client_text = tmpl.render(client)
with open(client_file, 'w') as f:
f.write(client_text)
os.chown(client_file, uid, gid)
tmpl = jinja2.Template(config_tmpl)
config_text = tmpl.render(openvpn)
# we need to support quoting of raw parameters from OpenVPN CLI
# see https://phabricator.vyos.net/T1632
config_text = config_text.replace(""",'"')
with open(get_config_name(interface), 'w') as f:
f.write(config_text)
os.chown(get_config_name(interface), uid, gid)
return None
def apply(openvpn):
pid = 0
pidfile = '/var/run/openvpn/{}.pid'.format(openvpn['intf'])
if os.path.isfile(pidfile):
pid = 0
with open(pidfile, 'r') as f:
pid = int(f.read())
# Always stop OpenVPN service. We can not send a SIGUSR1 for restart of the
# service as the configuration is not re-read. Stop daemon only if it's
# running - it could have died or killed by someone evil
if pid_exists(pid):
cmd = 'start-stop-daemon --stop --quiet'
cmd += ' --pidfile ' + pidfile
subprocess_cmd(cmd)
# cleanup old PID file
if os.path.isfile(pidfile):
os.remove(pidfile)
# 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(openvpn['intf'])):
os.remove(get_config_name(openvpn['intf']))
# cleanup client config dir
directory = os.path.dirname(get_config_name(openvpn['intf']))
if os.path.isdir(directory + '/ccd/' + openvpn['intf']):
try:
os.remove(directory + '/ccd/' + openvpn['intf'] + '/*')
except:
pass
return None
# On configuration change we need to wait for the 'old' interface to
# vanish from the Kernel, if it is not gone, OpenVPN will report:
# ERROR: Cannot ioctl TUNSETIFF vtun10: Device or resource busy (errno=16)
while openvpn['intf'] in interfaces():
sleep(0.250) # 250ms
# No matching OpenVPN process running - maybe it got killed or none
# existed - nevertheless, spawn new OpenVPN process
cmd = 'start-stop-daemon --start --quiet'
cmd += ' --pidfile ' + pidfile
cmd += ' --exec /usr/sbin/openvpn'
# now pass arguments to openvpn binary
cmd += ' --'
cmd += ' --config ' + get_config_name(openvpn['intf'])
# execute assembled command
subprocess_cmd(cmd)
return None
if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
sys.exit(1)
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Sep 26, 12:17 PM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4285118
Default Alt Text
(53 KB)
Attached To
Mode
rVYOSONEX vyos-1x
Attached
Detach File
Event Timeline
Log In to Comment