summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/https/nginx.default.tmpl1
-rw-r--r--data/templates/openvpn/server.conf.tmpl175
-rw-r--r--interface-definitions/include/port-number.xml.i12
-rw-r--r--interface-definitions/include/vif-s.xml.i1
-rw-r--r--interface-definitions/interfaces-tunnel.xml.in2
-rw-r--r--interface-definitions/interfaces-wireguard.xml.in36
-rw-r--r--python/vyos/airbag.py169
-rw-r--r--python/vyos/ifconfig/tunnel.py2
-rw-r--r--python/vyos/remote.py29
-rw-r--r--python/vyos/util.py21
-rw-r--r--python/vyos/version.py15
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py1
-rwxr-xr-xsrc/conf_mode/interfaces-pppoe.py4
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py155
-rwxr-xr-xsrc/conf_mode/interfaces-wireguard.py411
-rwxr-xr-xsrc/conf_mode/interfaces-wireless.py8
-rwxr-xr-xsrc/conf_mode/interfaces-wirelessmodem.py4
-rwxr-xr-xsrc/conf_mode/vrf.py1
-rwxr-xr-xsrc/helpers/vyos-merge-config.py10
-rwxr-xr-xsrc/op_mode/dns_forwarding_reset.py17
-rwxr-xr-xsrc/op_mode/lldp_op.py6
-rwxr-xr-xsrc/op_mode/wireguard.py11
22 files changed, 723 insertions, 368 deletions
diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl
index 33f7b2820..f4f2c1848 100644
--- a/data/templates/https/nginx.default.tmpl
+++ b/data/templates/https/nginx.default.tmpl
@@ -43,6 +43,7 @@ server {
location ~ /(retrieve|configure|config-file|image|generate|show) {
{% if server.api %}
proxy_pass http://localhost:{{ server.api.port }};
+ proxy_read_timeout 600;
proxy_buffering off;
{% else %}
return 503;
diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl
index e7715dfb5..5f6d1fc3c 100644
--- a/data/templates/openvpn/server.conf.tmpl
+++ b/data/templates/openvpn/server.conf.tmpl
@@ -3,18 +3,20 @@
# See https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
# for individual keyword definition
-{% if description %}
+{% if description -%}
# {{ description }}
-{% endif %}
+
+{% endif -%}
verb 3
status /opt/vyatta/etc/openvpn/status/{{ intf }}.status 30
writepid /var/run/openvpn/{{ intf }}.pid
-dev-type {{ type }}
-dev {{ intf }}
user {{ uid }}
group {{ gid }}
+
+dev-type {{ type }}
+dev {{ intf }}
persist-key
iproute /usr/libexec/vyos/system/unpriv-ip
@@ -22,187 +24,188 @@ proto {% if 'tcp-active' in protocol -%}tcp-client{% elif 'tcp-passive' in proto
{%- if local_host %}
local {{ local_host }}
-{% endif %}
+{%- endif %}
{%- if mode == 'server' and protocol == 'udp' and not local_host %}
multihome
-{% endif %}
+{%- endif %}
{%- if local_port %}
lport {{ local_port }}
-{% endif %}
+{%- endif %}
-{%- if remote_port %}
+{% if remote_port -%}
rport {{ remote_port }}
{% endif %}
{%- if remote_host %}
-{% for remote in remote_host -%}
+{%- for remote in remote_host -%}
remote {{ remote }}
{% endfor -%}
-{% endif %}
+{% endif -%}
-{%- if shared_secret_file %}
+{% if shared_secret_file %}
secret {{ shared_secret_file }}
-{% endif %}
+{%- endif %}
{%- if persistent_tunnel %}
persist-tun
-{% endif %}
+{%- endif %}
+
+{%- if redirect_gateway %}
+push "redirect-gateway {{ redirect_gateway }}"
+{%- endif %}
-{%- if mode %}
-{%- if 'client' in mode %}
+{%- if compress_lzo %}
+compress lzo
+{%- endif %}
+
+{% if 'client' in mode -%}
#
# OpenVPN Client mode
#
client
nobind
-{%- elif 'server' in mode %}
+
+{% 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 %}
+{%- endif %}
{%- if bridge_member %}
server-bridge nogw
{%- else %}
server {{ server_subnet }}
-{% endif %}
+{%- endif %}
+
+{%- if server_max_conn %}
+max-clients {{ server_max_conn }}
+{%- endif %}
{%- if server_reject_unconfigured %}
ccd-exclusive
+{%- endif %}
+
+keepalive {{ ping_interval }} {{ ping_restart }}
+management /tmp/openvpn-mgmt-intf unix
+
+{% for route in server_push_route -%}
+push "route {{ route }}"
+{% endfor -%}
+
+{% for ns in server_dns_nameserver -%}
+push "dhcp-option DNS {{ ns }}"
+{% endfor -%}
+
+{%- if server_domain -%}
+push "dhcp-option DOMAIN {{ server_domain }}"
{% endif %}
-{%- else %}
+{% else -%}
#
# OpenVPN site-2-site mode
#
ping {{ ping_interval }}
ping-restart {{ ping_restart }}
-{%- if local_address_subnet %}
+{% if local_address_subnet -%}
ifconfig {{ local_address }} {{ local_address_subnet }}
-{% elif remote_address %}
+{%- elif remote_address -%}
ifconfig {{ local_address }} {{ remote_address }}
-{% endif %}
+{%- endif %}
-{% endif %}
-{% endif %}
+{% endif -%}
+{% if tls -%}
+# TLS options
{%- if tls_ca_cert %}
ca {{ tls_ca_cert }}
-{% endif %}
+{%- endif %}
{%- if tls_cert %}
cert {{ tls_cert }}
-{% endif %}
+{%- endif %}
{%- if tls_key %}
key {{ tls_key }}
-{% endif %}
+{%- endif %}
{%- if tls_crypt %}
tls-crypt {{ tls_crypt }}
-{% endif %}
+{%- endif %}
{%- if tls_crl %}
crl-verify {{ tls_crl }}
-{% endif %}
+{%- endif %}
{%- if tls_version_min %}
tls-version-min {{tls_version_min}}
-{% endif %}
+{%- endif %}
{%- if tls_dh %}
dh {{ tls_dh }}
-{% endif %}
+{%- endif %}
{%- if tls_auth %}
tls-auth {{tls_auth}}
-{% endif %}
+{%- endif %}
+{%- if tls_role %}
{%- if 'active' in tls_role %}
tls-client
{%- elif 'passive' in tls_role %}
tls-server
-{% endif %}
+{%- endif %}
+{%- endif %}
-{%- if redirect_gateway %}
-push "redirect-gateway {{ redirect_gateway }}"
-{% endif %}
-
-{%- if compress_lzo %}
-compress lzo
-{% endif %}
-
-{%- if hash %}
-auth {{ hash }}
-{% endif %}
+{%- endif %}
+# Encryption options
{%- if encryption %}
-{%- if 'des' in encryption %}
+{% if encryption == 'des' -%}
cipher des-cbc
-{%- elif '3des' in encryption %}
+{%- elif encryption == '3des' -%}
cipher des-ede3-cbc
-{%- elif 'bf128' in encryption %}
+{%- elif encryption == 'bf128' -%}
cipher bf-cbc
keysize 128
-{%- elif 'bf256' in encryption %}
+{%- elif encryption == 'bf256' -%}
cipher bf-cbc
keysize 25
-{%- elif 'aes128gcm' in encryption %}
+{%- elif encryption == 'aes128gcm' -%}
cipher aes-128-gcm
-{%- elif 'aes128' in encryption %}
+{%- elif encryption == 'aes128' -%}
cipher aes-128-cbc
-{%- elif 'aes192gcm' in encryption %}
+{%- elif encryption == 'aes192gcm' -%}
cipher aes-192-gcm
-{%- elif 'aes192' in encryption %}
+{%- elif encryption == 'aes192' -%}
cipher aes-192-cbc
-{%- elif 'aes256gcm' in encryption %}
+{%- elif encryption == 'aes256gcm' -%}
cipher aes-256-gcm
-{%- elif 'aes256' in encryption %}
+{%- elif encryption == 'aes256' -%}
cipher aes-256-cbc
-{% endif %}
-{% endif %}
+{%- endif -%}
+{%- endif %}
{%- if ncp_ciphers %}
ncp-ciphers {{ncp_ciphers}}
-{% endif %}
+{%- endif %}
{%- if disable_ncp %}
ncp-disable
-{% endif %}
+{%- endif %}
+
+{% if hash -%}
+auth {{ hash }}
+{%- 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 %}
+{%- endif %}
# DEPRECATED This option will be removed in OpenVPN 2.5
# Until OpenVPN v2.3 the format of the X.509 Subject fields was formatted like this:
@@ -218,6 +221,12 @@ client-config-dir /opt/vyatta/etc/openvpn/ccd/{{ intf }}
# See https://phabricator.vyos.net/T1512
compat-names
+{% if options -%}
+#
+# Custom options added by user (not validated)
+#
+
{% for option in options -%}
{{ option }}
{% endfor -%}
+{%- endif %}
diff --git a/interface-definitions/include/port-number.xml.i b/interface-definitions/include/port-number.xml.i
new file mode 100644
index 000000000..78eb4b7af
--- /dev/null
+++ b/interface-definitions/include/port-number.xml.i
@@ -0,0 +1,12 @@
+<leafNode name="port">
+ <properties>
+ <help>Port number used to establish connection</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Numeric IP port</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+</leafNode>
diff --git a/interface-definitions/include/vif-s.xml.i b/interface-definitions/include/vif-s.xml.i
index 2120aa32d..acbb34dbd 100644
--- a/interface-definitions/include/vif-s.xml.i
+++ b/interface-definitions/include/vif-s.xml.i
@@ -12,6 +12,7 @@
#include <include/dhcp-dhcpv6-options.xml.i>
#include <include/interface-disable-link-detect.xml.i>
#include <include/interface-disable.xml.i>
+ #include <include/interface-vrf.xml.i>
<leafNode name="ethertype">
<properties>
<help>Set Ethertype</help>
diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in
index 3ba82067f..e1ac60319 100644
--- a/interface-definitions/interfaces-tunnel.xml.in
+++ b/interface-definitions/interfaces-tunnel.xml.in
@@ -107,7 +107,7 @@
</leafNode>
<leafNode name="encapsulation">
<properties>
- <help>Ignore link state changes</help>
+ <help>Encapsulation of this tunnel interface</help>
<completionHelp>
<list>gre gre-bridge ipip sit ipip6 ip6ip6 ip6gre</list>
</completionHelp>
diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in
index d3f084774..9db608afb 100644
--- a/interface-definitions/interfaces-wireguard.xml.in
+++ b/interface-definitions/interfaces-wireguard.xml.in
@@ -19,26 +19,9 @@
#include <include/address-ipv4-ipv6.xml.i>
#include <include/interface-description.xml.i>
#include <include/interface-disable.xml.i>
- <leafNode name="port">
- <properties>
- <help>Local port to listen for incoming 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="mtu">
- <properties>
- <help>interface mtu size(default: 1420)</help>
- <constraint>
- <validator name="numeric" argument="--range 68-9000"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/interface-vrf.xml.i>
+ #include <include/port-number.xml.i>
+ #include <include/interface-mtu-68-9000.xml.i>
<leafNode name="fwmark">
<properties>
<help>A 32-bit fwmark value set on all outgoing packets</help>
@@ -113,18 +96,7 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="port">
- <properties>
- <help>Port number on tunnel remote end</help>
- <valueHelp>
- <format>1-65535</format>
- <description>Numeric IP port</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-65535"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/port-number.xml.i>
<leafNode name="persistent-keepalive">
<properties>
<help>how often send keep alives in seconds</help>
diff --git a/python/vyos/airbag.py b/python/vyos/airbag.py
new file mode 100644
index 000000000..664974d5f
--- /dev/null
+++ b/python/vyos/airbag.py
@@ -0,0 +1,169 @@
+# Copyright 2019-2020 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+import logging
+import logging.handlers
+from datetime import datetime
+
+from vyos.config import Config
+from vyos.version import get_version
+from vyos.util import run
+from vyos.util import debug
+
+
+# we allow to disable the extra logging
+DISABLE = False
+
+
+# emulate a file object
+class _IO(object):
+ def __init__(self, std, log):
+ self.std = std
+ self.log = log
+
+ def write(self, message):
+ self.std.write(message)
+ if DISABLE:
+ return
+ for line in message.split('\n'):
+ s = line.rstrip()
+ if s:
+ self.log(s)
+
+ def flush(self):
+ self.std.flush()
+
+ def close(self):
+ pass
+
+
+# The function which will be used to report information
+# to users when an exception is unhandled
+def bug_report(dtype, value, trace):
+ from traceback import format_exception
+
+ sys.stdout.flush()
+ sys.stderr.flush()
+
+ information = {
+ 'date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
+ 'version': get_version(),
+ 'trace': format_exception(dtype, value, trace),
+ 'instructions': COMMUNITY if 'rolling' in get_version() else SUPPORTED,
+ }
+
+ sys.stdout.write(INTRO.format(**information))
+ sys.stdout.flush()
+
+ sys.stderr.write(FAULT.format(**information))
+ sys.stderr.flush()
+
+
+# define an exception handler to be run when an exception
+# reach the end of __main__ and was not intercepted
+def intercepter(dtype, value, trace):
+ bug_report(dtype, value, trace)
+ # debug returns either '' or 'developer' if debuging is enabled
+ if debug('developer'):
+ import pdb
+ pdb.pm()
+
+
+def InterceptingLogger(address, _singleton=[False]):
+ skip = _singleton.pop()
+ _singleton.append(True)
+ if skip:
+ return
+
+ logger = logging.getLogger('VyOS')
+ logger.setLevel(logging.DEBUG)
+ handler = logging.handlers.SysLogHandler(address='/dev/log', facility='syslog')
+ logger.addHandler(handler)
+
+ # log to syslog any message sent to stderr
+ sys.stderr = _IO(sys.stderr, logger.critical)
+
+
+# lists as default arguments in function is normally dangerous
+# as they will keep any modification performed, unless this is
+# what you want to do (in that case to only run the code once)
+def InterceptingException(excepthook,_singleton=[False]):
+ skip = _singleton.pop()
+ _singleton.append(True)
+ if skip:
+ return
+
+ # install the handler to replace the default behaviour
+ # which just prints the exception trace on screen
+ sys.excepthook = excepthook
+
+
+# Do not attempt the extra logging for operational commands
+try:
+ # This fails during boot
+ insession = Config().in_session()
+except:
+ # we save info on boot to help debugging
+ insession = True
+
+
+# Installing the interception, it currently does not work when
+# running testing so we are checking that we are on the router
+# as otherwise it prevents dpkg-buildpackage to work
+if get_version() and insession:
+ InterceptingLogger('/run/systemd/journal/dev-log')
+ InterceptingException(intercepter)
+
+
+# Messages to print
+
+FAULT = """\
+Date: {date}
+VyOS image: {version}
+
+{trace}
+"""
+
+INTRO = """\
+VyOS had an issue completing a command.
+
+We are sorry that you encountered a problem with VyOS.
+There are a few things you can do to help us (and yourself):
+{instructions}
+
+PLEASE, when reporting, do include as much information as you can:
+- do not obfuscate any data (feel free to send us a private communication with
+ the extra information if your business policy is strict on information sharing)
+- and include all the information presented below
+
+"""
+
+COMMUNITY = """\
+- Make sure you are running the latest version of the code available at
+ https://downloads.vyos.io/rolling/current/amd64/vyos-rolling-latest.iso
+- Consult the forum to see how to handle this issue
+ https://forum.vyos.io
+- Join our community on slack where our users exchange help and advice
+ https://vyos.slack.com
+""".strip()
+
+SUPPORTED = """\
+- Make sure you are running the latest stable version of VyOS
+ the code is available at https://downloads.vyos.io/?dir=release/current
+- Contact us on our online help desk
+ https://support.vyos.io/
+""".strip()
diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py
index 1bbb9eb6a..05060669a 100644
--- a/python/vyos/ifconfig/tunnel.py
+++ b/python/vyos/ifconfig/tunnel.py
@@ -31,7 +31,7 @@ def enable_to_on(value):
raise ValueError(f'expect enable or disable but got "{value}"')
-
+@Interface.register
class _Tunnel(Interface):
"""
_Tunnel: private base class for tunnels
diff --git a/python/vyos/remote.py b/python/vyos/remote.py
index f0bf41cd4..f8a21f068 100644
--- a/python/vyos/remote.py
+++ b/python/vyos/remote.py
@@ -17,8 +17,7 @@ import sys
import os
import re
import fileinput
-
-from vyos.util import cmd, DEVNULL
+import subprocess
def check_and_add_host_key(host_name):
@@ -34,8 +33,10 @@ def check_and_add_host_key(host_name):
keyscan_cmd = 'ssh-keyscan -t rsa {} 2>/dev/null'.format(host_name)
try:
- host_key = cmd(keyscan_cmd, stderr=DEVNULL, universal_newlines=True)
- except OSError:
+ host_key = subprocess.check_output(keyscan_cmd, shell=True,
+ stderr=subprocess.DEVNULL,
+ universal_newlines=True)
+ except subprocess.CalledProcessError as err:
sys.exit("Can not get RSA host key")
# libssh2 (jessie; stretch) does not recognize ec host keys, and curl
@@ -63,8 +64,10 @@ def check_and_add_host_key(host_name):
fingerprint_cmd = 'ssh-keygen -lf /dev/stdin <<< "{}"'.format(host_key)
try:
- fingerprint = cmd(fingerprint_cmd, stderr=DEVNULL, universal_newlines=True)
- except OSError:
+ fingerprint = subprocess.check_output(fingerprint_cmd, shell=True,
+ stderr=subprocess.DEVNULL,
+ universal_newlines=True)
+ except subprocess.CalledProcessError as err:
sys.exit("Can not get RSA host key fingerprint.")
print("RSA host key fingerprint is {}".format(fingerprint.split()[1]))
@@ -125,8 +128,9 @@ def get_remote_config(remote_file):
# Try header first, and look for 'OK' or 'Moved' codes:
curl_cmd = 'curl {0} -q -I {1}'.format(redirect_opt, remote_file)
try:
- curl_output = cmd(curl_cmd, shell=True, universal_newlines=True)
- except OSError:
+ curl_output = subprocess.check_output(curl_cmd, shell=True,
+ universal_newlines=True)
+ except subprocess.CalledProcessError:
sys.exit(1)
return_vals = re.findall(r'^HTTP\/\d+\.?\d\s+(\d+)\s+(.*)$',
@@ -142,6 +146,9 @@ def get_remote_config(remote_file):
curl_cmd = 'curl {0} -# {1}'.format(redirect_opt, remote_file)
try:
- return cmd(curl_cmd, universal_newlines=True)
- except OSError:
- return None
+ config_file = subprocess.check_output(curl_cmd, shell=True,
+ universal_newlines=True)
+ except subprocess.CalledProcessError:
+ config_file = None
+
+ return config_file
diff --git a/python/vyos/util.py b/python/vyos/util.py
index 16cfae92d..c827425ee 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -19,6 +19,12 @@ import sys
from subprocess import Popen, PIPE, STDOUT, DEVNULL
def debug(flag):
+ # this is to force all new flags to be registered here so that
+ # they can be documented:
+ # - developer: the code will drop into PBD on un-handled exception
+ # - ifconfig: prints command and sysfs access on stdout for interface
+ if flag not in ['developer', 'ifconfig']:
+ return False
return flag if os.path.isfile(f'/tmp/vyos.{flag}.debug') else ''
@@ -93,16 +99,25 @@ def read_file(path):
return data
-def chown_file(path, user, group):
- """ change file owner """
+def chown(path, user, group):
+ """ change file/directory owner """
from pwd import getpwnam
from grp import getgrnam
- if os.path.isfile(path):
+ if os.path.exists(path):
uid = getpwnam(user).pw_uid
gid = getgrnam(group).gr_gid
os.chown(path, uid, gid)
+def chmod_750(path):
+ """ make file/directory only executable to user and group """
+ from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP
+
+ if os.path.exists(path):
+ bitmask = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP
+ os.chmod(path, bitmask)
+
+
def chmod_x(path):
""" make file executable """
from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH
diff --git a/python/vyos/version.py b/python/vyos/version.py
index 383efbc1e..d51a940d6 100644
--- a/python/vyos/version.py
+++ b/python/vyos/version.py
@@ -44,7 +44,7 @@ def get_version_data(file=version_file):
file (str): path to the version file
Returns:
- dict: version data
+ dict: version data, if it can not be found and empty dict
The optional ``file`` argument comes in handy in upgrade scripts
that need to retrieve information from images other than the running image.
@@ -52,17 +52,20 @@ def get_version_data(file=version_file):
is an implementation detail and may change in the future, while the interface
of this module will stay the same.
"""
- with open(file, 'r') as f:
- version_data = json.load(f)
- return version_data
+ try:
+ with open(file, 'r') as f:
+ version_data = json.load(f)
+ return version_data
+ except FileNotFoundError:
+ return {}
def get_version(file=None):
"""
- Get the version number
+ Get the version number, or an empty string if it could not be determined
"""
version_data = None
if file:
version_data = get_version_data(file=file)
else:
version_data = get_version_data()
- return version_data["version"]
+ return version_data.get('version','')
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index e9b40bb38..f34e4f7fe 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -428,6 +428,7 @@ def get_config():
# Minimum required TLS version
if conf.exists('tls tls-version-min'):
openvpn['tls_version_min'] = conf.return_value('tls tls-version-min')
+ openvpn['tls'] = True
if conf.exists('shared-secret-key-file'):
openvpn['shared_secret_file'] = conf.return_value('shared-secret-key-file')
diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py
index 407547175..26441838e 100755
--- a/src/conf_mode/interfaces-pppoe.py
+++ b/src/conf_mode/interfaces-pppoe.py
@@ -24,7 +24,7 @@ from netifaces import interfaces
from vyos.config import Config
from vyos.defaults import directories as vyos_data_dir
from vyos.ifconfig import Interface
-from vyos.util import chown_file, chmod_x, cmd
+from vyos.util import chown, chmod_x, cmd
from vyos import ConfigError
default_config_data = {
@@ -240,7 +240,7 @@ def apply(pppoe):
cmd(f'systemctl start ppp@{intf}.service')
# make logfile owned by root / vyattacfg
- chown_file(pppoe['logfile'], 'root', 'vyattacfg')
+ chown(pppoe['logfile'], 'root', 'vyattacfg')
return None
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index 646e61c53..38d490c3c 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -29,18 +29,99 @@ from vyos import ConfigError
class FixedDict(dict):
+ """
+ FixedDict: A dictionnary not allowing new keys to be created after initialisation.
+
+ >>> f = FixedDict(**{'count':1})
+ >>> f['count'] = 2
+ >>> f['king'] = 3
+ File "...", line ..., in __setitem__
+ raise ConfigError(f'Option "{k}" has no defined default')
+ """
def __init__ (self, **options):
self._allowed = options.keys()
super().__init__(**options)
def __setitem__ (self, k, v):
+ """
+ __setitem__ is a builtin which is called by python when setting dict values:
+ >>> d = dict()
+ >>> d['key'] = 'value'
+ >>> d
+ {'key': 'value'}
+
+ is syntaxic sugar for
+
+ >>> d = dict()
+ >>> d.__setitem__('key','value')
+ >>> d
+ {'key': 'value'}
+ """
if k not in self._allowed:
raise ConfigError(f'Option "{k}" has no defined default')
super().__setitem__(k, v)
-class ConfigurationState (Config):
+class ConfigurationState(Config):
+ """
+ The current API require a dict to be generated by get_config()
+ which is then consumed by verify(), generate() and apply()
+
+ ConfiguartionState is an helper class wrapping Config and providing
+ an common API to this dictionary structure
+
+ Its to_dict() function return a dictionary containing three fields,
+ each a dict, called options, changes, actions.
+
+ options:
+
+ contains the configuration options for the dict and its value
+ {'options': {'commment': 'test'}} will be set if
+ 'set interface dummy dum1 description test' was used and
+ the key 'commment' is used to index the description info.
+
+ changes:
+
+ per key, let us know how the data was modified using one of the action
+ a special key called 'section' is used to indicate what happened to the
+ section. for example:
+
+ 'set interface dummy dum1 description test' when no interface was setup
+ will result in the following changes
+ {'changes': {'section': 'create', 'comment': 'create'}}
+
+ on an existing interface, depending if there was a description
+ 'set interface dummy dum1 description test' will result in one of
+ {'changes': {'comment': 'create'}} (not present before)
+ {'changes': {'comment': 'static'}} (unchanged)
+ {'changes': {'comment': 'modify'}} (changed from half)
+
+ and 'delete interface dummy dummy1 description' will result in:
+ {'changes': {'comment': 'delete'}}
+
+ actions:
+
+ for each action list the configuration key which were changes
+ in our example if we added the 'description' and added an IP we would have
+ {'actions': { 'create': ['comment'], 'modify': ['addresses-add']}}
+
+ the actions are:
+ 'create': it did not exist previously and was created
+ 'modify': it did exist previously but its content changed
+ 'static': it did exist and did not change
+ 'delete': it was present but was removed from the configuration
+ 'absent': it was not and is not present
+ which for each field represent how it was modified since the last commit
+ """
+
def __init__ (self, section, default):
+ """
+ initialise the class for a given configuration path:
+
+ >>> conf = ConfigurationState('interfaces ethernet eth1')
+ all further references to get_value(s) and get_effective(s)
+ will be for this part of the configuration (eth1)
+ """
super().__init__()
self.section = section
self.default = deepcopy(default)
@@ -61,6 +142,15 @@ class ConfigurationState (Config):
self.changes['section'] = 'create'
def _act(self, section):
+ """
+ Returns for a given configuration field determine what happened to it
+
+ 'create': it did not exist previously and was created
+ 'modify': it did exist previously but its content changed
+ 'static': it did exist and did not change
+ 'delete': it was present but was removed from the configuration
+ 'absent': it was not and is not present
+ """
if self.exists(section):
if self.exists_effective(section):
if self.return_value(section) != self.return_effective_value(section):
@@ -89,24 +179,71 @@ class ConfigurationState (Config):
self.options[name] = value
def get_value(self, name, key, default=None):
+ """
+ >>> conf.get_value('comment', 'description')
+ will place the string of 'interface dummy description test'
+ into the dictionnary entry 'comment' using Config.return_value
+ (the data in the configuration to apply)
+ """
if self._action(name, key) in ('delete', 'absent'):
return
return self._get(name, key, default, self.return_value)
def get_values(self, name, key, default=None):
+ """
+ >>> conf.get_values('addresses-add', 'address')
+ will place a list made of the IP present in 'interface dummy dum1 address'
+ into the dictionnary entry 'addr' using Config.return_values
+ (the data in the configuration to apply)
+ """
if self._action(name, key) in ('delete', 'absent'):
return
return self._get(name, key, default, self.return_values)
def get_effective(self, name, key, default=None):
+ """
+ >>> conf.get_value('comment', 'description')
+ will place the string of 'interface dummy description test'
+ into the dictionnary entry 'comment' using Config.return_effective_value
+ (the data in the configuration to apply)
+ """
self._action(name, key)
return self._get(name, key, default, self.return_effective_value)
def get_effectives(self, name, key, default=None):
+ """
+ >>> conf.get_effectives('addresses-add', 'address')
+ will place a list made of the IP present in 'interface ethernet eth1 address'
+ into the dictionnary entry 'addresses-add' using Config.return_effectives_value
+ (the data in the un-modified configuration)
+ """
self._action(name, key)
return self._get(name, key, default, self.return_effectives_value)
def load(self, mapping):
+ """
+ load will take a dictionary defining how we wish the configuration
+ to be parsed and apply this definition to set the data.
+
+ >>> mapping = {
+ 'addresses-add' : ('address', True, None),
+ 'comment' : ('description', False, 'auto'),
+ }
+ >>> conf.load(mapping)
+
+ mapping is a dictionary where each key represents the name we wish
+ to have (such as 'addresses-add'), with a list a content representing
+ how the data should be parsed:
+ - the configuration section name
+ such as 'address' under 'interface ethernet eth1'
+ - boolean indicating if this data can have multiple values
+ for 'address', True, as multiple IPs can be set
+ for 'description', False, as it is a single string
+ - default represent the default value if absent from the configuration
+ 'None' indicate that no default should be set if the configuration
+ does not have the configuration section
+
+ """
for local_name, (config_name, multiple, default) in mapping.items():
if multiple:
self.get_values(local_name, config_name, default)
@@ -114,12 +251,21 @@ class ConfigurationState (Config):
self.get_value(local_name, config_name, default)
def remove_default (self,*options):
+ """
+ remove all the values which were not changed from the default
+ """
for option in options:
if self.exists(option) and self_return_value(option) != self.default[option]:
continue
del self.options[option]
def to_dict (self):
+ """
+ provide a dictionary with the generated data for the configuration
+ options: the configuration value for the key
+ changes: per key how they changed from the previous configuration
+ actions: per changes all the options which were changed
+ """
# as we have to use a dict() for the API for verify and apply the options
return {
'options': self.options,
@@ -203,7 +349,8 @@ def get_class (options):
}
kls = dispatch[options['type']]
- if options['type'] == 'gre' and not options['remote']:
+ if options['type'] == 'gre' and not options['remote'] \
+ and not options['key'] and not options['multicast']:
# will use GreTapIf on GreIf deletion but it does not matter
return GRETapIf
elif options['type'] == 'sit' and options['6rd-prefix']:
@@ -420,9 +567,9 @@ def verify(conf):
incompatible = []
if afi_local == IP6:
- incompatible.extend(['ttl', 'tos', 'key',])
+ incompatible.extend(['remote','ttl', 'tos', 'key',])
if afi_local == IP4:
- incompatible.extend(['encaplimit', 'flowlabel', 'hoplimit', 'tclass'])
+ incompatible.extend(['remote','encaplimit', 'flowlabel', 'hoplimit', 'tclass'])
for option in incompatible:
if option in options:
diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py
index 54121a6c1..8e80a85a2 100755
--- a/src/conf_mode/interfaces-wireguard.py
+++ b/src/conf_mode/interfaces-wireguard.py
@@ -14,173 +14,180 @@
# 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 sys
import os
import re
+from sys import exit
from copy import deepcopy
from netifaces import interfaces
-from vyos import ConfigError
from vyos.config import Config
from vyos.configdict import list_diff
-from vyos.util import run, is_bridge_member
from vyos.ifconfig import WireGuardIf
+from vyos.util import chown, run, is_bridge_member, chmod_750
+from vyos import ConfigError
kdir = r'/config/auth/wireguard'
+default_config_data = {
+ 'intfc': '',
+ 'address': [],
+ 'address_remove': [],
+ 'description': '',
+ 'lport': None,
+ 'deleted': False,
+ 'disable': False,
+ 'fwmark': 0x00,
+ 'mtu': 1420,
+ 'peer': [],
+ 'peer_remove': [], # stores public keys of peers to remove
+ 'pk': f'{kdir}/default/private.key',
+ 'vrf': ''
+}
+
def _check_kmod():
- if not os.path.exists('/sys/module/wireguard'):
- if run('modprobe wireguard') != 0:
- raise ConfigError("modprobe wireguard failed")
+ modules = ['wireguard']
+ for module in modules:
+ if not os.path.exists(f'/sys/module/{module}'):
+ if run(f'modprobe {module}') != 0:
+ raise ConfigError(f'Loading Kernel module {module} failed')
def _migrate_default_keys():
if os.path.exists(f'{kdir}/private.key') and not os.path.exists(f'{kdir}/default/private.key'):
- old_umask = os.umask(0o027)
location = f'{kdir}/default'
- run(f'sudo mkdir -p {location}')
- run(f'sudo chgrp vyattacfg {location}')
- run(f'sudo chmod 750 {location}')
+ if not os.path.exists(location):
+ os.makedirs(location)
+
+ chown(location, 'root', 'vyattacfg')
+ chmod_750(location)
os.rename(f'{kdir}/private.key', f'{location}/private.key')
os.rename(f'{kdir}/public.key', f'{location}/public.key')
- os.umask(old_umask)
def get_config():
- c = Config()
- if not c.exists(['interfaces', 'wireguard']):
- return None
+ conf = Config()
+ base = ['interfaces', 'wireguard']
# determine tagNode instance
if 'VYOS_TAGNODE_VALUE' not in os.environ:
raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified')
- dflt_cnf = {
- 'intfc': '',
- 'addr': [],
- 'addr_remove': [],
- 'descr': '',
- 'lport': None,
- 'delete': False,
- 'state': 'up',
- 'fwmark': 0x00,
- 'mtu': 1420,
- 'peer': {},
- 'peer_remove': [],
- 'pk': '{}/default/private.key'.format(kdir)
- }
-
- ifname = str(os.environ['VYOS_TAGNODE_VALUE'])
- wg = deepcopy(dflt_cnf)
- wg['intfc'] = ifname
- wg['descr'] = ifname
-
- c.set_level(['interfaces', 'wireguard'])
-
- # interface removal state
- if not c.exists(ifname) and c.exists_effective(ifname):
- wg['delete'] = True
-
- if not wg['delete']:
- c.set_level(['interfaces', 'wireguard', ifname])
- if c.exists(['address']):
- wg['addr'] = c.return_values(['address'])
-
- # determine addresses which need to be removed
- eff_addr = c.return_effective_values(['address'])
- wg['addr_remove'] = list_diff(eff_addr, wg['addr'])
-
- # ifalias description
- if c.exists(['description']):
- wg['descr'] = c.return_value(['description'])
-
- # link state
- if c.exists(['disable']):
- wg['state'] = 'down'
-
- # local port to listen on
- if c.exists(['port']):
- wg['lport'] = c.return_value(['port'])
-
- # fwmark value
- if c.exists(['fwmark']):
- wg['fwmark'] = c.return_value(['fwmark'])
-
- # mtu
- if c.exists('mtu'):
- wg['mtu'] = c.return_value('mtu')
-
- # private key
- if c.exists(['private-key']):
- wg['pk'] = "{0}/{1}/private.key".format(
- kdir, c.return_value(['private-key']))
-
- # peer removal, wg identifies peers by its pubkey
- peer_eff = c.list_effective_nodes(['peer'])
- peer_rem = list_diff(peer_eff, c.list_nodes(['peer']))
- for p in peer_rem:
- wg['peer_remove'].append(
- c.return_effective_value(['peer', p, 'pubkey']))
-
- # peer settings
- if c.exists(['peer']):
- for p in c.list_nodes(['peer']):
- if not c.exists(['peer', p, 'disable']):
- wg['peer'].update(
- {
- p: {
- 'allowed-ips': [],
- 'address': '',
- 'port': '',
- 'pubkey': ''
- }
- }
- )
- # peer allowed-ips
- if c.exists(['peer', p, 'allowed-ips']):
- wg['peer'][p]['allowed-ips'] = c.return_values(
- ['peer', p, 'allowed-ips'])
- # peer address
- if c.exists(['peer', p, 'address']):
- wg['peer'][p]['address'] = c.return_value(
- ['peer', p, 'address'])
- # peer port
- if c.exists(['peer', p, 'port']):
- wg['peer'][p]['port'] = c.return_value(
- ['peer', p, 'port'])
- # persistent-keepalive
- if c.exists(['peer', p, 'persistent-keepalive']):
- wg['peer'][p]['persistent-keepalive'] = c.return_value(
- ['peer', p, 'persistent-keepalive'])
- # preshared-key
- if c.exists(['peer', p, 'preshared-key']):
- wg['peer'][p]['psk'] = c.return_value(
- ['peer', p, 'preshared-key'])
- # peer pubkeys
- key_eff = c.return_effective_value(['peer', p, 'pubkey'])
- key_cfg = c.return_value(['peer', p, 'pubkey'])
- wg['peer'][p]['pubkey'] = key_cfg
-
- # on a pubkey change we need to remove the pubkey first
- # peers are identified by pubkey, so key update means
- # peer removal and re-add
- if key_eff != key_cfg and key_eff != None:
- wg['peer_remove'].append(key_cfg)
-
- # if a peer is disabled, we have to exec a remove for it's pubkey
- else:
- peer_key = c.return_value(['peer', p, 'pubkey'])
- wg['peer_remove'].append(peer_key)
+ wg = deepcopy(default_config_data)
+ wg['intf'] = os.environ['VYOS_TAGNODE_VALUE']
+
+ # Check if interface has been removed
+ if not conf.exists(base + [wg['intf']]):
+ wg['deleted'] = True
+ return wg
+
+ conf.set_level(base + [wg['intf']])
+
+ # retrieve configured interface addresses
+ if conf.exists(['address']):
+ wg['address'] = conf.return_values(['address'])
+
+ # get interface addresses (currently effective) - to determine which
+ # address is no longer valid and needs to be removed
+ eff_addr = conf.return_effective_values(['address'])
+ wg['address_remove'] = list_diff(eff_addr, wg['address'])
+
+ # retrieve interface description
+ if conf.exists(['description']):
+ wg['description'] = conf.return_value(['description'])
+
+ # disable interface
+ if conf.exists(['disable']):
+ wg['disable'] = True
+
+ # local port to listen on
+ if conf.exists(['port']):
+ wg['lport'] = conf.return_value(['port'])
+
+ # fwmark value
+ if conf.exists(['fwmark']):
+ wg['fwmark'] = int(conf.return_value(['fwmark']))
+
+ # Maximum Transmission Unit (MTU)
+ if conf.exists('mtu'):
+ wg['mtu'] = int(conf.return_value(['mtu']))
+
+ # retrieve VRF instance
+ if conf.exists('vrf'):
+ wg['vrf'] = conf.return_value('vrf')
+
+ # private key
+ if conf.exists(['private-key']):
+ wg['pk'] = "{0}/{1}/private.key".format(
+ kdir, conf.return_value(['private-key']))
+
+ # peer removal, wg identifies peers by its pubkey
+ peer_eff = conf.list_effective_nodes(['peer'])
+ peer_rem = list_diff(peer_eff, conf.list_nodes(['peer']))
+ for peer in peer_rem:
+ wg['peer_remove'].append(
+ conf.return_effective_value(['peer', peer, 'pubkey']))
+
+ # peer settings
+ if conf.exists(['peer']):
+ for p in conf.list_nodes(['peer']):
+ # set new config level for this peer
+ conf.set_level(base + [wg['intf'], 'peer', p])
+ peer = {
+ 'allowed-ips': [],
+ 'address': '',
+ 'name': p,
+ 'persistent_keepalive': '',
+ 'port': '',
+ 'psk': '',
+ 'pubkey': ''
+ }
+
+ # peer allowed-ips
+ if conf.exists(['allowed-ips']):
+ peer['allowed-ips'] = conf.return_values(['allowed-ips'])
+
+ # peer address
+ if conf.exists(['address']):
+ peer['address'] = conf.return_value(['address'])
+
+ # peer port
+ if conf.exists(['port']):
+ peer['port'] = conf.return_value(['port'])
+
+ # persistent-keepalive
+ if conf.exists(['persistent-keepalive']):
+ peer['persistent_keepalive'] = conf.return_value(['persistent-keepalive'])
+
+ # preshared-key
+ if conf.exists(['preshared-key']):
+ peer['psk'] = conf.return_value(['preshared-key'])
+
+ # peer pubkeys
+ if conf.exists(['pubkey']):
+ key_eff = conf.return_effective_value(['pubkey'])
+ key_cfg = conf.return_value(['pubkey'])
+ peer['pubkey'] = key_cfg
+
+ # on a pubkey change we need to remove the pubkey first
+ # peers are identified by pubkey, so key update means
+ # peer removal and re-add
+ if key_eff != key_cfg and key_eff != None:
+ wg['peer_remove'].append(key_cfg)
+
+ # if a peer is disabled, we have to exec a remove for it's pubkey
+ if conf.exists(['disable']):
+ wg['peer_remove'].append(peer['pubkey'])
+ else:
+ wg['peer'].append(peer)
+
return wg
-def verify(c):
- if not c:
- return None
+def verify(wg):
+ interface = wg['intf']
- if c['delete']:
- interface = c['intfc']
+ if wg['deleted']:
is_member, bridge = is_bridge_member(interface)
if is_member:
# can not use a f'' formatted-string here as bridge would not get
@@ -189,98 +196,100 @@ def verify(c):
'is a member of bridge "{1}"!'.format(interface, bridge))
return None
- if not os.path.exists(c['pk']):
- raise ConfigError(
- "No keys found, generate them by executing: \'run generate wireguard [keypair|named-keypairs]\'")
-
- if not c['delete']:
- if not c['addr']:
- raise ConfigError("ERROR: IP address required")
- if not c['peer']:
- raise ConfigError("ERROR: peer required")
- for p in c['peer']:
- if not c['peer'][p]['allowed-ips']:
- raise ConfigError("ERROR: allowed-ips required for peer " + p)
- if not c['peer'][p]['pubkey']:
- raise ConfigError("peer pubkey required for peer " + p)
-
-
-def apply(c):
- # no wg configs left, remove all interface from system
- # maybe move it into ifconfig.py
- if not c:
- net_devs = os.listdir('/sys/class/net/')
- for dev in net_devs:
- if os.path.isdir('/sys/class/net/' + dev):
- buf = open('/sys/class/net/' + dev + '/uevent', 'r').read()
- if re.search("DEVTYPE=wireguard", buf, re.I | re.M):
- wg_intf = re.sub("INTERFACE=", "", re.search(
- "INTERFACE=.*", buf, re.I | re.M).group(0))
- # XXX: we are ignoring any errors here
- run(f'ip l d dev {wg_intf} >/dev/null')
- return None
+ vrf_name = wg['vrf']
+ if vrf_name and vrf_name not in interfaces():
+ raise ConfigError(f'VRF "{vrf_name}" does not exist')
+
+ if not os.path.exists(wg['pk']):
+ raise ConfigError('No keys found, generate them by executing:\n' \
+ '"run generate wireguard [keypair|named-keypairs]"')
+ if not wg['address']:
+ raise ConfigError(f'IP address required for interface "{interface}"!')
+
+ if not wg['peer']:
+ raise ConfigError(f'Peer required for interface "{interface}"!')
+
+ # run checks on individual configured WireGuard peer
+ for peer in wg['peer']:
+ peer_name = peer['name']
+ if not peer['allowed-ips']:
+ raise ConfigError(f'Peer allowed-ips required for peer "{peer_name}"!')
+
+ if not peer['pubkey']:
+ raise ConfigError(f'Peer public-key required for peer "{peer_name}"!')
+
+
+def apply(wg):
# init wg class
- intfc = WireGuardIf(c['intfc'])
+ w = WireGuardIf(wg['intf'])
# single interface removal
- if c['delete']:
- intfc.remove()
+ if wg['deleted']:
+ w.remove()
return None
- # remove IP addresses
- for ip in c['addr_remove']:
- intfc.del_addr(ip)
+ # Configure interface address(es)
+ # - not longer required addresses get removed first
+ # - newly addresses will be added second
+ for addr in wg['address_remove']:
+ w.del_addr(addr)
+ for addr in wg['address']:
+ w.add_addr(addr)
- # add IP addresses
- for ip in c['addr']:
- intfc.add_addr(ip)
+ # Maximum Transmission Unit (MTU)
+ w.set_mtu(wg['mtu'])
- # interface mtu
- intfc.set_mtu(int(c['mtu']))
+ # update interface description used e.g. within SNMP
+ w.set_alias(wg['description'])
- # ifalias for snmp from description
- intfc.set_alias(str(c['descr']))
+ # assign/remove VRF
+ w.set_vrf(wg['vrf'])
# remove peers
- if c['peer_remove']:
- for pkey in c['peer_remove']:
- intfc.remove_peer(pkey)
+ for pub_key in wg['peer_remove']:
+ w.remove_peer(pub_key)
# peer pubkey
# setting up the wg interface
- intfc.config['private-key'] = c['pk']
- for p in c['peer']:
+ w.config['private-key'] = c['pk']
+
+ for peer in wg['peer']:
# peer pubkey
- intfc.config['pubkey'] = str(c['peer'][p]['pubkey'])
+ w.config['pubkey'] = peer['pubkey']
# peer allowed-ips
- intfc.config['allowed-ips'] = c['peer'][p]['allowed-ips']
+ w.config['allowed-ips'] = peer['allowed-ips']
# local listen port
- if c['lport']:
- intfc.config['port'] = c['lport']
+ if wg['lport']:
+ w.config['port'] = wg['lport']
# fwmark
if c['fwmark']:
- intfc.config['fwmark'] = c['fwmark']
+ w.config['fwmark'] = wg['fwmark']
+
# endpoint
- if c['peer'][p]['address'] and c['peer'][p]['port']:
- intfc.config['endpoint'] = "{}:{}".format(c['peer'][p]['address'], c['peer'][p]['port'])
+ if peer['address'] and peer['port']:
+ w.config['endpoint'] = '{}:{}'.format(
+ peer['address'], peer['port'])
# persistent-keepalive
- if 'persistent-keepalive' in c['peer'][p]:
- intfc.config['keepalive'] = c['peer'][p]['persistent-keepalive']
+ if peer['persistent_keepalive']:
+ w.config['keepalive'] = peer['persistent_keepalive']
# maybe move it into ifconfig.py
# preshared-key - needs to be read from a file
- if 'psk' in c['peer'][p]:
+ if peer['psk']:
psk_file = '/config/auth/wireguard/psk'
- old_umask = os.umask(0o077)
- open(psk_file, 'w').write(str(c['peer'][p]['psk']))
- os.umask(old_umask)
- intfc.config['psk'] = psk_file
- intfc.update()
+ with open(psk_file, 'w') as f:
+ f.write(peer['psk'])
+ w.config['psk'] = psk_file
+
+ w.update()
- # interface state
- intfc.set_admin_state(c['state'])
+ # Enable/Disable interface
+ if wg['disable']:
+ w.set_admin_state('down')
+ else:
+ w.set_admin_state('up')
return None
@@ -293,4 +302,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index 709085b0f..138f27755 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -29,7 +29,7 @@ from vyos.configdict import list_diff, vlan_to_dict
from vyos.defaults import directories as vyos_data_dir
from vyos.ifconfig import WiFiIf
from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config
-from vyos.util import process_running, chmod_x, chown_file, run, is_bridge_member
+from vyos.util import process_running, chmod_x, chown, run, is_bridge_member
from vyos import ConfigError
user = 'root'
@@ -121,7 +121,7 @@ def get_conf_file(conf_type, intf):
if not os.path.exists(cfg_dir):
os.mkdir(cfg_dir)
chmod_x(cfg_dir)
- chown_file(cfg_dir, user, group)
+ chown(cfg_dir, user, group)
cfg_file = cfg_dir + r'/{}.cfg'.format(intf)
return cfg_file
@@ -133,7 +133,7 @@ def get_pid(conf_type, intf):
if not os.path.exists(cfg_dir):
os.mkdir(cfg_dir)
chmod_x(cfg_dir)
- chown_file(cfg_dir, user, group)
+ chown(cfg_dir, user, group)
cfg_file = cfg_dir + r'/{}.pid'.format(intf)
return cfg_file
@@ -146,7 +146,7 @@ def get_wpa_suppl_config_name(intf):
if not os.path.exists(cfg_dir):
os.mkdir(cfg_dir)
chmod_x(cfg_dir)
- chown_file(cfg_dir, user, group)
+ chown(cfg_dir, user, group)
cfg_file = cfg_dir + r'/{}.cfg'.format(intf)
return cfg_file
diff --git a/src/conf_mode/interfaces-wirelessmodem.py b/src/conf_mode/interfaces-wirelessmodem.py
index 49445aaa4..e5af37b8f 100755
--- a/src/conf_mode/interfaces-wirelessmodem.py
+++ b/src/conf_mode/interfaces-wirelessmodem.py
@@ -23,7 +23,7 @@ from netifaces import interfaces
from vyos.config import Config
from vyos.defaults import directories as vyos_data_dir
-from vyos.util import chown_file, chmod_x, cmd, run, is_bridge_member
+from vyos.util import chown, chmod_x, cmd, run, is_bridge_member
from vyos import ConfigError
default_config_data = {
@@ -219,7 +219,7 @@ def apply(wwan):
intf = wwan['intf']
cmd(f'systemctl start ppp@{intf}.service')
# make logfile owned by root / vyattacfg
- chown_file(wwan['logfile'], 'root', 'vyattacfg')
+ chown(wwan['logfile'], 'root', 'vyattacfg')
return None
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index 07466f3aa..586424c09 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -195,6 +195,7 @@ def apply(vrf_config):
#
# - https://github.com/torvalds/linux/blob/master/Documentation/networking/vrf.txt
# - https://github.com/Mellanox/mlxsw/wiki/Virtual-Routing-and-Forwarding-(VRF)
+ # - https://github.com/Mellanox/mlxsw/wiki/L3-Tunneling
# - https://netdevconf.info/1.1/proceedings/slides/ahern-vrf-tutorial.pdf
# - https://netdevconf.info/1.2/slides/oct6/02_ahern_what_is_l3mdev_slides.pdf
diff --git a/src/helpers/vyos-merge-config.py b/src/helpers/vyos-merge-config.py
index 6546c03e3..10a5ea4bc 100755
--- a/src/helpers/vyos-merge-config.py
+++ b/src/helpers/vyos-merge-config.py
@@ -17,13 +17,13 @@
import sys
import os
+import subprocess
import tempfile
import vyos.defaults
import vyos.remote
from vyos.config import Config
from vyos.configtree import ConfigTree
from vyos.migrator import Migrator, VirtualMigrator
-from vyos.util import cmd
if (len(sys.argv) < 2):
@@ -100,10 +100,12 @@ if path:
add_cmds = [ cmd for cmd in add_cmds if path in cmd ]
for cmd in add_cmds:
+ cmd = "/opt/vyatta/sbin/my_" + cmd
+
try:
- cmd(f'/opt/vyatta/sbin/my_{cmd}', message='Called process error')
- except OSError as err:
- print(err)
+ subprocess.check_call(cmd, shell=True)
+ except subprocess.CalledProcessError as err:
+ print("Called process error: {}.".format(err))
if effective_config.session_changed():
print("Merge complete. Use 'commit' to make changes effective.")
diff --git a/src/op_mode/dns_forwarding_reset.py b/src/op_mode/dns_forwarding_reset.py
index 93c2444b9..dad78d7e8 100755
--- a/src/op_mode/dns_forwarding_reset.py
+++ b/src/op_mode/dns_forwarding_reset.py
@@ -21,13 +21,12 @@
import os
-import sys
import argparse
-import vyos.config
+from sys import exit
+from vyos.config import Config
from vyos.util import run
-
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--all", action="store_true", help="Reset all cache")
parser.add_argument("domain", type=str, nargs="?", help="Domain to reset cache entries for")
@@ -36,16 +35,18 @@ if __name__ == '__main__':
args = parser.parse_args()
# Do nothing if service is not configured
- c = vyos.config.Config()
- if not c.exists_effective('service dns forwarding'):
+ c = Config()
+ if not c.exists_effective(['service', 'dns', 'forwarding']):
print("DNS forwarding is not configured")
- sys.exit(0)
+ exit(0)
if args.all:
run("rec_control wipe-cache \'.$\'")
- sys.exit(1)
+ exit(0)
+
elif args.domain:
run("rec_control wipe-cache \'{0}$\'".format(args.domain))
+
else:
parser.print_help()
- sys.exit(1)
+ exit(1)
diff --git a/src/op_mode/lldp_op.py b/src/op_mode/lldp_op.py
index c8a5543b6..5d48e3210 100755
--- a/src/op_mode/lldp_op.py
+++ b/src/op_mode/lldp_op.py
@@ -23,6 +23,7 @@ from sys import exit
from tabulate import tabulate
from vyos.util import popen
+from vyos.config import Config
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--all", action="store_true", help="Show LLDP neighbors on all interfaces")
@@ -141,6 +142,11 @@ if __name__ == '__main__':
args = parser.parse_args()
tmp = { 'neighbors' : [] }
+ c = Config()
+ if not c.exists_effective(['service', 'lldp']):
+ print('Service LLDP is not configured')
+ exit(0)
+
if args.all:
neighbors = minidom.parseString(_get_neighbors())
for neighbor in neighbors.getElementsByTagName('interface'):
diff --git a/src/op_mode/wireguard.py b/src/op_mode/wireguard.py
index d940d79eb..1b90f4fa7 100755
--- a/src/op_mode/wireguard.py
+++ b/src/op_mode/wireguard.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,8 +13,6 @@
#
# 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 argparse
import os
@@ -27,7 +25,7 @@ from vyos.ifconfig import WireGuardIf
from vyos import ConfigError
from vyos.config import Config
-from vyos.util import run
+from vyos.util import cmd, run
dir = r'/config/auth/wireguard'
psk = dir + '/preshared.key'
@@ -88,10 +86,11 @@ def genpsk():
it's stored only in the cli config
"""
- run('wg genpsk')
+ psk = cmd('wg genpsk')
+ print(psk)
def list_key_dirs():
- """ lists all dirs under /config/auth/wireguard """
+ """ lists all dirs under /config/auth/wireguard """
if os.path.exists(dir):
nks = next(os.walk(dir))[1]
for nk in nks: