From 6748dbe0100cfedf1b2f00884899e71729bfa9f3 Mon Sep 17 00:00:00 2001 From: Kim Hagen Date: Tue, 17 Aug 2021 07:04:34 -0500 Subject: add part 2fa --- interface-definitions/interfaces-openvpn.xml.in | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 7ff08ac86..1a07e7d91 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -635,6 +635,53 @@ net30 + + + 2-factor authentication + + + + + Time-based One-Time Passwords + + + + + Maximum allowed clock slop in seconds (default: 180) + + 180 + + + + time drift in seconds (default: 0) + + 0 + + + + Step value for TOTP in seconds (default: 30) + + 30 + + + + Number of digits to use from TOTP hash (default: 6) + + 6 + + + + expect password as result of a challenge response protocol (default: enabled) + + ^(enable|disable)$ + + + enable + + + + + -- cgit v1.2.3 From 02b6370c3cd1b580b0140deed6c250a682c3a4eb Mon Sep 17 00:00:00 2001 From: Kim Hagen Date: Wed, 1 Sep 2021 14:09:55 -0500 Subject: more 2fa changes --- interface-definitions/interfaces-openvpn.xml.in | 41 ++++++++++++++++++++++++- op-mode-definitions/openvpn.xml.in | 28 +++++++++++++++++ src/conf_mode/interfaces-openvpn.py | 12 +++++--- 3 files changed, 75 insertions(+), 6 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 1a07e7d91..b9575595c 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -648,32 +648,71 @@ Maximum allowed clock slop in seconds (default: 180) + + 1-65535 + Seconds + + + + 180 time drift in seconds (default: 0) + + 1-65535 + Seconds + + + + 0 Step value for TOTP in seconds (default: 30) + + 1-65535 + Seconds + + + + 30 Number of digits to use from TOTP hash (default: 6) + + 1-65535 + Seconds + + + + 6 expect password as result of a challenge response protocol (default: enabled) + + disable enable + + + disable + Disable challenge response (default) + + + enable + Enable chalenge response (default) + - ^(enable|disable)$ + ^(disable|enable)$ enable diff --git a/op-mode-definitions/openvpn.xml.in b/op-mode-definitions/openvpn.xml.in index 781fbdc9d..ee3b073b5 100644 --- a/op-mode-definitions/openvpn.xml.in +++ b/op-mode-definitions/openvpn.xml.in @@ -55,6 +55,34 @@ ${vyos_op_scripts_dir}/show_interfaces.py --intf=$4 + + + Show OpenVPN interface users + + + + + + + + Show 2fa authentication secret + + ${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$4" --intf="$6" --action=sercret + + + + Show 2fa otpauth uri + + ${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$4" --intf="$6" --action=uri + + + + Show 2fa QR code + + ${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$4" --intf="$6" --action=qrcode + + + Show summary of specified OpenVPN interface information diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index f19804910..8ccfee6ef 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -61,6 +61,9 @@ group = 'openvpn' cfg_dir = '/run/openvpn' cfg_file = '/run/openvpn/{ifname}.conf' +otp_path = '/config/auth/openvpn' +otp_file = '/config/auth/openvpn/{ifname}-otp-secrets' +secret_chars = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') def get_config(config=None): """ @@ -310,7 +313,6 @@ def verify(openvpn): if 'is_bridge_member' not in openvpn: raise ConfigError('Must specify "server subnet" or add interface to bridge in server mode') - for client_k, client_v in (dict_search('server.client', openvpn).items() or []): if (client_v.get('ip') and len(client_v['ip']) > 1) or (client_v.get('ipv6_ip') and len(client_v['ipv6_ip']) > 1): raise ConfigError(f'Server client "{client_k}": cannot specify more than 1 IPv4 and 1 IPv6 IP') @@ -362,10 +364,11 @@ def verify(openvpn): print(f'Warning: Client "{client["name"]}" IP {client["ipv6_ip"][0]} is in server IP pool, it is not reserved for this client.') if dict_search('server.2fa.totp', openvpn): - if not Path(otp_file).is_file(): - Path(otp_file).touch() + if not Path(otp_file.format(**openvpn)).is_file(): + Path(otp_path).mkdir(parents=True, exist_ok=True) + Path(otp_file.format(**openvpn)).touch() for client in (dict_search('server.client', openvpn) or []): - with open(otp_file, "r+") as f: + with open(otp_file.format(**openvpn), "r+") as f: users = f.readlines() exists = None for user in users: @@ -377,7 +380,6 @@ def verify(openvpn): totp_secret = ''.join(random.choice(secret_chars) for _ in range(16)) f.write("{0} otp totp:sha1:base32:{1}::xxx *\n".format(client, totp_secret)) - else: # checks for both client and site-to-site go here if dict_search('server.reject_unconfigured_clients', openvpn): -- cgit v1.2.3 From fa101ed0e160c5f8cb4fd1b714ebddd4134b4798 Mon Sep 17 00:00:00 2001 From: Kim Hagen Date: Fri, 3 Sep 2021 04:13:57 -0500 Subject: remove default values from xml --- interface-definitions/interfaces-openvpn.xml.in | 5 ----- 1 file changed, 5 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index b9575595c..0395f7d65 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -656,7 +656,6 @@ - 180 @@ -669,7 +668,6 @@ - 0 @@ -682,7 +680,6 @@ - 30 @@ -695,7 +692,6 @@ - 6 @@ -715,7 +711,6 @@ ^(disable|enable)$ - enable -- cgit v1.2.3 From 5366f9c9ce9850cdf3fddbf0c2947994a0c7eef6 Mon Sep 17 00:00:00 2001 From: Kim Hagen Date: Fri, 3 Sep 2021 06:00:07 -0500 Subject: do not use capitals in opmode rename t0 to drift add subnemu for 2fa to make it more readable --- data/templates/openvpn/server.conf.tmpl | 2 +- interface-definitions/interfaces-openvpn.xml.in | 2 +- op-mode-definitions/openvpn.xml.in | 39 +++++++++++++++---------- 3 files changed, 25 insertions(+), 18 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl index 679c25dd8..d97ff7717 100644 --- a/data/templates/openvpn/server.conf.tmpl +++ b/data/templates/openvpn/server.conf.tmpl @@ -131,7 +131,7 @@ push "dhcp-option DOMAIN {{ server.domain_name }}" {% if server['2fa']['totp'] is defined and server['2fa']['totp'] is not none %} plugin "/usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-otp.so" "otp_secrets=/config/auth/openvpn/{{ ifname }}-otp-secrets otp_slop= {{- server['2fa']['totp']['slop']|default(180) }} totp_t0= -{{- server['2fa']['totp']['t0']|default(0) }} totp_step= +{{- server['2fa']['totp']['drift']|default(0) }} totp_step= {{- server['2fa']['totp']['step']|default(30) }} totp_digits= {{- server['2fa']['totp']['digits']|default(6)}} password_is_cr= {%-if server['2fa']['totp']['challenge']|default('enable') == 'enable' %}1{% else %}0{% endif %}" diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 0395f7d65..62fac9be0 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -657,7 +657,7 @@ - + time drift in seconds (default: 0) diff --git a/op-mode-definitions/openvpn.xml.in b/op-mode-definitions/openvpn.xml.in index 6549976c5..068d5d8fb 100644 --- a/op-mode-definitions/openvpn.xml.in +++ b/op-mode-definitions/openvpn.xml.in @@ -63,24 +63,31 @@ - + - Show 2fa authentication secret + Show 2fa information - ${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$6" --intf="$4" --action=secret - - - - Show 2fa otpauth uri - - ${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$6" --intf="$4" --action=uri - - - - Show 2fa QR code - - ${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$6" --intf="$4" --action=qrcode - + + + + Show 2fa authentication secret + + ${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$6" --intf="$4" --action=secret + + + + Show 2fa otpauth uri + + ${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$6" --intf="$4" --action=uri + + + + Show 2fa QR code + + ${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$6" --intf="$4" --action=qrcode + + + -- cgit v1.2.3 From 74a8c4b42b5ad31cdf34ddea07f83f7bff86be87 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 4 Oct 2021 22:25:19 +0200 Subject: bgp: T3741: "parameter default no-ipv4-unicast" is now a default option --- data/templates/frr/bgpd.frr.tmpl | 2 - .../include/bgp/protocol-common-config.xml.i | 6 -- smoketest/configs/bgp-small-ipv4-unicast | 77 ++++++++++++++++++++++ smoketest/scripts/cli/test_protocols_bgp.py | 3 - src/migration-scripts/bgp/1-to-2 | 77 ++++++++++++++++++++++ 5 files changed, 154 insertions(+), 11 deletions(-) create mode 100644 smoketest/configs/bgp-small-ipv4-unicast create mode 100755 src/migration-scripts/bgp/1-to-2 (limited to 'interface-definitions') diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 27a2b98a5..a35930c93 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -230,10 +230,8 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% else %} no bgp ebgp-requires-policy {% endif %} -{% if parameters is defined and parameters.default is defined and parameters.default.no_ipv4_unicast is defined %} {# Option must be set before any neighbor - see https://phabricator.vyos.net/T3463 #} no bgp default ipv4-unicast -{% endif %} {# Workaround for T2100 until we have decided about a migration script #} no bgp network import-check {% if address_family is defined and address_family is not none %} diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 30033bc50..2dfae517e 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -1253,12 +1253,6 @@ - - - Deactivate IPv4 unicast for a peer by default - - - diff --git a/smoketest/configs/bgp-small-ipv4-unicast b/smoketest/configs/bgp-small-ipv4-unicast new file mode 100644 index 000000000..a4dcd6218 --- /dev/null +++ b/smoketest/configs/bgp-small-ipv4-unicast @@ -0,0 +1,77 @@ +interfaces { + ethernet eth0 { + address 192.0.2.1 + address 2001:db8::1/64 + } + loopback lo { + } +} +protocols { + bgp 65001 { + address-family { + ipv4-unicast { + network 10.0.150.0/23 { + } + } + ipv6-unicast { + network 2001:db8:200::/40 { + } + } + } + neighbor 192.0.2.10 { + remote-as 65010 + } + neighbor 192.0.2.11 { + remote-as 65011 + } + neighbor 2001:db8::10 { + remote-as 65010 + } + neighbor 2001:db8::11 { + remote-as 65011 + } + parameters { + log-neighbor-changes + } + } +} +service { + ssh { + disable-host-validation + port 22 + } +} +system { + config-management { + commit-revisions 200 + } + console { + device ttyS0 { + speed 115200 + } + } + domain-name vyos.net + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + syslog { + global { + facility all { + level notice + } + facility protocols { + level debug + } + } + } +} + +/* Warning: Do not remove the following line. */ +/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@9:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */ +/* Release version: 1.2.5 */ diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 29b5aa9d1..16284ed01 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -221,8 +221,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): # Default local preference (higher = more preferred, default value is 100) self.cli_set(base_path + ['parameters', 'default', 'local-pref', local_pref]) - # Deactivate IPv4 unicast for a peer by default - self.cli_set(base_path + ['parameters', 'default', 'no-ipv4-unicast']) self.cli_set(base_path + ['parameters', 'graceful-restart', 'stalepath-time', stalepath_time]) self.cli_set(base_path + ['parameters', 'graceful-shutdown']) self.cli_set(base_path + ['parameters', 'ebgp-requires-policy']) @@ -246,7 +244,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' bgp router-id {router_id}', frrconfig) self.assertIn(f' bgp log-neighbor-changes', frrconfig) self.assertIn(f' bgp default local-preference {local_pref}', frrconfig) - self.assertIn(f' no bgp default ipv4-unicast', frrconfig) self.assertIn(f' bgp graceful-restart stalepath-time {stalepath_time}', frrconfig) self.assertIn(f' bgp graceful-shutdown', frrconfig) self.assertIn(f' bgp bestpath as-path multipath-relax', frrconfig) diff --git a/src/migration-scripts/bgp/1-to-2 b/src/migration-scripts/bgp/1-to-2 new file mode 100755 index 000000000..4c6d5ceb8 --- /dev/null +++ b/src/migration-scripts/bgp/1-to-2 @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 . + +# T3741: no-ipv4-unicast is now enabled by default + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree +from vyos.template import is_ipv4 + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['protocols', 'bgp'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +# This is now a default option - simply delete it. +# As it was configured explicitly - we can also bail out early as we need to +# do nothing! +if config.exists(base + ['parameters', 'default', 'no-ipv4-unicast']): + config.delete(base + ['parameters', 'default', 'no-ipv4-unicast']) + + # Check if the "default" node is now empty, if so - remove it + if len(config.list_nodes(base + ['parameters', 'default'])) == 0: + config.delete(base + ['parameters', 'default']) + + # Check if the "default" node is now empty, if so - remove it + if len(config.list_nodes(base + ['parameters'])) == 0: + config.delete(base + ['parameters']) + + exit(0) + +# As we now install a new default option into BGP we need to migrate all +# existing BGP neighbors and restore the old behavior +if config.exists(base + ['neighbor']): + for neighbor in config.list_nodes(base + ['neighbor']): + peer_group = base + ['neighbor', neighbor, 'peer-group'] + if config.exists(peer_group): + peer_group_name = config.return_value(peer_group) + # peer group enables old behavior for neighbor - bail out + if config.exists(base + ['peer-group', peer_group_name, 'address-family', 'ipv4-unicast']): + continue + + afi_ipv4 = base + ['neighbor', neighbor, 'address-family', 'ipv4-unicast'] + if not config.exists(afi_ipv4): + config.set(afi_ipv4) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) -- cgit v1.2.3 From 15d4977f5d52d3fc5f9c2fa501964739f2335741 Mon Sep 17 00:00:00 2001 From: Volodymyr Date: Tue, 5 Oct 2021 12:08:24 +0000 Subject: container: T3881: Fix description for container --- interface-definitions/containers.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'interface-definitions') diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index bf672307c..fb8241d71 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -141,7 +141,7 @@ - Mount a volume into the container + Restart options for container no on-failure always -- cgit v1.2.3 From ba8630da96396f09c638fccdc9cfe6a3ee70fd58 Mon Sep 17 00:00:00 2001 From: Kim Hagen Date: Thu, 7 Oct 2021 08:44:00 -0500 Subject: pull request fixes --- data/templates/openvpn/server.conf.tmpl | 12 ++--- interface-definitions/interfaces-openvpn.xml.in | 23 +++++---- op-mode-definitions/openvpn.xml.in | 16 +++---- src/conf_mode/interfaces-openvpn.py | 18 ++++++- src/op_mode/show_openvpn_2fa.py | 64 ------------------------- src/op_mode/show_openvpn_mfa.py | 64 +++++++++++++++++++++++++ 6 files changed, 106 insertions(+), 91 deletions(-) delete mode 100755 src/op_mode/show_openvpn_2fa.py create mode 100755 src/op_mode/show_openvpn_mfa.py (limited to 'interface-definitions') diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl index 644eb805f..3104203ad 100644 --- a/data/templates/openvpn/server.conf.tmpl +++ b/data/templates/openvpn/server.conf.tmpl @@ -127,14 +127,10 @@ push "dhcp-option DNS6 {{ nameserver }}" {% if server.domain_name is defined and server.domain_name is not none %} push "dhcp-option DOMAIN {{ server.domain_name }}" {% endif %} -{% if server['2fa'] is defined and server['2fa'] is not none %} -{% if server['2fa']['totp'] is defined and server['2fa']['totp'] is not none %} -plugin "/usr/lib/openvpn/openvpn-otp.so" "otp_secrets=/config/auth/openvpn/{{ ifname }}-otp-secrets otp_slop= -{{- server['2fa']['totp']['slop']|default(180) }} totp_t0= -{{- server['2fa']['totp']['drift']|default(0) }} totp_step= -{{- server['2fa']['totp']['step']|default(30) }} totp_digits= -{{- server['2fa']['totp']['digits']|default(6)}} password_is_cr= -{%-if server['2fa']['totp']['challenge']|default('enable') == 'enable' %}1{% else %}0{% endif %}" +{% if server.mfa is defined and server.mfa is not none %} +{% if server.mfa.totp is defined and server.mfa.totp is not none %} +{% set totp_config = server.mfa.totp %} +plugin "{{ plugin_dir}}/openvpn-otp.so" "otp_secrets=/config/auth/openvpn/{{ ifname }}-otp-secrets {{ 'otp_slop=' ~ totp_config.slop }} {{ 'totp_t0=' ~ totp_config.drift }} {{ 'totp_step=' ~ totp_config.step }} {{ 'totp_digits=' ~ totp_config.digits }} password_is_cr={{ '1' if totp_config.challenge == 'enable' else '0' }}" {% endif %} {% endif %} {% endif %} diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 62fac9be0..023f9f55d 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -635,14 +635,14 @@ net30 - + - 2-factor authentication + multi-factor authentication - Time-based One-Time Passwords + Time-based one-time passwords @@ -656,10 +656,11 @@ + 180 - time drift in seconds (default: 0) + Time drift in seconds (default: 0) 1-65535 Seconds @@ -668,10 +669,11 @@ + 0 - Step value for TOTP in seconds (default: 30) + Step value for totp in seconds (default: 30) 1-65535 Seconds @@ -680,10 +682,11 @@ + 30 - Number of digits to use from TOTP hash (default: 6) + Number of digits to use for totp hash (default: 6) 1-65535 Seconds @@ -692,25 +695,27 @@ + 6 - expect password as result of a challenge response protocol (default: enabled) + Expect password as result of a challenge response protocol (default: enabled) disable enable disable - Disable challenge response (default) + Disable challenge-response enable - Enable chalenge response (default) + Enable chalenge-response (default) ^(disable|enable)$ + enable diff --git a/op-mode-definitions/openvpn.xml.in b/op-mode-definitions/openvpn.xml.in index 068d5d8fb..7243d69fd 100644 --- a/op-mode-definitions/openvpn.xml.in +++ b/op-mode-definitions/openvpn.xml.in @@ -63,28 +63,28 @@ - + - Show 2fa information + Show multi-factor authentication information - Show 2fa authentication secret + Show multi-factor authentication secret - ${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$6" --intf="$4" --action=secret + ${vyos_op_scripts_dir}/show_openvpn_mfa.py --user="$6" --intf="$4" --action=secret - Show 2fa otpauth uri + Show multi-factor authentication otpauth uri - ${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$6" --intf="$4" --action=uri + ${vyos_op_scripts_dir}/show_openvpn_mfa.py --user="$6" --intf="$4" --action=uri - Show 2fa QR code + Show multi-factor authentication QR code - ${vyos_op_scripts_dir}/show_openvpn_2fa.py --user="$6" --intf="$4" --action=qrcode + ${vyos_op_scripts_dir}/show_openvpn_mfa.py --user="$6" --intf="$4" --action=qrcode diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 365d0982e..220c4f157 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -80,6 +80,11 @@ def get_config(config=None): tmp_pki = conf.get_config_dict(['pki'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) + # We have to get the dict using 'get_config_dict' instead of 'get_interface_dict' + # as 'get_interface_dict' merges the defaults in, so we can not check for defaults in there. + tmp_openvpn = conf.get_config_dict(base + [os.environ['VYOS_TAGNODE_VALUE']], key_mangling=('-', '_'), + get_first_key=True, no_tag_node_value_mangle=True) + openvpn = get_interface_dict(conf, base) if 'deleted' not in openvpn: @@ -89,6 +94,14 @@ def get_config(config=None): openvpn['daemon_user'] = user openvpn['daemon_group'] = group + # We have to cleanup the config dict, as default values could enable features + # which are not explicitly enabled on the CLI. Example: server mfa totp + # originate comes with defaults, which will enable the + # totp plugin, even when not set via CLI so we + # need to check this first and drop those keys + if 'totp' not in tmp_openvpn['server']: + del openvpn['server']['mfa']['totp'] + return openvpn def is_ec_private_key(pki, cert_name): @@ -369,8 +382,8 @@ def verify(openvpn): 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.') - # add 2fa users to the file the 2fa plugin uses - if dict_search('server.2fa.totp', openvpn): + # add mfa users to the file the mfa plugin uses + if dict_search('server.mfa.totp', openvpn): if not Path(otp_file.format(**openvpn)).is_file(): Path(otp_path).mkdir(parents=True, exist_ok=True) Path(otp_file.format(**openvpn)).touch() @@ -590,6 +603,7 @@ def generate_pki_files(openvpn): def generate(openvpn): interface = openvpn['ifname'] directory = os.path.dirname(cfg_file.format(**openvpn)) + plugin_dir = '/usr/lib/openvpn' # we can't know in advance which clients have been removed, # thus all client configs will be removed and re-added on demand diff --git a/src/op_mode/show_openvpn_2fa.py b/src/op_mode/show_openvpn_2fa.py deleted file mode 100755 index 8600f755d..000000000 --- a/src/op_mode/show_openvpn_2fa.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2017, 2021 VyOS maintainers and contributors -# -# 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 . - -import re -import socket -import urllib.parse -import argparse - -from vyos.util import popen - -otp_file = '/config/auth/openvpn/{interface}-otp-secrets' - -def get_2fa_secret(interface, client): - try: - with open(otp_file.format(interface=interface), "r") as f: - users = f.readlines() - for user in users: - if re.search('^' + client + ' ', user): - return user.split(':')[3] - except: - pass - -def get_2fa_uri(client, secret): - hostname = socket.gethostname() - fqdn = socket.getfqdn() - uri = 'otpauth://totp/{hostname}:{client}@{fqdn}?secret={secret}' - - return urllib.parse.quote(uri.format(hostname=hostname, client=client, fqdn=fqdn, secret=secret), safe='/:@?=') - -if __name__ == '__main__': - parser = argparse.ArgumentParser(add_help=False, description='Show 2fa information') - parser.add_argument('--intf', action="store", type=str, default='', help='only show the specified interface') - parser.add_argument('--user', action="store", type=str, default='', help='only show the specified users') - parser.add_argument('--action', action="store", type=str, default='show', help='action to perform') - - args = parser.parse_args() - secret = get_2fa_secret(args.intf, args.user) - - if args.action == "secret" and secret: - print(secret) - - if args.action == "uri" and secret: - uri = get_2fa_uri(args.user, secret) - print(uri) - - if args.action == "qrcode" and secret: - uri = get_2fa_uri(args.user, secret) - qrcode,err = popen('qrencode -t ansiutf8', input=uri) - print(qrcode) - diff --git a/src/op_mode/show_openvpn_mfa.py b/src/op_mode/show_openvpn_mfa.py new file mode 100755 index 000000000..1ab54600c --- /dev/null +++ b/src/op_mode/show_openvpn_mfa.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +# Copyright 2017, 2021 VyOS maintainers and contributors +# +# 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 . + +import re +import socket +import urllib.parse +import argparse + +from vyos.util import popen + +otp_file = '/config/auth/openvpn/{interface}-otp-secrets' + +def get_mfa_secret(interface, client): + try: + with open(otp_file.format(interface=interface), "r") as f: + users = f.readlines() + for user in users: + if re.search('^' + client + ' ', user): + return user.split(':')[3] + except: + pass + +def get_mfa_uri(client, secret): + hostname = socket.gethostname() + fqdn = socket.getfqdn() + uri = 'otpauth://totp/{hostname}:{client}@{fqdn}?secret={secret}' + + return urllib.parse.quote(uri.format(hostname=hostname, client=client, fqdn=fqdn, secret=secret), safe='/:@?=') + +if __name__ == '__main__': + parser = argparse.ArgumentParser(add_help=False, description='Show two-factor authentication information') + parser.add_argument('--intf', action="store", type=str, default='', help='only show the specified interface') + parser.add_argument('--user', action="store", type=str, default='', help='only show the specified users') + parser.add_argument('--action', action="store", type=str, default='show', help='action to perform') + + args = parser.parse_args() + secret = get_mfa_secret(args.intf, args.user) + + if args.action == "secret" and secret: + print(secret) + + if args.action == "uri" and secret: + uri = get_mfa_uri(args.user, secret) + print(uri) + + if args.action == "qrcode" and secret: + uri = get_mfa_uri(args.user, secret) + qrcode,err = popen('qrencode -t ansiutf8', input=uri) + print(qrcode) + -- cgit v1.2.3 From 4218a5bcb1093108e25d4e07fa07050b4f79d3d5 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 10 Oct 2021 18:53:02 +0200 Subject: lcd: T2564: add support for hd44780 displays --- data/templates/lcd/LCDd.conf.tmpl | 7 +++++++ debian/control | 1 + interface-definitions/system-lcd.xml.in | 8 ++++++-- 3 files changed, 14 insertions(+), 2 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/lcd/LCDd.conf.tmpl b/data/templates/lcd/LCDd.conf.tmpl index 6cf6a440f..2c7ad920f 100644 --- a/data/templates/lcd/LCDd.conf.tmpl +++ b/data/templates/lcd/LCDd.conf.tmpl @@ -53,6 +53,8 @@ DriverPath=/usr/lib/x86_64-linux-gnu/lcdproc/ Driver=CFontzPacket {% elif model == 'sdec' %} Driver=sdeclcd +{% elif model == 'hd44780' %} +Driver=hd44780 {% endif %} {% endif %} @@ -128,5 +130,10 @@ USB=yes ## SDEC driver for Lanner, Watchguard, Sophos sppliances ## [sdeclcd] # No options +{% elif model == 'hd44780' %} +[hd44780] +ConnectionType=ezio +Device={{ device }} +Size=16x2 {% endif %} {% endif %} diff --git a/debian/control b/debian/control index f3a26e73e..6c0f2f886 100644 --- a/debian/control +++ b/debian/control @@ -72,6 +72,7 @@ Depends: iw, keepalived (>=2.0.5), lcdproc, + lcdproc-extra-drivers, libatomic1, libcharon-extra-plugins (>=5.9), libcharon-extauth-plugins (>=5.9), diff --git a/interface-definitions/system-lcd.xml.in b/interface-definitions/system-lcd.xml.in index 36116ae1b..4c9d5c92e 100644 --- a/interface-definitions/system-lcd.xml.in +++ b/interface-definitions/system-lcd.xml.in @@ -12,7 +12,7 @@ Model of the display attached to this system [REQUIRED] - cfa-533 cfa-631 cfa-633 cfa-635 sdec + cfa-533 cfa-631 cfa-633 cfa-635 hd44780 sdec cfa-533 @@ -30,12 +30,16 @@ cfa-635 Crystalfontz CFA-635 + + hd44780 + Hitachi HD44780, Caswell Appliances + sdec Lanner, Watchguard, Nexcom NSA, Sophos UTM appliances - ^(cfa-533|cfa-631|cfa-633|cfa-635|sdec)$ + ^(cfa-533|cfa-631|cfa-633|cfa-635|hd44780|sdec)$ -- cgit v1.2.3 From a633bdd2ed65971b2f137d5f985f8f3d85b9acfc Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Fri, 15 Oct 2021 18:18:39 +0000 Subject: containers: T3676: Allow to set capabilities --- interface-definitions/containers.xml.in | 24 ++++++++++++++++++++++++ src/conf_mode/containers.py | 10 +++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) (limited to 'interface-definitions') diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index fb8241d71..24d1870af 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -21,6 +21,30 @@ + + + Add capabilities + + net-admin setpcap sys-time + + + net-admin + Net-admin option + + + setpcap + Setpcap option + + + sys-time + Sys-time option + + + ^(net-admin|setpcap|sys-time)$ + + + + #include #include diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 1e0197a13..cc34f9d39 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -271,6 +271,14 @@ def apply(container): tmp = run(f'podman image exists {image}') if tmp != 0: print(os.system(f'podman pull {image}')) + # Add capability options. Should be in uppercase + cap_add = '' + if 'cap_add' in container_config: + for c in container_config['cap_add']: + c = c.upper() + c = c.replace('-', '_') + cap_add += f' --cap-add={c}' + # Check/set environment options "-e foo=bar" env_opt = '' if 'environment' in container_config: @@ -299,7 +307,7 @@ def apply(container): dvol = vol_config['destination'] volume += f' -v {svol}:{dvol}' - container_base_cmd = f'podman run --detach --interactive --tty --replace ' \ + container_base_cmd = f'podman run --detach --interactive --tty --replace {cap_add} ' \ f'--memory {memory}m --memory-swap 0 --restart {restart} ' \ f'--name {name} {port} {volume} {env_opt}' if 'allow_host_networks' in container_config: -- cgit v1.2.3 From 3d00140453b3967370c77ddd9dac4af223a7ddce Mon Sep 17 00:00:00 2001 From: Marek Isalski Date: Fri, 6 Aug 2021 14:44:48 +0100 Subject: l2tp: T3724: allow setting accel-ppp l2tp host-name --- data/templates/accel-ppp/l2tp.config.tmpl | 3 +++ interface-definitions/vpn_l2tp.xml.in | 5 +++++ src/conf_mode/vpn_l2tp.py | 2 ++ 3 files changed, 10 insertions(+) (limited to 'interface-definitions') diff --git a/data/templates/accel-ppp/l2tp.config.tmpl b/data/templates/accel-ppp/l2tp.config.tmpl index 44c96b935..9fcda76d4 100644 --- a/data/templates/accel-ppp/l2tp.config.tmpl +++ b/data/templates/accel-ppp/l2tp.config.tmpl @@ -57,6 +57,9 @@ bind={{ outside_addr }} {% if lns_shared_secret %} secret={{ lns_shared_secret }} {% endif %} +{% if lns_host_name %} +host-name={{ lns_host_name }} +{% endif %} [client-ip-range] 0.0.0.0/0 diff --git a/interface-definitions/vpn_l2tp.xml.in b/interface-definitions/vpn_l2tp.xml.in index cbd5e38e7..6cb0d4544 100644 --- a/interface-definitions/vpn_l2tp.xml.in +++ b/interface-definitions/vpn_l2tp.xml.in @@ -34,6 +34,11 @@ Tunnel password used to authenticate the client (LAC) + + + Sent to the client (LAC) in the Host-Name attribute + + diff --git a/src/conf_mode/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py index 9c52f77ca..818e8fa0b 100755 --- a/src/conf_mode/vpn_l2tp.py +++ b/src/conf_mode/vpn_l2tp.py @@ -290,6 +290,8 @@ def get_config(config=None): # LNS secret if conf.exists(['lns', 'shared-secret']): l2tp['lns_shared_secret'] = conf.return_value(['lns', 'shared-secret']) + if conf.exists(['lns', 'host-name']): + l2tp['lns_host_name'] = conf.return_value(['lns', 'host-name']) if conf.exists(['ccp-disable']): l2tp['ccp_disable'] = True -- cgit v1.2.3 From 56810c62f91d3f11e23b2836bb4d14db6ec8ce25 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 16 Oct 2021 19:06:13 +0200 Subject: xml: l2tp: T3724: add constraint regex for host-name It's the same regex as used for "set system host-name" to not allow too much garbage here... --- interface-definitions/vpn_l2tp.xml.in | 3 +++ 1 file changed, 3 insertions(+) (limited to 'interface-definitions') diff --git a/interface-definitions/vpn_l2tp.xml.in b/interface-definitions/vpn_l2tp.xml.in index 6cb0d4544..6a88756a7 100644 --- a/interface-definitions/vpn_l2tp.xml.in +++ b/interface-definitions/vpn_l2tp.xml.in @@ -37,6 +37,9 @@ Sent to the client (LAC) in the Host-Name attribute + + [A-Za-z0-9][-.A-Za-z0-9]*[A-Za-z0-9] + -- cgit v1.2.3 From 35aeea69c62a1755595d34b856d03f58cdd2da4c Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Mon, 11 Oct 2021 06:32:07 +0000 Subject: ddclient: T3897: Add option for IPv6 Dynamic DNS --- data/templates/dynamic-dns/ddclient.conf.tmpl | 2 +- interface-definitions/dns-dynamic.xml.in | 6 ++++ smoketest/scripts/cli/test_service_dns_dynamic.py | 38 +++++++++++++++++++++++ src/conf_mode/dynamic_dns.py | 2 +- 4 files changed, 46 insertions(+), 2 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/dynamic-dns/ddclient.conf.tmpl b/data/templates/dynamic-dns/ddclient.conf.tmpl index 9d379de00..517e4bad4 100644 --- a/data/templates/dynamic-dns/ddclient.conf.tmpl +++ b/data/templates/dynamic-dns/ddclient.conf.tmpl @@ -9,7 +9,7 @@ ssl=yes {% set web_skip = ", web-skip='" + interface[iface].use_web.skip + "'" if interface[iface].use_web.skip is defined else '' %} use=web, web='{{ interface[iface].use_web.url }}'{{ web_skip }} {% else %} -use=if, if={{ iface }} +{{ 'usev6=if' if interface[iface].ipv6_enable is defined else 'use=if' }}, if={{ iface }} {% endif %} {% if interface[iface].rfc2136 is defined and interface[iface].rfc2136 is not none %} diff --git a/interface-definitions/dns-dynamic.xml.in b/interface-definitions/dns-dynamic.xml.in index 250642691..64826516e 100644 --- a/interface-definitions/dns-dynamic.xml.in +++ b/interface-definitions/dns-dynamic.xml.in @@ -274,6 +274,12 @@ + + + Allow explicit IPv6 addresses for Dynamic DNS for this interface + + + diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py index d8a87ffd4..03fccf2c7 100755 --- a/smoketest/scripts/cli/test_service_dns_dynamic.py +++ b/smoketest/scripts/cli/test_service_dns_dynamic.py @@ -24,6 +24,7 @@ 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 PROCESS_NAME = 'ddclient' DDCLIENT_CONF = '/run/ddclient/ddclient.conf' @@ -122,5 +123,42 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase): # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) + def test_dyndns_ipv6(self): + ddns = ['interface', 'eth0', 'service', 'dynv6'] + hostname = 'test.ddns.vyos.io' + proto = 'dyndns2' + user = 'none' + password = 'paSS_4ord' + servr = 'ddns.vyos.io' + + self.cli_set(base_path + ['interface', 'eth0', 'ipv6-enable']) + self.cli_set(base_path + ddns + ['host-name', hostname]) + self.cli_set(base_path + ddns + ['login', user]) + self.cli_set(base_path + ddns + ['password', password]) + self.cli_set(base_path + ddns + ['protocol', proto]) + self.cli_set(base_path + ddns + ['server', servr]) + + # commit changes + self.cli_commit() + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + config = read_file(DDCLIENT_CONF) + + protocol = get_config_value('protocol') + login = get_config_value('login') + pwd = get_config_value('password') + server = get_config_value('server') + + # Check some generating config parameters + self.assertTrue(protocol == proto) + self.assertTrue(login == user) + self.assertTrue(pwd == "'" + password + "'") + self.assertTrue(server == servr) + + self.assertIn('usev6=if', config) + self.assertIn(hostname, config) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/dynamic_dns.py b/src/conf_mode/dynamic_dns.py index c979feca7..646de6324 100755 --- a/src/conf_mode/dynamic_dns.py +++ b/src/conf_mode/dynamic_dns.py @@ -132,7 +132,7 @@ def generate(dyndns): return None render(config_file, 'dynamic-dns/ddclient.conf.tmpl', dyndns, - permission=0o600) + permission=0o644) return None -- cgit v1.2.3 From 103fca9f77c28e392146f791e0241e119feeadc9 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 21 Oct 2021 19:38:38 +0200 Subject: tunnel: T3925: dhcp-interface was of no use - use source-interface instead (cherry picked from commit c1015d8ce0013719eb898b60b14ffec192b8141c) --- interface-definitions/interfaces-tunnel.xml.in | 1 - python/vyos/configverify.py | 7 ++-- smoketest/configs/tunnel-broker | 2 +- smoketest/scripts/cli/test_interfaces_tunnel.py | 20 ----------- src/migration-scripts/interfaces/21-to-22 | 46 +++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 27 deletions(-) create mode 100755 src/migration-scripts/interfaces/21-to-22 (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in index 7450ef2af..cca732f82 100644 --- a/interface-definitions/interfaces-tunnel.xml.in +++ b/interface-definitions/interfaces-tunnel.xml.in @@ -54,7 +54,6 @@ - #include Encapsulation of this tunnel interface diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 8aca76568..365a28feb 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -110,15 +110,12 @@ def verify_tunnel(config): raise ConfigError('Must configure the tunnel encapsulation for '\ '{ifname}!'.format(**config)) - if 'source_address' not in config and 'dhcp_interface' not in config: - raise ConfigError('source-address is mandatory for tunnel') + if 'source_address' not in config and 'source_interface' not in config: + raise ConfigError('source-address or source-interface required for tunnel!') if 'remote' not in config and config['encapsulation'] != 'gre': raise ConfigError('remote ip address is mandatory for tunnel') - if {'source_address', 'dhcp_interface'} <= set(config): - raise ConfigError('Can not use both source-address and dhcp-interface') - if config['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre', 'ip6gretap', 'ip6erspan']: error_ipv6 = 'Encapsulation mode requires IPv6' if 'source_address' in config and not is_ipv6(config['source_address']): diff --git a/smoketest/configs/tunnel-broker b/smoketest/configs/tunnel-broker index d4a5c2dfc..03ac0db41 100644 --- a/smoketest/configs/tunnel-broker +++ b/smoketest/configs/tunnel-broker @@ -56,7 +56,7 @@ interfaces { tunnel tun100 { address 172.16.0.1/30 encapsulation gre-bridge - local-ip 192.0.2.0 + dhcp-interface eth0 remote-ip 192.0.2.100 } tunnel tun200 { diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py index 841527d21..fc2e254d6 100755 --- a/smoketest/scripts/cli/test_interfaces_tunnel.py +++ b/smoketest/scripts/cli/test_interfaces_tunnel.py @@ -156,26 +156,6 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase): self.cli_delete(self._base_path + [interface]) self.cli_commit() - def test_tunnel_verify_local_dhcp(self): - # We can not use source-address and dhcp-interface at the same time - - interface = f'tun1020' - local_if_addr = f'10.0.0.1/24' - - self.cli_set(self._base_path + [interface, 'address', local_if_addr]) - self.cli_set(self._base_path + [interface, 'encapsulation', 'gre']) - self.cli_set(self._base_path + [interface, 'source-address', self.local_v4]) - self.cli_set(self._base_path + [interface, 'remote', remote_ip4]) - self.cli_set(self._base_path + [interface, 'dhcp-interface', 'eth0']) - - # source-address and dhcp-interface can not be used at the same time - with self.assertRaises(ConfigSessionError): - self.cli_commit() - self.cli_delete(self._base_path + [interface, 'dhcp-interface']) - - # Check if commit is ok - self.cli_commit() - def test_tunnel_parameters_gre(self): interface = f'tun1030' gre_key = '10' diff --git a/src/migration-scripts/interfaces/21-to-22 b/src/migration-scripts/interfaces/21-to-22 new file mode 100755 index 000000000..098102102 --- /dev/null +++ b/src/migration-scripts/interfaces/21-to-22 @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 . + +from sys import argv +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +base = ['interfaces', 'tunnel'] + +if not config.exists(base): + exit(0) + +for interface in config.list_nodes(base): + path = base + [interface, 'dhcp-interface'] + if config.exists(path): + tmp = config.return_value(path) + config.delete(path) + config.set(base + [interface, 'source-interface'], value=tmp) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) -- cgit v1.2.3 From b1db3de80b8b5f4e2dcbc6d687d342986345c4b2 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Fri, 22 Oct 2021 11:55:17 +0000 Subject: hosts: T2683: Allow multiple entries for static-host-mapping --- data/templates/vyos-hostsd/hosts.tmpl | 5 +++-- interface-definitions/dns-domain-name.xml.in | 2 +- src/conf_mode/host_name.py | 2 +- src/services/vyos-hostsd | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/vyos-hostsd/hosts.tmpl b/data/templates/vyos-hostsd/hosts.tmpl index 8b73c6e51..03662d562 100644 --- a/data/templates/vyos-hostsd/hosts.tmpl +++ b/data/templates/vyos-hostsd/hosts.tmpl @@ -17,8 +17,9 @@ ff02::2 ip6-allrouters {% for tag, taghosts in hosts.items() %} # {{ tag }} {% for host, hostprops in taghosts.items() if hostprops.address is defined %} -{{ "%-15s" | format(hostprops.address) }} {{ host }} {{ hostprops.aliases|join(' ') if hostprops.aliases is defined }} +{% for addr in hostprops.address %} +{{ "%-15s" | format(addr) }} {{ host }} {{ hostprops.aliases|join(' ') if hostprops.aliases is defined }} +{% endfor %} {% endfor %} {% endfor %} {% endif %} - diff --git a/interface-definitions/dns-domain-name.xml.in b/interface-definitions/dns-domain-name.xml.in index 2b1644609..005a55ab3 100644 --- a/interface-definitions/dns-domain-name.xml.in +++ b/interface-definitions/dns-domain-name.xml.in @@ -102,11 +102,11 @@ + - diff --git a/src/conf_mode/host_name.py b/src/conf_mode/host_name.py index a7135911d..87bad0dc6 100755 --- a/src/conf_mode/host_name.py +++ b/src/conf_mode/host_name.py @@ -79,7 +79,7 @@ def get_config(config=None): # system static-host-mapping for hn in conf.list_nodes(['system', 'static-host-mapping', 'host-name']): hosts['static_host_mapping'][hn] = {} - hosts['static_host_mapping'][hn]['address'] = conf.return_value(['system', 'static-host-mapping', 'host-name', hn, 'inet']) + hosts['static_host_mapping'][hn]['address'] = conf.return_values(['system', 'static-host-mapping', 'host-name', hn, 'inet']) hosts['static_host_mapping'][hn]['aliases'] = conf.return_values(['system', 'static-host-mapping', 'host-name', hn, 'alias']) return hosts diff --git a/src/services/vyos-hostsd b/src/services/vyos-hostsd index 4c4bb036e..f4b1d0fc2 100755 --- a/src/services/vyos-hostsd +++ b/src/services/vyos-hostsd @@ -317,7 +317,7 @@ hosts_add_schema = op_type_schema.extend({ 'data': { str: { str: { - 'address': str, + 'address': [str], 'aliases': [str] } } -- cgit v1.2.3 From 45f815928976519ffba2ecadf197f725e7640852 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Mon, 25 Oct 2021 18:07:06 +0000 Subject: snmp: T2763: Add protocol TCP for service snmp --- data/templates/snmp/etc.snmpd.conf.tmpl | 2 +- interface-definitions/snmp.xml.in | 20 ++++++++++++++++++++ src/conf_mode/snmp.py | 9 +++++++-- 3 files changed, 28 insertions(+), 3 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/snmp/etc.snmpd.conf.tmpl b/data/templates/snmp/etc.snmpd.conf.tmpl index db2114fa1..30806ce8a 100644 --- a/data/templates/snmp/etc.snmpd.conf.tmpl +++ b/data/templates/snmp/etc.snmpd.conf.tmpl @@ -39,7 +39,7 @@ SysDescr {{ description }} {% endif %} # Listen -agentaddress unix:/run/snmpd.socket{% if listen_on %}{% for li in listen_on %},{{ li }}{% endfor %}{% else %},udp:161{% if ipv6_enabled %},udp6:161{% endif %}{% endif %} +agentaddress unix:/run/snmpd.socket{% if listen_on %}{% for li in listen_on %},{{ li }}{% endfor %}{% else %},{{protocol}}:161{% if ipv6_enabled %},{{protocol}}6:161{% endif %}{% endif %} # SNMP communities {% for c in communities %} diff --git a/interface-definitions/snmp.xml.in b/interface-definitions/snmp.xml.in index b0b7768d2..949536fe7 100644 --- a/interface-definitions/snmp.xml.in +++ b/interface-definitions/snmp.xml.in @@ -149,6 +149,26 @@ Oid must be 'route-table' + + + Listen protocol for SNMP + + udp tcp + + + udp + Listen protocol UDP (default) + + + tcp + Listen protocol TCP + + + ^(udp|tcp)$ + + + udp + Register a subtree for SMUX-based processing diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py index 23e45a5b7..2a420b193 100755 --- a/src/conf_mode/snmp.py +++ b/src/conf_mode/snmp.py @@ -52,6 +52,7 @@ default_config_data = { 'communities': [], 'smux_peers': [], 'location' : '', + 'protocol' : 'udp', 'description' : '', 'contact' : '', 'route_table': 'False', @@ -151,6 +152,9 @@ def get_config(): if conf.exists('location'): snmp['location'] = conf.return_value('location') + if conf.exists('protocol'): + snmp['protocol'] = conf.return_value('protocol') + if conf.exists('smux-peer'): snmp['smux_peers'] = conf.return_values('smux-peer') @@ -404,13 +408,14 @@ def verify(snmp): for listen in snmp['listen_address']: addr = listen[0] port = listen[1] + protocol = snmp['protocol'] if is_ipv4(addr): # example: udp:127.0.0.1:161 - listen = 'udp:' + addr + ':' + port + listen = f'{protocol}:{addr}:{port}' elif snmp['ipv6_enabled']: # example: udp6:[::1]:161 - listen = 'udp6:' + '[' + addr + ']' + ':' + port + listen = f'{protocol}6:[{addr}]:{port}' # We only wan't to configure addresses that exist on the system. # Hint the user if they don't exist -- cgit v1.2.3 From bb5a04954d4b3d3f0b99d608c72028e8b1720699 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Tue, 19 Oct 2021 22:56:36 +0000 Subject: containers: T3916: Add capabilities net-raw and sys-admin --- interface-definitions/containers.xml.in | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 24d1870af..1e9c36ee5 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -23,24 +23,32 @@ - Add capabilities + Container capabilities/permissions - net-admin setpcap sys-time + net-admin net-raw setpcap sys-admin sys-time net-admin - Net-admin option + Network operations (interface, firewall, routing tables) + + + net-raw + Permission to create raw network sockets setpcap - Setpcap option + Capability sets (from bounded or inherited set) + + + sys-admin + Administation operations (quotactl, mount, sethostname, setdomainame) sys-time - Sys-time option + Permission to set system clock - ^(net-admin|setpcap|sys-time)$ + ^(net-admin|net-raw|setpcap|sys-admin|sys-time)$ -- cgit v1.2.3 From 2c89e1fc4f1aa8a84b3c610b791873bf66c2fec8 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Tue, 26 Oct 2021 16:13:06 +0000 Subject: bgp: T3945: Add route-map for aggregate-address --- data/templates/frr/bgpd.frr.tmpl | 3 +++ interface-definitions/include/bgp/afi-aggregate-address.xml.i | 1 + 2 files changed, 4 insertions(+) (limited to 'interface-definitions') diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index a35930c93..2d01ed6a6 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -266,6 +266,9 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% if afi_config.aggregate_address is defined and afi_config.aggregate_address is not none %} {% for ip in afi_config.aggregate_address %} aggregate-address {{ ip }}{{ ' as-set' if afi_config.aggregate_address[ip].as_set is defined }}{{ ' summary-only' if afi_config.aggregate_address[ip].summary_only is defined }} +{% if afi_config.aggregate_address[ip].route_map is defined and afi_config.aggregate_address[ip].route_map is not none %} + aggregate-address {{ ip }} route-map {{ afi_config.aggregate_address[ip].route_map }} +{% endif %} {% endfor %} {% endif %} {% if afi_config.maximum_paths is defined and afi_config.maximum_paths.ebgp is defined and afi_config.maximum_paths.ebgp is not none %} diff --git a/interface-definitions/include/bgp/afi-aggregate-address.xml.i b/interface-definitions/include/bgp/afi-aggregate-address.xml.i index 646751c32..c1b7958da 100644 --- a/interface-definitions/include/bgp/afi-aggregate-address.xml.i +++ b/interface-definitions/include/bgp/afi-aggregate-address.xml.i @@ -5,6 +5,7 @@ +#include Announce the aggregate summary network only -- cgit v1.2.3 From 0852c588d5557052af442cb1a3887f94046fa0f4 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 29 Oct 2021 22:14:48 +0200 Subject: https: pki: T3642: embed CA certificate into chain if specified --- interface-definitions/https.xml.in | 1 + src/conf_mode/https.py | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/https.xml.in b/interface-definitions/https.xml.in index bb6f71744..f60df7c34 100644 --- a/interface-definitions/https.xml.in +++ b/interface-definitions/https.xml.in @@ -121,6 +121,7 @@ TLS certificates + #include #include diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index be4380462..92dc4a410 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -28,6 +28,7 @@ from vyos.pki import wrap_certificate from vyos.pki import wrap_private_key from vyos.template import render from vyos.util import call +from vyos.util import write_file from vyos import airbag airbag.enable() @@ -139,15 +140,18 @@ def generate(https): cert_path = os.path.join(cert_dir, f'{cert_name}.pem') key_path = os.path.join(key_dir, f'{cert_name}.pem') - with open(cert_path, 'w') as f: - f.write(wrap_certificate(pki_cert['certificate'])) + server_cert = str(wrap_certificate(pki_cert['certificate'])) + if 'ca-certificate' in cert_dict: + ca_cert = cert_dict['ca-certificate'] + print(ca_cert) + server_cert += '\n' + str(wrap_certificate(https['pki']['ca'][ca_cert]['certificate'])) - with open(key_path, 'w') as f: - f.write(wrap_private_key(pki_cert['private']['key'])) + write_file(cert_path, server_cert) + write_file(key_path, wrap_private_key(pki_cert['private']['key'])) vyos_cert_data = { - "crt": cert_path, - "key": key_path + 'crt': cert_path, + 'key': key_path } for block in server_block_list: -- cgit v1.2.3 From f227987ccf41e01d4ddafb6db7b36ecf13148c78 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 31 Oct 2021 13:48:15 +0100 Subject: console: T3954: bugfix RuntimeError: dictionary keys changed during iteration --- interface-definitions/system-console.xml.in | 1 + src/conf_mode/system_console.py | 70 ++++++++++++++++++----------- 2 files changed, 45 insertions(+), 26 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/system-console.xml.in b/interface-definitions/system-console.xml.in index 88f7f82a9..2897e5e97 100644 --- a/interface-definitions/system-console.xml.in +++ b/interface-definitions/system-console.xml.in @@ -74,6 +74,7 @@ ^(1200|2400|4800|9600|19200|38400|57600|115200)$ + 115200 diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py index 33a546bd3..19b252513 100755 --- a/src/conf_mode/system_console.py +++ b/src/conf_mode/system_console.py @@ -18,9 +18,14 @@ import os import re from vyos.config import Config -from vyos.util import call, read_file, write_file +from vyos.configdict import dict_merge +from vyos.util import call +from vyos.util import read_file +from vyos.util import write_file from vyos.template import render -from vyos import ConfigError, airbag +from vyos.xml import defaults +from vyos import ConfigError +from vyos import airbag airbag.enable() by_bus_dir = '/dev/serial/by-bus' @@ -36,21 +41,27 @@ def get_config(config=None): console = conf.get_config_dict(base, get_first_key=True) # bail out early if no serial console is configured - if 'device' not in console.keys(): + if 'device' not in console: return console # convert CLI values to system values - for device in console['device'].keys(): - # no speed setting has been configured - use default value - if not 'speed' in console['device'][device].keys(): - tmp = { 'speed': '' } - if device.startswith('hvc'): - tmp['speed'] = 38400 - else: - tmp['speed'] = 115200 + default_values = defaults(base + ['device']) + for device, device_config in console['device'].items(): + if 'speed' not in device_config and device.startswith('hvc'): + # XEN console has a different default console speed + console['device'][device]['speed'] = 38400 + else: + # Merge in XML defaults - the proper way to do it + console['device'][device] = dict_merge(default_values, + console['device'][device]) + + return console - console['device'][device].update(tmp) +def verify(console): + if not console or 'device' not in console: + return None + for device in console['device']: if device.startswith('usb'): # It is much easiert to work with the native ttyUSBn name when using # getty, but that name may change across reboots - depending on the @@ -58,13 +69,13 @@ def get_config(config=None): # to its dynamic device file - and create a new dict entry for it. by_bus_device = f'{by_bus_dir}/{device}' if os.path.isdir(by_bus_dir) and os.path.exists(by_bus_device): - tmp = os.path.basename(os.readlink(by_bus_device)) - # updating the dict must come as last step in the loop! - console['device'][tmp] = console['device'].pop(device) + device = os.path.basename(os.readlink(by_bus_device)) - return console + # If the device name still starts with usbXXX no matching tty was found + # and it can not be used as a serial interface + if device.startswith('usb'): + raise ConfigError(f'Device {device} does not support beeing used as tty') -def verify(console): return None def generate(console): @@ -76,20 +87,29 @@ def generate(console): call(f'systemctl stop {basename}') os.unlink(os.path.join(root, basename)) - if not console: + if not console or 'device' not in console: return None - for device in console['device'].keys(): + for device, device_config in console['device'].items(): + if device.startswith('usb'): + # It is much easiert to work with the native ttyUSBn name when using + # getty, but that name may change across reboots - depending on the + # amount of connected devices. We will resolve the fixed device name + # to its dynamic device file - and create a new dict entry for it. + by_bus_device = f'{by_bus_dir}/{device}' + if os.path.isdir(by_bus_dir) and os.path.exists(by_bus_device): + device = os.path.basename(os.readlink(by_bus_device)) + config_file = base_dir + f'/serial-getty@{device}.service' getty_wants_symlink = base_dir + f'/getty.target.wants/serial-getty@{device}.service' - render(config_file, 'getty/serial-getty.service.tmpl', console['device'][device]) + render(config_file, 'getty/serial-getty.service.tmpl', device_config) os.symlink(config_file, getty_wants_symlink) # GRUB # For existing serial line change speed (if necessary) # Only applys to ttyS0 - if 'ttyS0' not in console['device'].keys(): + if 'ttyS0' not in console['device']: return None speed = console['device']['ttyS0']['speed'] @@ -98,7 +118,6 @@ def generate(console): return None lines = read_file(grub_config).split('\n') - p = re.compile(r'^(.* console=ttyS0),[0-9]+(.*)$') write = False newlines = [] @@ -122,9 +141,8 @@ def generate(console): return None def apply(console): - # reset screen blanking + # Reset screen blanking call('/usr/bin/setterm -blank 0 -powersave off -powerdown 0 -term linux /dev/tty1 2>&1') - # Reload systemd manager configuration call('systemctl daemon-reload') @@ -136,11 +154,11 @@ def apply(console): call('/usr/bin/setterm -blank 15 -powersave powerdown -powerdown 60 -term linux /dev/tty1 2>&1') # Start getty process on configured serial interfaces - for device in console['device'].keys(): + for device in console['device']: # Only start console if it exists on the running system. If a user # detaches a USB serial console and reboots - it should not fail! if os.path.exists(f'/dev/{device}'): - call(f'systemctl start serial-getty@{device}.service') + call(f'systemctl restart serial-getty@{device}.service') return None -- cgit v1.2.3