diff options
38 files changed, 599 insertions, 353 deletions
diff --git a/data/templates/accel-ppp/pppoe.config.j2 b/data/templates/accel-ppp/pppoe.config.j2 index 0a92e2d54..f4129d3e2 100644 --- a/data/templates/accel-ppp/pppoe.config.j2 +++ b/data/templates/accel-ppp/pppoe.config.j2 @@ -105,20 +105,13 @@ ac-name={{ access_concentrator }} {% if interface is vyos_defined %} {% for iface, iface_config in interface.items() %} -{% if iface_config.vlan_id is not vyos_defined and iface_config.vlan_range is not vyos_defined %} +{% if iface_config.vlan is not vyos_defined %} interface={{ iface }} -{% endif %} -{% if iface_config.vlan_range is vyos_defined %} -{% for regex in iface_config.regex %} -interface=re:^{{ iface | replace('.', '\\.') }}\.({{ regex }})$ -{% endfor %} -vlan-mon={{ iface }},{{ iface_config.vlan_range | join(',') }} -{% endif %} -{% if iface_config.vlan_id is vyos_defined %} -{% for vlan in iface_config.vlan_id %} -vlan-mon={{ iface }},{{ vlan }} -interface=re:^{{ iface | replace('.', '\\.') }}\.{{ vlan }}$ +{% else %} +{% for vlan in iface_config.vlan %} +interface=re:^{{ iface }}\.{{ vlan | range_to_regex }}$ {% endfor %} +vlan-mon={{ iface }},{{ iface_config.vlan | join(',') }} {% endif %} {% endfor %} {% endif %} diff --git a/data/templates/firewall/nftables-defines.j2 b/data/templates/firewall/nftables-defines.j2 index 97fc123d5..5336f7ee6 100644 --- a/data/templates/firewall/nftables-defines.j2 +++ b/data/templates/firewall/nftables-defines.j2 @@ -7,6 +7,7 @@ set A_{{ group_name }} { type {{ ip_type }} flags interval + auto-merge {% if group_conf.address is vyos_defined or includes %} elements = { {{ group_conf.address | nft_nested_group(includes, group.address_group, 'address') | join(",") }} } {% endif %} @@ -19,6 +20,7 @@ set A6_{{ group_name }} { type {{ ip_type }} flags interval + auto-merge {% if group_conf.address is vyos_defined or includes %} elements = { {{ group_conf.address | nft_nested_group(includes, group.ipv6_address_group, 'address') | join(",") }} } {% endif %} @@ -42,6 +44,7 @@ set N_{{ group_name }} { type {{ ip_type }} flags interval + auto-merge {% if group_conf.network is vyos_defined or includes %} elements = { {{ group_conf.network | nft_nested_group(includes, group.network_group, 'network') | join(",") }} } {% endif %} @@ -54,6 +57,7 @@ set N6_{{ group_name }} { type {{ ip_type }} flags interval + auto-merge {% if group_conf.network is vyos_defined or includes %} elements = { {{ group_conf.network | nft_nested_group(includes, group.ipv6_network_group, 'network') | join(",") }} } {% endif %} @@ -66,6 +70,7 @@ set P_{{ group_name }} { type inet_service flags interval + auto-merge {% if group_conf.port is vyos_defined or includes %} elements = { {{ group_conf.port | nft_nested_group(includes, group.port_group, 'port') | join(",") }} } {% endif %} diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2 index c0780dad5..9d609f73f 100644 --- a/data/templates/firewall/nftables.j2 +++ b/data/templates/firewall/nftables.j2 @@ -175,7 +175,7 @@ table ip6 vyos_filter { {% endif %} {% endfor %} {% endif %} - {{ conf | nft_default_rule(name_text) }} + {{ conf | nft_default_rule(name_text, ipv6=True) }} } {% endfor %} {% for set_name in ns.sets %} diff --git a/data/templates/ipsec/swanctl.conf.j2 b/data/templates/ipsec/swanctl.conf.j2 index bf6b8259c..38d7981c6 100644 --- a/data/templates/ipsec/swanctl.conf.j2 +++ b/data/templates/ipsec/swanctl.conf.j2 @@ -63,9 +63,11 @@ secrets { {% if peer_conf.local_address is vyos_defined %} id-local = {{ peer_conf.local_address }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }} {% endif %} - id-remote = {{ peer }} -{% if peer_conf.authentication.id is vyos_defined %} - id-localid = {{ peer_conf.authentication.id }} +{% for address in peer_conf.remote_address %} + id-remote_{{ address | dot_colon_to_dash }} = {{ address }} +{% endfor %} +{% if peer_conf.authentication.local_id is vyos_defined %} + id-localid = {{ peer_conf.authentication.local_id }} {% endif %} {% if peer_conf.authentication.remote_id is vyos_defined %} id-remoteid = {{ peer_conf.authentication.remote_id }} @@ -93,8 +95,8 @@ secrets { {% for ra, ra_conf in remote_access.connection.items() if ra_conf.disable is not vyos_defined %} {% if ra_conf.authentication.server_mode is vyos_defined('pre-shared-secret') %} ike_{{ ra }} { -{% if ra_conf.authentication.id is vyos_defined %} - id = "{{ ra_conf.authentication.id }}" +{% if ra_conf.authentication.local_id is vyos_defined %} + id = "{{ ra_conf.authentication.local_id }}" {% elif ra_conf.local_address is vyos_defined %} id = "{{ ra_conf.local_address }}" {% endif %} diff --git a/data/templates/ipsec/swanctl/peer.j2 b/data/templates/ipsec/swanctl/peer.j2 index 90d2c774f..d097a04fc 100644 --- a/data/templates/ipsec/swanctl/peer.j2 +++ b/data/templates/ipsec/swanctl/peer.j2 @@ -2,14 +2,14 @@ {% set name = peer.replace("@", "") | dot_colon_to_dash %} {# peer needs to reference the global IKE configuration for certain values #} {% set ike = ike_group[peer_conf.ike_group] %} - peer_{{ name }} { + {{ name }} { proposals = {{ ike | get_esp_ike_cipher | join(',') }} version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }} {% if peer_conf.virtual_address is vyos_defined %} vips = {{ peer_conf.virtual_address | join(', ') }} {% endif %} - local_addrs = {{ peer_conf.local_address if peer_conf.local_address != 'any' else '0.0.0.0/0' }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }} - remote_addrs = {{ peer if peer not in ['any', '0.0.0.0'] and peer[0:1] != '@' else '0.0.0.0/0' }} + local_addrs = {{ peer_conf.local_address if peer_conf.local_address != 'any' else '%any' }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }} + remote_addrs = {{ peer_conf.remote_address | join(",") if peer_conf.remote_address is vyos_defined and 'any' not in peer_conf.remote_address else '%any' }} {% if peer_conf.authentication.mode is vyos_defined('x509') %} send_cert = always {% endif %} @@ -21,7 +21,7 @@ aggressive = yes {% endif %} rekey_time = {{ ike.lifetime }}s - mobike = {{ "yes" if ike.mobike is not defined or ike.mobike == "enable" else "no" }} + mobike = {{ "no" if ike.disable_mobike is defined else "yes" }} {% if peer[0:1] == '@' %} keyingtries = 0 reauth_time = 0 @@ -30,12 +30,12 @@ {% elif peer_conf.connection_type is vyos_defined('respond') %} keyingtries = 1 {% endif %} -{% if peer_conf.force_encapsulation is vyos_defined('enable') %} +{% if peer_conf.force_udp_encapsulation is vyos_defined %} encap = yes {% endif %} local { -{% if peer_conf.authentication.id is vyos_defined %} - id = "{{ peer_conf.authentication.id }}" +{% if peer_conf.authentication.local_id is vyos_defined %} + id = "{{ peer_conf.authentication.local_id }}" {% endif %} auth = {{ 'psk' if peer_conf.authentication.mode == 'pre-shared-secret' else 'pubkey' }} {% if peer_conf.authentication.mode == 'x509' %} @@ -58,7 +58,7 @@ children { {% if peer_conf.vti.bind is vyos_defined and peer_conf.tunnel is not vyos_defined %} {% set vti_esp = esp_group[ peer_conf.vti.esp_group ] if peer_conf.vti.esp_group is vyos_defined else esp_group[ peer_conf.default_esp_group ] %} - peer_{{ name }}_vti { + {{ name }}-vti { esp_proposals = {{ vti_esp | get_esp_ike_cipher(ike) | join(',') }} {% if vti_esp.life_bytes is vyos_defined %} life_bytes = {{ vti_esp.life_bytes }} @@ -75,7 +75,7 @@ {% set if_id = peer_conf.vti.bind | replace('vti', '') | int + 1 %} if_id_in = {{ if_id }} if_id_out = {{ if_id }} - ipcomp = {{ 'yes' if vti_esp.compression is vyos_defined('enable') else 'no' }} + ipcomp = {{ 'yes' if vti_esp.compression is vyos_defined else 'no' }} mode = {{ vti_esp.mode }} {% if peer[0:1] == '@' %} start_action = none @@ -101,7 +101,7 @@ {% set local_suffix = '[{0}/{1}]'.format(proto, local_port) if proto or local_port else '' %} {% set remote_port = tunnel_conf.remote.port if tunnel_conf.remote.port is vyos_defined else '' %} {% set remote_suffix = '[{0}/{1}]'.format(proto, remote_port) if proto or remote_port else '' %} - peer_{{ name }}_tunnel_{{ tunnel_id }} { + {{ name }}-tunnel-{{ tunnel_id }} { esp_proposals = {{ tunnel_esp | get_esp_ike_cipher(ike) | join(',') }} {% if tunnel_esp.life_bytes is vyos_defined %} life_bytes = {{ tunnel_esp.life_bytes }} @@ -126,7 +126,7 @@ local_ts = {{ peer_conf.local_address }}{{ local_suffix }} remote_ts = {{ peer }}{{ remote_suffix }} {% endif %} - ipcomp = {{ 'yes' if tunnel_esp.compression is vyos_defined('enable') else 'no' }} + ipcomp = {{ 'yes' if tunnel_esp.compression is vyos_defined else 'no' }} mode = {{ tunnel_esp.mode }} {% if peer[0:1] == '@' %} start_action = none @@ -152,7 +152,7 @@ {% endif %} } {% if tunnel_conf.passthrough is vyos_defined %} - peer_{{ name }}_tunnel_{{ tunnel_id }}_passthrough { + {{ name }}-tunnel-{{ tunnel_id }}-passthrough { local_ts = {{ tunnel_conf.passthrough | join(",") }} remote_ts = {{ tunnel_conf.passthrough | join(",") }} start_action = trap diff --git a/data/templates/ocserv/ocserv_config.j2 b/data/templates/ocserv/ocserv_config.j2 index 1d105113d..3194354e6 100644 --- a/data/templates/ocserv/ocserv_config.j2 +++ b/data/templates/ocserv/ocserv_config.j2 @@ -1,5 +1,9 @@ ### generated by vpn_openconnect.py ### +{% if listen_address is vyos_defined %} +listen-host = {{ listen_address }} +{% endif %} + tcp-port = {{ listen_ports.tcp }} udp-port = {{ listen_ports.udp }} diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in index d39dddc77..d6fa76892 100644 --- a/interface-definitions/firewall.xml.in +++ b/interface-definitions/firewall.xml.in @@ -379,6 +379,14 @@ #include <include/firewall/default-action.xml.i> #include <include/firewall/enable-default-log.xml.i> #include <include/generic-description.xml.i> + <leafNode name="default-jump-target"> + <properties> + <help>Set jump target. Action jump must be defined in default-action to use this setting</help> + <completionHelp> + <path>firewall ipv6-name</path> + </completionHelp> + </properties> + </leafNode> <tagNode name="rule"> <properties> <help>Firewall rule number (IPv6)</help> @@ -452,6 +460,14 @@ #include <include/firewall/icmpv6-type-name.xml.i> </children> </node> + <leafNode name="jump-target"> + <properties> + <help>Set jump target. Action jump must be defined to use this setting</help> + <completionHelp> + <path>firewall ipv6-name</path> + </completionHelp> + </properties> + </leafNode> </children> </tagNode> </children> @@ -527,6 +543,14 @@ #include <include/firewall/default-action.xml.i> #include <include/firewall/enable-default-log.xml.i> #include <include/generic-description.xml.i> + <leafNode name="default-jump-target"> + <properties> + <help>Set jump target. Action jump must be defined in default-action to use this setting</help> + <completionHelp> + <path>firewall name</path> + </completionHelp> + </properties> + </leafNode> <tagNode name="rule"> <properties> <help>Firewall rule number (IPv4)</help> @@ -599,6 +623,14 @@ #include <include/firewall/icmp-type-name.xml.i> </children> </node> + <leafNode name="jump-target"> + <properties> + <help>Set jump target. Action jump must be defined to use this setting</help> + <completionHelp> + <path>firewall name</path> + </completionHelp> + </properties> + </leafNode> #include <include/firewall/ttl.xml.i> </children> </tagNode> diff --git a/interface-definitions/include/accel-ppp/vlan.xml.i b/interface-definitions/include/accel-ppp/vlan.xml.i new file mode 100644 index 000000000..7df711d4b --- /dev/null +++ b/interface-definitions/include/accel-ppp/vlan.xml.i @@ -0,0 +1,20 @@ +<!-- include start from accel-ppp/vlan.xml.i --> +<leafNode name="vlan"> + <properties> + <help>VLAN monitor for automatic creation of VLAN interfaces</help> + <valueHelp> + <format>u32:1-4094</format> + <description>VLAN for automatic creation </description> + </valueHelp> + <valueHelp> + <format>start-end</format> + <description>VLAN range for automatic creation (e.g. 1-4094)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4094"/> + </constraint> + <constraintErrorMessage>VLAN IDs need to be in range 1-4094</constraintErrorMessage> + <multi/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/firewall/action.xml.i b/interface-definitions/include/firewall/action.xml.i index 512cc23bd..0738fa503 100644 --- a/interface-definitions/include/firewall/action.xml.i +++ b/interface-definitions/include/firewall/action.xml.i @@ -3,13 +3,17 @@ <properties> <help>Rule action</help> <completionHelp> - <list>accept reject drop</list> + <list>accept jump reject drop</list> </completionHelp> <valueHelp> <format>accept</format> <description>Accept matching entries</description> </valueHelp> <valueHelp> + <format>jump</format> + <description>Jump to another chain</description> + </valueHelp> + <valueHelp> <format>reject</format> <description>Reject matching entries</description> </valueHelp> @@ -18,7 +22,7 @@ <description>Drop matching entries</description> </valueHelp> <constraint> - <regex>(accept|reject|drop)</regex> + <regex>(accept|jump|reject|drop)</regex> </constraint> </properties> </leafNode> diff --git a/interface-definitions/include/firewall/default-action.xml.i b/interface-definitions/include/firewall/default-action.xml.i index 92a2fcaaf..5107768d3 100644 --- a/interface-definitions/include/firewall/default-action.xml.i +++ b/interface-definitions/include/firewall/default-action.xml.i @@ -3,13 +3,17 @@ <properties> <help>Default-action for rule-set</help> <completionHelp> - <list>drop reject accept</list> + <list>drop jump reject accept</list> </completionHelp> <valueHelp> <format>drop</format> <description>Drop if no prior rules are hit</description> </valueHelp> <valueHelp> + <format>jump</format> + <description>Jump to another chain if no prior rules are hit</description> + </valueHelp> + <valueHelp> <format>reject</format> <description>Drop and notify source if no prior rules are hit</description> </valueHelp> @@ -18,7 +22,7 @@ <description>Accept if no prior rules are hit</description> </valueHelp> <constraint> - <regex>(drop|reject|accept)</regex> + <regex>(drop|jump|reject|accept)</regex> </constraint> </properties> <defaultValue>drop</defaultValue> diff --git a/interface-definitions/include/firewall/dscp.xml.i b/interface-definitions/include/firewall/dscp.xml.i index 642212d7e..796bab548 100644 --- a/interface-definitions/include/firewall/dscp.xml.i +++ b/interface-definitions/include/firewall/dscp.xml.i @@ -12,7 +12,6 @@ </valueHelp> <constraint> <validator name="numeric" argument="--range 0-63"/> - <validator name="range" argument="--min=0 --max=63"/> </constraint> <multi/> </properties> @@ -30,7 +29,6 @@ </valueHelp> <constraint> <validator name="numeric" argument="--range 0-63"/> - <validator name="range" argument="--min=0 --max=63"/> </constraint> <multi/> </properties> diff --git a/interface-definitions/include/firewall/packet-length.xml.i b/interface-definitions/include/firewall/packet-length.xml.i index 043f56d16..91f08314a 100644 --- a/interface-definitions/include/firewall/packet-length.xml.i +++ b/interface-definitions/include/firewall/packet-length.xml.i @@ -12,7 +12,6 @@ </valueHelp> <constraint> <validator name="numeric" argument="--range 1-65535"/> - <validator name="range" argument="--min=1 --max=65535"/> </constraint> <multi/> </properties> @@ -30,7 +29,6 @@ </valueHelp> <constraint> <validator name="numeric" argument="--range 1-65535"/> - <validator name="range" argument="--min=1 --max=65535"/> </constraint> <multi/> </properties> diff --git a/interface-definitions/include/firewall/tcp-flags.xml.i b/interface-definitions/include/firewall/tcp-flags.xml.i index 5a7b5a8d3..fc0da3135 100644 --- a/interface-definitions/include/firewall/tcp-flags.xml.i +++ b/interface-definitions/include/firewall/tcp-flags.xml.i @@ -127,7 +127,6 @@ </valueHelp> <constraint> <validator name="numeric" argument="--range 1-16384"/> - <validator name="range" argument="--min=1 --max=16384"/> </constraint> </properties> </leafNode> diff --git a/interface-definitions/include/ipsec/authentication-id.xml.i b/interface-definitions/include/ipsec/authentication-id.xml.i index 4967782ec..4e0b848c3 100644 --- a/interface-definitions/include/ipsec/authentication-id.xml.i +++ b/interface-definitions/include/ipsec/authentication-id.xml.i @@ -1,10 +1,10 @@ <!-- include start from ipsec/authentication-id.xml.i --> -<leafNode name="id"> +<leafNode name="local-id"> <properties> - <help>ID for peer authentication</help> + <help>Local ID for peer authentication</help> <valueHelp> <format>txt</format> - <description>ID used for peer authentication</description> + <description>Local ID used for peer authentication</description> </valueHelp> </properties> </leafNode> diff --git a/interface-definitions/include/ipsec/remote-address.xml.i b/interface-definitions/include/ipsec/remote-address.xml.i new file mode 100644 index 000000000..ba96290d0 --- /dev/null +++ b/interface-definitions/include/ipsec/remote-address.xml.i @@ -0,0 +1,30 @@ +<!-- include start from ipsec/remote-address.xml.i --> +<leafNode name="remote-address"> + <properties> + <help>IPv4 or IPv6 address of the remote peer</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 address of the remote peer</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address of the remote peer</description> + </valueHelp> + <valueHelp> + <format>hostname</format> + <description>Fully qualified domain name of the remote peer</description> + </valueHelp> + <valueHelp> + <format>any</format> + <description>Allow any IP address of the remote peer</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + <validator name="ipv6-address"/> + <validator name="fqdn"/> + <regex>(any)</regex> + </constraint> + <multi/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/version/ipsec-version.xml.i b/interface-definitions/include/version/ipsec-version.xml.i index 59295cc91..1c978e8e6 100644 --- a/interface-definitions/include/version/ipsec-version.xml.i +++ b/interface-definitions/include/version/ipsec-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/ipsec-version.xml.i --> -<syntaxVersion component='ipsec' version='9'></syntaxVersion> +<syntaxVersion component='ipsec' version='10'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/include/version/pppoe-server-version.xml.i b/interface-definitions/include/version/pppoe-server-version.xml.i index ec81487f8..6bdd8d75c 100644 --- a/interface-definitions/include/version/pppoe-server-version.xml.i +++ b/interface-definitions/include/version/pppoe-server-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/pppoe-server-version.xml.i --> -<syntaxVersion component='pppoe-server' version='5'></syntaxVersion> +<syntaxVersion component='pppoe-server' version='6'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/service-pppoe-server.xml.in b/interface-definitions/service-pppoe-server.xml.in index 50f42849b..b31109296 100644 --- a/interface-definitions/service-pppoe-server.xml.in +++ b/interface-definitions/service-pppoe-server.xml.in @@ -68,33 +68,7 @@ </completionHelp> </properties> <children> - <leafNode name="vlan-id"> - <properties> - <help>VLAN monitor for the automatic creation of single vlan</help> - <valueHelp> - <format>u32:1-4094</format> - <description>VLAN monitor for the automatic creation of single vlan</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4094"/> - </constraint> - <constraintErrorMessage>VLAN ID needs to be between 1 and 4094</constraintErrorMessage> - <multi/> - </properties> - </leafNode> - <leafNode name="vlan-range"> - <properties> - <help>VLAN monitor for the automatic creation of vlans range</help> - <valueHelp> - <format>start-end</format> - <description>VLAN monitor range for the automatic creation of vlans (e.g. 1-4094)</description> - </valueHelp> - <constraint> - <validator name="range" argument="--min=1 --max=4094"/> - </constraint> - <multi/> - </properties> - </leafNode> + #include <include/accel-ppp/vlan.xml.i> </children> </tagNode> #include <include/accel-ppp/gateway-address.xml.i> diff --git a/interface-definitions/vpn-ipsec.xml.in b/interface-definitions/vpn-ipsec.xml.in index d36fbb024..5887a349f 100644 --- a/interface-definitions/vpn-ipsec.xml.in +++ b/interface-definitions/vpn-ipsec.xml.in @@ -24,23 +24,9 @@ <children> <leafNode name="compression"> <properties> - <help>ESP compression</help> - <completionHelp> - <list>disable enable</list> - </completionHelp> - <valueHelp> - <format>disable</format> - <description>Disable ESP compression</description> - </valueHelp> - <valueHelp> - <format>enable</format> - <description>Enable ESP compression</description> - </valueHelp> - <constraint> - <regex>(disable|enable)</regex> - </constraint> + <help>Enable ESP compression</help> + <valueless/> </properties> - <defaultValue>disable</defaultValue> </leafNode> <leafNode name="lifetime"> <properties> @@ -309,20 +295,7 @@ <leafNode name="ikev2-reauth"> <properties> <help>Re-authentication of the remote peer during an IKE re-key (IKEv2 only)</help> - <completionHelp> - <list>yes no</list> - </completionHelp> - <valueHelp> - <format>yes</format> - <description>Enable remote host re-authentication during an IKE rekey (currently broken due to a strongswan bug)</description> - </valueHelp> - <valueHelp> - <format>no</format> - <description>Disable remote host re-authenticaton during an IKE rekey</description> - </valueHelp> - <constraint> - <regex>(yes|no)</regex> - </constraint> + <valueless/> </properties> </leafNode> <leafNode name="key-exchange"> @@ -357,25 +330,11 @@ </properties> <defaultValue>28800</defaultValue> </leafNode> - <leafNode name="mobike"> + <leafNode name="disable-mobike"> <properties> - <help>Enable MOBIKE Support (IKEv2 only)</help> - <completionHelp> - <list>enable disable</list> - </completionHelp> - <valueHelp> - <format>enable</format> - <description>Enable MOBIKE</description> - </valueHelp> - <valueHelp> - <format>disable</format> - <description>Disable MOBIKE</description> - </valueHelp> - <constraint> - <regex>(enable|disable)</regex> - </constraint> + <help>Disable MOBIKE Support (IKEv2 only)</help> + <valueless/> </properties> - <defaultValue>enable</defaultValue> </leafNode> <leafNode name="mode"> <properties> @@ -929,23 +888,15 @@ <children> <tagNode name="peer"> <properties> - <help>VPN peer</help> - <valueHelp> - <format>ipv4</format> - <description>IPv4 address of the peer</description> - </valueHelp> - <valueHelp> - <format>ipv6</format> - <description>IPv6 address of the peer</description> - </valueHelp> + <help>Connection name of the peer</help> <valueHelp> <format>txt</format> - <description>Hostname of the peer</description> - </valueHelp> - <valueHelp> - <format><@text></format> - <description>ID of the peer</description> + <description>Connection name of the peer</description> </valueHelp> + <constraint> + <regex>[-_a-zA-Z0-9|@]+</regex> + </constraint> + <constraintErrorMessage>Peer connection name must be alphanumeric and can contain hyphen and underscores</constraintErrorMessage> </properties> <children> #include <include/generic-disable-node.xml.i> @@ -1031,23 +982,10 @@ </leafNode> #include <include/generic-description.xml.i> #include <include/dhcp-interface.xml.i> - <leafNode name="force-encapsulation"> + <leafNode name="force-udp-encapsulation"> <properties> - <help>Force UDP Encapsulation for ESP payloads</help> - <completionHelp> - <list>enable disable</list> - </completionHelp> - <valueHelp> - <format>enable</format> - <description>Force UDP encapsulation</description> - </valueHelp> - <valueHelp> - <format>disable</format> - <description>Do not force UDP encapsulation</description> - </valueHelp> - <constraint> - <regex>(enable|disable)</regex> - </constraint> + <help>Force UDP encapsulation</help> + <valueless/> </properties> </leafNode> #include <include/ipsec/ike-group.xml.i> @@ -1075,6 +1013,7 @@ </properties> </leafNode> #include <include/ipsec/local-address.xml.i> + #include <include/ipsec/remote-address.xml.i> <tagNode name="tunnel"> <properties> <help>Peer tunnel</help> diff --git a/interface-definitions/vpn-openconnect.xml.in b/interface-definitions/vpn-openconnect.xml.in index 522465611..bc7f78e79 100644 --- a/interface-definitions/vpn-openconnect.xml.in +++ b/interface-definitions/vpn-openconnect.xml.in @@ -163,6 +163,10 @@ </node> </children> </node> + #include <include/listen-address-ipv4.xml.i> + <leafNode name="listen-address"> + <defaultValue>0.0.0.0</defaultValue> + </leafNode> <node name="listen-ports"> <properties> <help>Specify custom ports to use for client connections</help> diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index b56caef71..f9b7222fd 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -326,6 +326,10 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name): if 'action' in rule_conf: output.append(nft_action(rule_conf['action'])) + if 'jump' in rule_conf['action']: + target = rule_conf['jump_target'] + output.append(f'NAME{def_suffix}_{target}') + else: output.append('return') diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py index 28b5e2991..9a92c71b8 100644 --- a/python/vyos/ifconfig/wireguard.py +++ b/python/vyos/ifconfig/wireguard.py @@ -167,12 +167,8 @@ class WireGuardIf(Interface): # remove no longer associated peers first if 'peer_remove' in config: - for tmp in config['peer_remove']: - peer = config['peer_remove'][tmp] - peer['ifname'] = config['ifname'] - - cmd = 'wg set {ifname} peer {public_key} remove' - self._cmd(cmd.format(**peer)) + for peer, public_key in config['peer_remove'].items(): + self._cmd(f'wg set {self.ifname} peer {public_key} remove') config['private_key_file'] = '/tmp/tmp.wireguard.key' with open(config['private_key_file'], 'w') as f: @@ -187,44 +183,49 @@ class WireGuardIf(Interface): base_cmd = base_cmd.format(**config) - for tmp in config['peer']: - peer = config['peer'][tmp] - - # start of with a fresh 'wg' command - cmd = base_cmd + ' peer {public_key}' - - # If no PSK is given remove it by using /dev/null - passing keys via - # the shell (usually bash) is considered insecure, thus we use a file - no_psk_file = '/dev/null' - psk_file = no_psk_file - if 'preshared_key' in peer: - psk_file = '/tmp/tmp.wireguard.psk' - with open(psk_file, 'w') as f: - f.write(peer['preshared_key']) - cmd += f' preshared-key {psk_file}' - - # Persistent keepalive is optional - if 'persistent_keepalive'in peer: - cmd += ' persistent-keepalive {persistent_keepalive}' - - # Multiple allowed-ip ranges can be defined - ensure we are always - # dealing with a list - if isinstance(peer['allowed_ips'], str): - peer['allowed_ips'] = [peer['allowed_ips']] - cmd += ' allowed-ips ' + ','.join(peer['allowed_ips']) - - # Endpoint configuration is optional - if {'address', 'port'} <= set(peer): - if is_ipv6(peer['address']): - cmd += ' endpoint [{address}]:{port}' - else: - cmd += ' endpoint {address}:{port}' + if 'peer' in config: + for peer, peer_config in config['peer'].items(): + # T4702: No need to configure this peer when it was explicitly + # marked as disabled - also active sessions are terminated as + # the public key was already removed when entering this method! + if 'disable' in peer_config: + continue + + # start of with a fresh 'wg' command + cmd = base_cmd + ' peer {public_key}' + + # If no PSK is given remove it by using /dev/null - passing keys via + # the shell (usually bash) is considered insecure, thus we use a file + no_psk_file = '/dev/null' + psk_file = no_psk_file + if 'preshared_key' in peer_config: + psk_file = '/tmp/tmp.wireguard.psk' + with open(psk_file, 'w') as f: + f.write(peer_config['preshared_key']) + cmd += f' preshared-key {psk_file}' + + # Persistent keepalive is optional + if 'persistent_keepalive'in peer_config: + cmd += ' persistent-keepalive {persistent_keepalive}' + + # Multiple allowed-ip ranges can be defined - ensure we are always + # dealing with a list + if isinstance(peer_config['allowed_ips'], str): + peer_config['allowed_ips'] = [peer_config['allowed_ips']] + cmd += ' allowed-ips ' + ','.join(peer_config['allowed_ips']) + + # Endpoint configuration is optional + if {'address', 'port'} <= set(peer_config): + if is_ipv6(peer_config['address']): + cmd += ' endpoint [{address}]:{port}' + else: + cmd += ' endpoint {address}:{port}' - self._cmd(cmd.format(**peer)) + self._cmd(cmd.format(**peer_config)) - # PSK key file is not required to be stored persistently as its backed by CLI - if psk_file != no_psk_file and os.path.exists(psk_file): - os.remove(psk_file) + # PSK key file is not required to be stored persistently as its backed by CLI + if psk_file != no_psk_file and os.path.exists(psk_file): + os.remove(psk_file) # call base class super().update(config) diff --git a/python/vyos/template.py b/python/vyos/template.py index 9804308c1..0e79994f5 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -548,7 +548,7 @@ def nft_rule(rule_conf, fw_name, rule_id, ip_name='ip'): return parse_rule(rule_conf, fw_name, rule_id, ip_name) @register_filter('nft_default_rule') -def nft_default_rule(fw_conf, fw_name): +def nft_default_rule(fw_conf, fw_name, ipv6=False): output = ['counter'] default_action = fw_conf['default_action'] @@ -557,6 +557,11 @@ def nft_default_rule(fw_conf, fw_name): output.append(f'log prefix "[{fw_name[:19]}-default-{action_suffix}]"') output.append(nft_action(default_action)) + if 'default_jump_target' in fw_conf: + target = fw_conf['default_jump_target'] + def_suffix = '6' if ipv6 else '' + output.append(f'NAME{def_suffix}_{target}') + output.append(f'comment "{fw_name} default-action {default_action}"') return " ".join(output) @@ -611,6 +616,15 @@ def nft_nested_group(out_list, includes, groups, key): add_includes(name) return out_list +@register_filter('range_to_regex') +def range_to_regex(num_range): + from vyos.range_regex import range_to_regex + if '-' not in num_range: + return num_range + + regex = range_to_regex(num_range) + return f'({regex})' + @register_test('vyos_defined') def vyos_defined(value, test_value=None, var_type=None): """ diff --git a/smoketest/configs/pppoe-server b/smoketest/configs/pppoe-server index 7e4ccc80e..bfbef4a34 100644 --- a/smoketest/configs/pppoe-server +++ b/smoketest/configs/pppoe-server @@ -43,7 +43,13 @@ service { stop 192.168.0.200 } gateway-address 192.168.0.2 + interface eth1 { + } interface eth2 { + vlan-id 10 + vlan-id 20 + vlan-range 30-40 + vlan-range 50-60 } name-server 192.168.0.1 } diff --git a/smoketest/scripts/cli/base_accel_ppp_test.py b/smoketest/scripts/cli/base_accel_ppp_test.py index b2acb03cc..471bdaffb 100644 --- a/smoketest/scripts/cli/base_accel_ppp_test.py +++ b/smoketest/scripts/cli/base_accel_ppp_test.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2022 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 @@ -27,6 +27,17 @@ from vyos.util import process_named_running class BasicAccelPPPTest: class TestCase(VyOSUnitTestSHIM.TestCase): + + @classmethod + def setUpClass(cls): + cls._process_name = 'accel-pppd' + + super(BasicAccelPPPTest.TestCase, cls).setUpClass() + + # ensure we can also run this test on a live system - so lets clean + # out the current configuration :) + cls.cli_delete(cls, cls._base_path) + def setUp(self): self._gateway = '192.0.2.1' # ensure we can also run this test on a live system - so lets clean @@ -34,9 +45,15 @@ class BasicAccelPPPTest: self.cli_delete(self._base_path) def tearDown(self): + # Check for running process + self.assertTrue(process_named_running(self._process_name)) + self.cli_delete(self._base_path) self.cli_commit() + # Check for running process + self.assertFalse(process_named_running(self._process_name)) + def set(self, path): self.cli_set(self._base_path + path) @@ -113,9 +130,6 @@ class BasicAccelPPPTest: tmp = re.findall(regex, tmp) self.assertTrue(tmp) - # Check for running process - self.assertTrue(process_named_running(self._process_name)) - # Check local-users default value(s) self.delete(['authentication', 'local-users', 'username', user, 'static-ip']) # commit changes @@ -127,9 +141,6 @@ class BasicAccelPPPTest: tmp = re.findall(regex, tmp) self.assertTrue(tmp) - # Check for running process - self.assertTrue(process_named_running(self._process_name)) - def test_accel_radius_authentication(self): # Test configuration of RADIUS authentication for PPPoE server self.basic_config() @@ -186,9 +197,6 @@ class BasicAccelPPPTest: self.assertEqual(f'req-limit=0', server[4]) self.assertEqual(f'fail-time=0', server[5]) - # Check for running process - self.assertTrue(process_named_running(self._process_name)) - # # Disable Radius Accounting # diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index 8e4aac788..c54cba027 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -228,6 +228,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): def test_ipv4_advanced(self): name = 'smoketest-adv' + name2 = 'smoketest-adv2' interface = 'eth0' self.cli_set(['firewall', 'name', name, 'default-action', 'drop']) @@ -246,6 +247,13 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'name', name, 'rule', '7', 'dscp', '3-11']) self.cli_set(['firewall', 'name', name, 'rule', '7', 'dscp-exclude', '21-25']) + self.cli_set(['firewall', 'name', name2, 'default-action', 'jump']) + self.cli_set(['firewall', 'name', name2, 'default-jump-target', name]) + self.cli_set(['firewall', 'name', name2, 'enable-default-log']) + self.cli_set(['firewall', 'name', name2, 'rule', '1', 'source', 'address', '198.51.100.1']) + self.cli_set(['firewall', 'name', name2, 'rule', '1', 'action', 'jump']) + self.cli_set(['firewall', 'name', name2, 'rule', '1', 'jump-target', name]) + self.cli_set(['firewall', 'interface', interface, 'in', 'name', name]) self.cli_commit() @@ -254,7 +262,9 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): [f'iifname "{interface}"', f'jump NAME_{name}'], ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', 'return'], ['ip length 1-30000', 'ip length != 60000-65535', 'ip dscp 0x03-0x0b', 'ip dscp != 0x15-0x19', 'return'], - [f'log prefix "[{name}-default-D]"', 'drop'] + [f'log prefix "[{name}-default-D]"', 'drop'], + ['ip saddr 198.51.100.1', f'jump NAME_{name}'], + [f'log prefix "[{name2}-default-J]"', f'jump NAME_{name}'] ] self.verify_nftables(nftables_search, 'ip vyos_filter') @@ -291,6 +301,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): def test_ipv6_advanced(self): name = 'v6-smoketest-adv' + name2 = 'v6-smoketest-adv2' interface = 'eth0' self.cli_set(['firewall', 'ipv6-name', name, 'default-action', 'drop']) @@ -309,6 +320,13 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'dscp', '4-14']) self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'dscp-exclude', '31-35']) + self.cli_set(['firewall', 'ipv6-name', name2, 'default-action', 'jump']) + self.cli_set(['firewall', 'ipv6-name', name2, 'default-jump-target', name]) + self.cli_set(['firewall', 'ipv6-name', name2, 'enable-default-log']) + self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'source', 'address', '2001:db8::/64']) + self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'action', 'jump']) + self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'jump-target', name]) + self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name]) self.cli_commit() @@ -317,7 +335,9 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): [f'iifname "{interface}"', f'jump NAME6_{name}'], ['ip6 length { 65, 513, 1025 }', 'ip6 dscp { af21, 0x35 }', 'return'], ['ip6 length 1-1999', 'ip6 length != 60000-65535', 'ip6 dscp 0x04-0x0e', 'ip6 dscp != 0x1f-0x23', 'return'], - [f'log prefix "[{name}-default-D]"', 'drop'] + [f'log prefix "[{name}-default-D]"', 'drop'], + ['ip6 saddr 2001:db8::/64', f'jump NAME6_{name}'], + [f'log prefix "[{name2}-default-J]"', f'jump NAME6_{name}'] ] self.verify_nftables(nftables_search, 'ip6 vyos_filter') diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py index 51cc098ef..17687a26b 100755 --- a/smoketest/scripts/cli/test_service_pppoe-server.py +++ b/smoketest/scripts/cli/test_service_pppoe-server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 020 VyOS maintainers and contributors +# Copyright (C) 2022 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 @@ -14,27 +14,28 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import unittest from base_accel_ppp_test import BasicAccelPPPTest from configparser import ConfigParser from vyos.configsession import ConfigSessionError -from vyos.util import process_named_running +from vyos.util import read_file +from vyos.template import range_to_regex local_if = ['interfaces', 'dummy', 'dum667'] ac_name = 'ACN' interface = 'eth0' class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): - def setUp(self): - self._base_path = ['service', 'pppoe-server'] - self._process_name = 'accel-pppd' - self._config_file = '/run/accel-pppd/pppoe.conf' - self._chap_secrets = '/run/accel-pppd/pppoe.chap-secrets' + @classmethod + def setUpClass(cls): + cls._base_path = ['service', 'pppoe-server'] + cls._config_file = '/run/accel-pppd/pppoe.conf' + cls._chap_secrets = '/run/accel-pppd/pppoe.chap-secrets' - super().setUp() + # call base-classes classmethod + super(TestServicePPPoEServer, cls).setUpClass() def tearDown(self): self.cli_delete(local_if) @@ -120,8 +121,6 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): # check interface-cache self.assertEqual(conf['ppp']['unit-cache'], interface_cache) - # Check for running process - self.assertTrue(process_named_running(self._process_name)) def test_pppoe_server_authentication_protocols(self): # Test configuration of local authentication for PPPoE server @@ -139,8 +138,6 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): self.assertEqual(conf['modules']['auth_mschap_v2'], None) - # Check for running process - self.assertTrue(process_named_running(self._process_name)) def test_pppoe_server_client_ip_pool(self): # Test configuration of IPv6 client pools @@ -168,9 +165,6 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): self.assertEqual(conf['ip-pool'][start_stop], None) self.assertEqual(conf['ip-pool']['gw-ip-address'], self._gateway) - # Check for running process - self.assertTrue(process_named_running(self._process_name)) - def test_pppoe_server_client_ipv6_pool(self): # Test configuration of IPv6 client pools @@ -211,9 +205,6 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): self.assertEqual(conf['ipv6-pool'][client_prefix], None) self.assertEqual(conf['ipv6-pool']['delegate'], f'{delegate_prefix},{delegate_mask}') - # Check for running process - self.assertTrue(process_named_running(self._process_name)) - def test_accel_radius_authentication(self): radius_called_sid = 'ifname:mac' @@ -234,5 +225,27 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): self.assertEqual(conf['radius']['acct-interim-jitter'], radius_acct_interim_jitter) + def test_pppoe_server_vlan(self): + + vlans = ['100', '200', '300-310'] + + # Test configuration of local authentication for PPPoE server + self.basic_config() + + for vlan in vlans: + self.set(['interface', interface, 'vlan', vlan]) + + # commit changes + self.cli_commit() + + # Validate configuration values + config = read_file(self._config_file) + for vlan in vlans: + tmp = range_to_regex(vlan) + self.assertIn(f'interface=re:^{interface}\.{tmp}$', config) + + tmp = ','.join(vlans) + self.assertIn(f'vlan-mon={interface},{tmp}', config) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py index 8a6514d57..bd242104f 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 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 @@ -33,6 +33,7 @@ dhcp_waiting_file = '/tmp/ipsec_dhcp_waiting' swanctl_file = '/etc/swanctl/swanctl.conf' peer_ip = '203.0.113.45' +connection_name = 'main-branch' interface = 'eth1' vif = '100' esp_group = 'MyESPGroup' @@ -150,7 +151,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_set(ethernet_path + [interface, 'vif', vif, 'address', 'dhcp']) # Use VLAN to avoid getting IP from qemu dhcp server # Site to site - peer_base_path = base_path + ['site-to-site', 'peer', peer_ip] + peer_base_path = base_path + ['site-to-site', 'peer', connection_name] self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret']) self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret]) self.cli_set(peer_base_path + ['ike-group', ike_group]) @@ -173,7 +174,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): priority = '20' life_bytes = '100000' life_packets = '2000000' - peer_base_path = base_path + ['site-to-site', 'peer', peer_ip] + peer_base_path = base_path + ['site-to-site', 'peer', connection_name] self.cli_set(base_path + ['esp-group', esp_group, 'life-bytes', life_bytes]) self.cli_set(base_path + ['esp-group', esp_group, 'life-packets', life_packets]) @@ -183,6 +184,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_set(peer_base_path + ['ike-group', ike_group]) self.cli_set(peer_base_path + ['default-esp-group', esp_group]) self.cli_set(peer_base_path + ['local-address', local_address]) + self.cli_set(peer_base_path + ['remote-address', peer_ip]) self.cli_set(peer_base_path + ['tunnel', '1', 'protocol', 'tcp']) self.cli_set(peer_base_path + ['tunnel', '1', 'local', 'prefix', '172.16.10.0/24']) self.cli_set(peer_base_path + ['tunnel', '1', 'local', 'prefix', '172.16.11.0/24']) @@ -211,11 +213,11 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): f'local_addrs = {local_address} # dhcp:no', f'remote_addrs = {peer_ip}', f'mode = tunnel', - f'peer_{peer_ip.replace(".","-")}_tunnel_1', + f'{connection_name}-tunnel-1', f'local_ts = 172.16.10.0/24[tcp/443],172.16.11.0/24[tcp/443]', f'remote_ts = 172.17.10.0/24[tcp/443],172.17.11.0/24[tcp/443]', f'mode = tunnel', - f'peer_{peer_ip.replace(".","-")}_tunnel_2', + f'{connection_name}-tunnel-2', f'local_ts = 10.1.0.0/16', f'remote_ts = 10.2.0.0/16', f'priority = {priority}', @@ -226,7 +228,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): swanctl_secrets_lines = [ f'id-local = {local_address} # dhcp:no', - f'id-remote = {peer_ip}', + f'id-remote_{peer_ip.replace(".","-")} = {peer_ip}', f'secret = "{secret}"' ] for line in swanctl_secrets_lines: @@ -236,18 +238,24 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): def test_03_site_to_site_vti(self): local_address = '192.0.2.10' vti = 'vti10' + # IKE + self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2']) + self.cli_set(base_path + ['ike-group', ike_group, 'disable-mobike']) + # ESP + self.cli_set(base_path + ['esp-group', esp_group, 'compression']) # VTI interface self.cli_set(vti_path + [vti, 'address', '10.1.1.1/24']) - self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2']) # Site to site - peer_base_path = base_path + ['site-to-site', 'peer', peer_ip] + peer_base_path = base_path + ['site-to-site', 'peer', connection_name] self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret']) self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret]) self.cli_set(peer_base_path + ['connection-type', 'none']) + self.cli_set(peer_base_path + ['force-udp-encapsulation']) self.cli_set(peer_base_path + ['ike-group', ike_group]) self.cli_set(peer_base_path + ['default-esp-group', esp_group]) self.cli_set(peer_base_path + ['local-address', local_address]) + self.cli_set(peer_base_path + ['remote-address', peer_ip]) self.cli_set(peer_base_path + ['tunnel', '1', 'local', 'prefix', '172.16.10.0/24']) self.cli_set(peer_base_path + ['tunnel', '1', 'local', 'prefix', '172.16.11.0/24']) self.cli_set(peer_base_path + ['tunnel', '1', 'remote', 'prefix', '172.17.10.0/24']) @@ -269,10 +277,12 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): f'proposals = aes128-sha1-modp1024', f'esp_proposals = aes128-sha1-modp1024', f'local_addrs = {local_address} # dhcp:no', + f'mobike = no', f'remote_addrs = {peer_ip}', f'mode = tunnel', f'local_ts = 172.16.10.0/24,172.16.11.0/24', f'remote_ts = 172.17.10.0/24,172.17.11.0/24', + f'ipcomp = yes', f'start_action = none', f'if_id_in = {if_id}', # will be 11 for vti10 - shifted by one f'if_id_out = {if_id}', @@ -283,7 +293,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): swanctl_secrets_lines = [ f'id-local = {local_address} # dhcp:no', - f'id-remote = {peer_ip}', + f'id-remote_{peer_ip.replace(".","-")} = {peer_ip}', f'secret = "{secret}"' ] for line in swanctl_secrets_lines: @@ -311,7 +321,6 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_set(nhrp_path + ['tunnel', tunnel_if, 'shortcut']) # IKE/ESP Groups - self.cli_set(base_path + ['esp-group', esp_group, 'compression', 'disable']) self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', esp_lifetime]) self.cli_set(base_path + ['esp-group', esp_group, 'mode', 'transport']) self.cli_set(base_path + ['esp-group', esp_group, 'pfs', 'dh-group2']) @@ -320,7 +329,6 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'encryption', '3des']) self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'hash', 'md5']) - self.cli_set(base_path + ['ike-group', ike_group, 'ikev2-reauth', 'no']) self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev1']) self.cli_set(base_path + ['ike-group', ike_group, 'lifetime', ike_lifetime]) self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'dh-group', '2']) @@ -366,10 +374,11 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_set(vti_path + [vti, 'address', '192.168.0.1/31']) peer_ip = '172.18.254.202' + connection_name = 'office' local_address = '172.18.254.201' - peer_base_path = base_path + ['site-to-site', 'peer', peer_ip] + peer_base_path = base_path + ['site-to-site', 'peer', connection_name] - self.cli_set(peer_base_path + ['authentication', 'id', peer_name]) + self.cli_set(peer_base_path + ['authentication', 'local-id', peer_name]) self.cli_set(peer_base_path + ['authentication', 'mode', 'x509']) self.cli_set(peer_base_path + ['authentication', 'remote-id', 'peer2']) self.cli_set(peer_base_path + ['authentication', 'x509', 'ca-certificate', ca_name]) @@ -378,6 +387,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_set(peer_base_path + ['ike-group', ike_group]) self.cli_set(peer_base_path + ['ikev2-reauth', 'inherit']) self.cli_set(peer_base_path + ['local-address', local_address]) + self.cli_set(peer_base_path + ['remote-address', peer_ip]) self.cli_set(peer_base_path + ['vti', 'bind', vti]) self.cli_set(peer_base_path + ['vti', 'esp-group', esp_group]) @@ -391,7 +401,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): # to also support a vti0 interface if_id = str(int(if_id) +1) swanctl_lines = [ - f'peer_{tmp}', + f'{connection_name}', f'version = 0', # key-exchange not set - defaulting to 0 for ikev1 and ikev2 f'send_cert = always', f'mobike = yes', @@ -416,7 +426,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.assertIn(line, swanctl_conf) swanctl_secrets_lines = [ - f'peer_{tmp}', + f'{connection_name}', f'file = {peer_name}.pem', ] for line in swanctl_secrets_lines: @@ -430,7 +440,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): local_address = '192.0.2.5' local_id = 'vyos-r1' remote_id = 'vyos-r2' - peer_base_path = base_path + ['site-to-site', 'peer', peer_ip] + peer_base_path = base_path + ['site-to-site', 'peer', connection_name] self.cli_set(tunnel_path + ['tun1', 'encapsulation', 'gre']) self.cli_set(tunnel_path + ['tun1', 'source-address', local_address]) @@ -438,10 +448,9 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['interface', interface]) self.cli_set(base_path + ['options', 'flexvpn']) self.cli_set(base_path + ['options', 'interface', 'tun1']) - self.cli_set(base_path + ['ike-group', ike_group, 'ikev2-reauth', 'no']) self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2']) - self.cli_set(peer_base_path + ['authentication', 'id', local_id]) + self.cli_set(peer_base_path + ['authentication', 'local-id', local_id]) self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret']) self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret]) self.cli_set(peer_base_path + ['authentication', 'remote-id', remote_id]) @@ -449,6 +458,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_set(peer_base_path + ['ike-group', ike_group]) self.cli_set(peer_base_path + ['default-esp-group', esp_group]) self.cli_set(peer_base_path + ['local-address', local_address]) + self.cli_set(peer_base_path + ['remote-address', peer_ip]) self.cli_set(peer_base_path + ['tunnel', '1', 'protocol', 'gre']) self.cli_set(peer_base_path + ['virtual-address', '203.0.113.55']) @@ -464,7 +474,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): f'life_time = 3600s', # default value f'local_addrs = {local_address} # dhcp:no', f'remote_addrs = {peer_ip}', - f'peer_{peer_ip.replace(".","-")}_tunnel_1', + f'{connection_name}-tunnel-1', f'mode = tunnel', ] @@ -473,7 +483,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): swanctl_secrets_lines = [ f'id-local = {local_address} # dhcp:no', - f'id-remote = {peer_ip}', + f'id-remote_{peer_ip.replace(".","-")} = {peer_ip}', f'id-localid = {local_id}', f'id-remoteid = {remote_id}', f'secret = "{secret}"', diff --git a/smoketest/scripts/cli/test_vpn_sstp.py b/smoketest/scripts/cli/test_vpn_sstp.py index f58920b5b..434e3aa05 100755 --- a/smoketest/scripts/cli/test_vpn_sstp.py +++ b/smoketest/scripts/cli/test_vpn_sstp.py @@ -19,29 +19,49 @@ import unittest from base_accel_ppp_test import BasicAccelPPPTest from vyos.util import read_file - pki_path = ['pki'] -cert_data = 'MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIwWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIxMDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu+JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3LftzngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93+dm/LDnp7C0=' -key_data = 'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww' + +cert_data = """ +MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIw +WTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv +bWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIx +MDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNV +BAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlP +UzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3 +QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu ++JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3Lftz +ngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93 ++dm/LDnp7C0=""" + +key_data = """ +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx +2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7 +u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww +""" class TestVPNSSTPServer(BasicAccelPPPTest.TestCase): - def setUp(self): - self._base_path = ['vpn', 'sstp'] - self._process_name = 'accel-pppd' - self._config_file = '/run/accel-pppd/sstp.conf' - self._chap_secrets = '/run/accel-pppd/sstp.chap-secrets' - super().setUp() + @classmethod + def setUpClass(cls): + cls._base_path = ['vpn', 'sstp'] + cls._config_file = '/run/accel-pppd/sstp.conf' + cls._chap_secrets = '/run/accel-pppd/sstp.chap-secrets' - def tearDown(self): - self.cli_delete(pki_path) - super().tearDown() + # call base-classes classmethod + super(TestVPNSSTPServer, cls).setUpClass() - def basic_config(self): - self.cli_delete(pki_path) - self.cli_set(pki_path + ['ca', 'sstp', 'certificate', cert_data]) - self.cli_set(pki_path + ['certificate', 'sstp', 'certificate', cert_data]) - self.cli_set(pki_path + ['certificate', 'sstp', 'private', 'key', key_data]) + cls.cli_set(cls, pki_path + ['ca', 'sstp', 'certificate', cert_data.replace('\n','')]) + cls.cli_set(cls, pki_path + ['certificate', 'sstp', 'certificate', cert_data.replace('\n','')]) + cls.cli_set(cls, pki_path + ['certificate', 'sstp', 'private', 'key', key_data.replace('\n','')]) + @classmethod + def tearDownClass(cls): + cls.cli_delete(cls, pki_path) + + super(TestVPNSSTPServer, cls).tearDownClass() + + def basic_config(self): # SSL is mandatory self.set(['ssl', 'ca-certificate', 'sstp']) self.set(['ssl', 'certificate', 'sstp']) diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index eeb57bd30..cbd9cbe90 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -179,6 +179,20 @@ def verify_rule(firewall, rule_conf, ipv6): if 'action' not in rule_conf: raise ConfigError('Rule action must be defined') + if 'jump' in rule_conf['action'] and 'jump_target' not in rule_conf: + raise ConfigError('Action set to jump, but no jump-target specified') + + if 'jump_target' in rule_conf: + if 'jump' not in rule_conf['action']: + raise ConfigError('jump-target defined, but action jump needed and it is not defined') + target = rule_conf['jump_target'] + if not ipv6: + if target not in dict_search_args(firewall, 'name'): + raise ConfigError(f'Invalid jump-target. Firewall name {target} does not exist on the system') + else: + if target not in dict_search_args(firewall, 'ipv6_name'): + raise ConfigError(f'Invalid jump-target. Firewall ipv6-name {target} does not exist on the system') + if 'fragment' in rule_conf: if {'match_frag', 'match_non_frag'} <= set(rule_conf['fragment']): raise ConfigError('Cannot specify both "match-frag" and "match-non-frag"') @@ -287,6 +301,18 @@ def verify(firewall): for name in ['name', 'ipv6_name']: if name in firewall: for name_id, name_conf in firewall[name].items(): + if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf: + raise ConfigError('default-action set to jump, but no default-jump-target specified') + if 'default_jump_target' in name_conf: + target = name_conf['default_jump_target'] + if 'jump' not in name_conf['default_action']: + raise ConfigError('default-jump-target defined,but default-action jump needed and it is not defined') + if name_conf['default_jump_target'] == name_id: + raise ConfigError(f'Loop detected on default-jump-target.') + ## Now need to check that default-jump-target exists (other firewall chain/name) + if target not in dict_search_args(firewall, name): + raise ConfigError(f'Invalid jump-target. Firewall {name} {target} does not exist on the system') + if 'rule' in name_conf: for rule_id, rule_conf in name_conf['rule'].items(): verify_rule(firewall, rule_conf, name == 'ipv6_name') diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index 61bab2feb..8d738f55e 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -14,16 +14,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os - from sys import exit -from copy import deepcopy from vyos.config import Config from vyos.configdict import dict_merge from vyos.configdict import get_interface_dict -from vyos.configdict import node_changed -from vyos.configdict import leaf_node_changed +from vyos.configdict import is_node_changed from vyos.configverify import verify_vrf from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete @@ -50,17 +46,20 @@ def get_config(config=None): ifname, wireguard = get_interface_dict(conf, base) # Check if a port was changed - wireguard['port_changed'] = leaf_node_changed(conf, base + [ifname, 'port']) + tmp = is_node_changed(conf, base + [ifname, 'port']) + if tmp: wireguard['port_changed'] = {} # Determine which Wireguard peer has been removed. # Peers can only be removed with their public key! - dict = {} - tmp = node_changed(conf, base + [ifname, 'peer'], key_mangling=('-', '_')) - for peer in (tmp or []): - public_key = leaf_node_changed(conf, base + [ifname, 'peer', peer, 'public_key']) - if public_key: - dict = dict_merge({'peer_remove' : {peer : {'public_key' : public_key[0]}}}, dict) - wireguard.update(dict) + if 'peer' in wireguard: + peer_remove = {} + for peer, peer_config in wireguard['peer'].items(): + # T4702: If anything on a peer changes we remove the peer first and re-add it + if is_node_changed(conf, base + [ifname, 'peer', peer]): + if 'public_key' in peer_config: + peer_remove = dict_merge({'peer_remove' : {peer : peer_config['public_key']}}, peer_remove) + if peer_remove: + wireguard.update(peer_remove) return wireguard @@ -81,12 +80,11 @@ def verify(wireguard): if 'peer' not in wireguard: raise ConfigError('At least one Wireguard peer is required!') - if 'port' in wireguard and wireguard['port_changed']: + if 'port' in wireguard and 'port_changed' in wireguard: listen_port = int(wireguard['port']) if check_port_availability('0.0.0.0', listen_port, 'udp') is not True: - raise ConfigError( - f'The UDP port {listen_port} is busy or unavailable and cannot be used for the interface' - ) + raise ConfigError(f'UDP port {listen_port} is busy or unavailable and ' + 'cannot be used for the interface!') # run checks on individual configured WireGuard peer for tmp in wireguard['peer']: diff --git a/src/conf_mode/policy-route.py b/src/conf_mode/policy-route.py index 9fddbd2c6..00539b9c7 100755 --- a/src/conf_mode/policy-route.py +++ b/src/conf_mode/policy-route.py @@ -92,7 +92,7 @@ def get_config(config=None): return policy -def verify_rule(policy, name, rule_conf, ipv6): +def verify_rule(policy, name, rule_conf, ipv6, rule_id): icmp = 'icmp' if not ipv6 else 'icmpv6' if icmp in rule_conf: icmp_defined = False @@ -166,7 +166,7 @@ def verify(policy): for name, pol_conf in policy[route].items(): if 'rule' in pol_conf: for rule_id, rule_conf in pol_conf['rule'].items(): - verify_rule(policy, name, rule_conf, ipv6) + verify_rule(policy, name, rule_conf, ipv6, rule_id) for ifname, if_policy in policy['interfaces'].items(): name = dict_search_args(if_policy, 'route') diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py index 6086ef859..dfe73094f 100755 --- a/src/conf_mode/service_pppoe-server.py +++ b/src/conf_mode/service_pppoe-server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2022 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 @@ -21,13 +21,12 @@ from sys import exit from vyos.config import Config from vyos.configdict import get_accel_dict from vyos.configverify import verify_accel_ppp_base_service +from vyos.configverify import verify_interface_exists from vyos.template import render from vyos.util import call from vyos.util import dict_search -from vyos.util import get_interface_config from vyos import ConfigError from vyos import airbag -from vyos.range_regex import range_to_regex airbag.enable() @@ -54,15 +53,14 @@ def verify(pppoe): verify_accel_ppp_base_service(pppoe) if 'wins_server' in pppoe and len(pppoe['wins_server']) > 2: - raise ConfigError('Not more then two IPv4 WINS name-servers can be configured') + raise ConfigError('Not more then two WINS name-servers can be configured') if 'interface' not in pppoe: raise ConfigError('At least one listen interface must be defined!') # Check is interface exists in the system - for iface in pppoe['interface']: - if not get_interface_config(iface): - raise ConfigError(f'Interface {iface} does not exist!') + for interface in pppoe['interface']: + verify_interface_exists(interface) # local ippool and gateway settings config checks if not (dict_search('client_ip_pool.subnet', pppoe) or @@ -81,13 +79,6 @@ def generate(pppoe): if not pppoe: return None - # Generate special regex for dynamic interfaces - for iface in pppoe['interface']: - if 'vlan_range' in pppoe['interface'][iface]: - pppoe['interface'][iface]['regex'] = [] - for vlan_range in pppoe['interface'][iface]['vlan_range']: - pppoe['interface'][iface]['regex'].append(range_to_regex(vlan_range)) - render(pppoe_conf, 'accel-ppp/pppoe.config.j2', pppoe) if dict_search('authentication.mode', pppoe) == 'local': @@ -101,15 +92,15 @@ def generate(pppoe): def apply(pppoe): + systemd_service = 'accel-ppp@pppoe.service' if not pppoe: - call('systemctl stop accel-ppp@pppoe.service') + call(f'systemctl stop {systemd_service}') for file in [pppoe_conf, pppoe_chap_secrets]: if os.path.exists(file): os.unlink(file) - return None - call('systemctl restart accel-ppp@pppoe.service') + call(f'systemctl reload-or-restart {systemd_service}') if __name__ == '__main__': try: diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index 5ca32d23e..c9061366d 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 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 @@ -16,6 +16,7 @@ import ipaddress import os +import re from sys import exit from time import sleep @@ -348,6 +349,14 @@ def verify(ipsec): if 'site_to_site' in ipsec and 'peer' in ipsec['site_to_site']: for peer, peer_conf in ipsec['site_to_site']['peer'].items(): has_default_esp = False + # Peer name it is swanctl connection name and shouldn't contain dots or colons, T4118 + if bool(re.search(':|\.', peer)): + raise ConfigError(f'Incorrect peer name "{peer}" ' + f'Peer name can contain alpha-numeric letters, hyphen and underscore') + + if 'remote_address' not in peer_conf: + print(f'You should set correct remote-address "peer {peer} remote-address x.x.x.x"\n') + if 'default_esp_group' in peer_conf: has_default_esp = True if 'esp_group' not in ipsec or peer_conf['default_esp_group'] not in ipsec['esp_group']: diff --git a/src/conf_mode/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py index 23b1baf4d..c050b796b 100755 --- a/src/conf_mode/vpn_openconnect.py +++ b/src/conf_mode/vpn_openconnect.py @@ -81,9 +81,10 @@ def verify(ocserv): # Check if listen-ports not binded other services # It can be only listen by 'ocserv-main' for proto, port in ocserv.get('listen_ports').items(): - if check_port_availability('0.0.0.0', int(port), proto) is not True and \ + if check_port_availability(ocserv['listen_address'], int(port), proto) is not True and \ not is_listen_port_bind_service(int(port), 'ocserv-main'): raise ConfigError(f'"{proto}" port "{port}" is used by another service') + # Check authentication if "authentication" in ocserv: if "mode" in ocserv["authentication"]: diff --git a/src/migration-scripts/ipsec/9-to-10 b/src/migration-scripts/ipsec/9-to-10 new file mode 100755 index 000000000..98e0aede4 --- /dev/null +++ b/src/migration-scripts/ipsec/9-to-10 @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import re + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree +from vyos.template import is_ipv4 +from vyos.template import is_ipv6 + + +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 = ['vpn', 'ipsec'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +# IKE changes, T4118: +if config.exists(base + ['ike-group']): + for ike_group in config.list_nodes(base + ['ike-group']): + # replace 'ipsec ike-group <tag> mobike disable' + # => 'ipsec ike-group <tag> disable-mobike' + mobike = base + ['ike-group', ike_group, 'mobike'] + if config.exists(mobike): + if config.return_value(mobike) == 'disable': + config.set(base + ['ike-group', ike_group, 'disable-mobike']) + config.delete(mobike) + + # replace 'ipsec ike-group <tag> ikev2-reauth yes' + # => 'ipsec ike-group <tag> ikev2-reauth' + reauth = base + ['ike-group', ike_group, 'ikev2-reauth'] + if config.exists(reauth): + if config.return_value(reauth) == 'yes': + config.delete(reauth) + config.set(reauth) + else: + config.delete(reauth) + +# ESP changes +# replace 'ipsec esp-group <tag> compression enable' +# => 'ipsec esp-group <tag> compression' +if config.exists(base + ['esp-group']): + for esp_group in config.list_nodes(base + ['esp-group']): + compression = base + ['esp-group', esp_group, 'compression'] + if config.exists(compression): + if config.return_value(compression) == 'enable': + config.delete(compression) + config.set(compression) + else: + config.delete(compression) + +# PEER changes +if config.exists(base + ['site-to-site', 'peer']): + for peer in config.list_nodes(base + ['site-to-site', 'peer']): + # replace: 'peer <tag> id x' + # => 'peer <tag> local-id x' + if config.exists(base + ['site-to-site', 'peer', peer, 'authentication', 'id']): + config.rename(base + ['site-to-site', 'peer', peer, 'authentication', 'id'], 'local-id') + + # For the peer '@foo' set remote-id 'foo' if remote-id is not defined + if peer.startswith('@'): + if not config.exists(base + ['site-to-site', 'peer', peer, 'authentication', 'remote-id']): + tmp = peer.replace('@', '') + config.set(base + ['site-to-site', 'peer', peer, 'authentication', 'remote-id'], value=tmp) + + # replace: 'peer <tag> force-encapsulation enable' + # => 'peer <tag> force-udp-encapsulation' + force_enc = base + ['site-to-site', 'peer', peer, 'force-encapsulation'] + if config.exists(force_enc): + if config.return_value(force_enc) == 'enable': + config.delete(force_enc) + config.set(base + ['site-to-site', 'peer', peer, 'force-udp-encapsulation']) + else: + config.delete(force_enc) + + # add option: 'peer <tag> remote-address x.x.x.x' + remote_address = peer + if peer.startswith('@'): + remote_address = 'any' + config.set(base + ['site-to-site', 'peer', peer, 'remote-address', remote_address]) + # Peer name it is swanctl connection name and shouldn't contain dots or colons + # rename peer: + # peer 192.0.2.1 => peer peer_192-0-2-1 + # peer 2001:db8::2 => peer peer_2001-db8--2 + # peer @foo => peer peer_foo + re_peer_name = re.sub(':|\.', '-', peer) + if re_peer_name.startswith('@'): + re_peer_name = re.sub('@', '', re_peer_name) + new_peer_name = f'peer_{re_peer_name}' + + config.rename(base + ['site-to-site', 'peer', peer], new_peer_name) + +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) diff --git a/src/migration-scripts/pppoe-server/5-to-6 b/src/migration-scripts/pppoe-server/5-to-6 new file mode 100755 index 000000000..e4888f4db --- /dev/null +++ b/src/migration-scripts/pppoe-server/5-to-6 @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# - T4703: merge vlan-id and vlan-range to vlan CLI node + +from vyos.configtree import ConfigTree +from sys import argv +from sys import exit + +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_path = ['service', 'pppoe-server', 'interface'] +if not config.exists(base_path): + # Nothing to do + exit(0) + +for interface in config.list_nodes(base_path): + for vlan in ['vlan-id', 'vlan-range']: + if config.exists(base_path + [interface, vlan]): + print(interface, vlan) + for tmp in config.return_values(base_path + [interface, vlan]): + config.set(base_path + [interface, 'vlan'], value=tmp, replace=False) + config.delete(base_path + [interface, vlan]) + +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) + diff --git a/src/validators/range b/src/validators/range deleted file mode 100755 index d4c25f3c4..000000000 --- a/src/validators/range +++ /dev/null @@ -1,56 +0,0 @@ -#!/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 <http://www.gnu.org/licenses/>. - -import re -import sys -import argparse - -class MalformedRange(Exception): - pass - -def validate_range(value, min=None, max=None): - try: - lower, upper = re.match(r'^(\d+)-(\d+)$', value).groups() - - lower, upper = int(lower), int(upper) - - if int(lower) > int(upper): - raise MalformedRange("the lower bound exceeds the upper bound".format(value)) - - if min is not None: - if lower < min: - raise MalformedRange("the lower bound must not be less than {}".format(min)) - - if max is not None: - if upper > max: - raise MalformedRange("the upper bound must not be greater than {}".format(max)) - - except (AttributeError, ValueError): - raise MalformedRange("range syntax error") - -parser = argparse.ArgumentParser(description='Range validator.') -parser.add_argument('--min', type=int, action='store') -parser.add_argument('--max', type=int, action='store') -parser.add_argument('value', action='store') - -if __name__ == '__main__': - args = parser.parse_args() - - try: - validate_range(args.value, min=args.min, max=args.max) - except MalformedRange as e: - print("Incorrect range '{}': {}".format(args.value, e)) - sys.exit(1) |