diff options
110 files changed, 3031 insertions, 702 deletions
diff --git a/data/config-mode-dependencies/vyos-1x.json b/data/config-mode-dependencies/vyos-1x.json index b0586e0bb..afe3dd838 100644 --- a/data/config-mode-dependencies/vyos-1x.json +++ b/data/config-mode-dependencies/vyos-1x.json @@ -1,6 +1,7 @@ { "system_conntrack": { - "conntrack_sync": ["service_conntrack-sync"] + "conntrack_sync": ["service_conntrack-sync"], + "vrf": ["vrf"] }, "firewall": { "conntrack": ["system_conntrack"], @@ -30,6 +31,9 @@ "rpki": ["protocols_rpki"], "sstp": ["vpn_sstp"] }, + "vpn_ipsec": { + "nhrp": ["protocols_nhrp"] + }, "vpn_l2tp": { "ipsec": ["vpn_ipsec"] }, diff --git a/data/templates/conntrack/nftables-ct.j2 b/data/templates/conntrack/nftables-ct.j2 index 762a6f693..c753e6bcb 100644 --- a/data/templates/conntrack/nftables-ct.j2 +++ b/data/templates/conntrack/nftables-ct.j2 @@ -40,9 +40,6 @@ table ip vyos_conntrack { chain PREROUTING { type filter hook prerouting priority -300; policy accept; -{% if ipv4_firewall_action == 'accept' or ipv4_nat_action == 'accept' %} - counter jump VYOS_CT_HELPER -{% endif %} counter jump VYOS_CT_IGNORE counter jump VYOS_CT_TIMEOUT counter jump FW_CONNTRACK @@ -51,11 +48,15 @@ table ip vyos_conntrack { notrack } - chain OUTPUT { - type filter hook output priority -300; policy accept; {% if ipv4_firewall_action == 'accept' or ipv4_nat_action == 'accept' %} + chain PREROUTING_HELPER { + type filter hook prerouting priority -5; policy accept; counter jump VYOS_CT_HELPER + } {% endif %} + + chain OUTPUT { + type filter hook output priority -300; policy accept; counter jump VYOS_CT_IGNORE counter jump VYOS_CT_TIMEOUT counter jump FW_CONNTRACK @@ -66,6 +67,13 @@ table ip vyos_conntrack { notrack } +{% if ipv4_firewall_action == 'accept' or ipv4_nat_action == 'accept' %} + chain OUTPUT_HELPER { + type filter hook output priority -5; policy accept; + counter jump VYOS_CT_HELPER + } +{% endif %} + {{ helper_tmpl.conntrack_helpers(module_map, modules, ipv4=True) }} chain FW_CONNTRACK { @@ -122,9 +130,6 @@ table ip6 vyos_conntrack { chain PREROUTING { type filter hook prerouting priority -300; policy accept; -{% if ipv6_firewall_action == 'accept' or ipv6_nat_action == 'accept' %} - counter jump VYOS_CT_HELPER -{% endif %} counter jump VYOS_CT_IGNORE counter jump VYOS_CT_TIMEOUT counter jump FW_CONNTRACK @@ -132,11 +137,15 @@ table ip6 vyos_conntrack { notrack } - chain OUTPUT { - type filter hook output priority -300; policy accept; {% if ipv6_firewall_action == 'accept' or ipv6_nat_action == 'accept' %} + chain PREROUTING_HELPER { + type filter hook prerouting priority -5; policy accept; counter jump VYOS_CT_HELPER + } {% endif %} + + chain OUTPUT { + type filter hook output priority -300; policy accept; counter jump VYOS_CT_IGNORE counter jump VYOS_CT_TIMEOUT counter jump FW_CONNTRACK @@ -144,6 +153,13 @@ table ip6 vyos_conntrack { notrack } +{% if ipv6_firewall_action == 'accept' or ipv6_nat_action == 'accept' %} + chain OUTPUT_HELPER { + type filter hook output priority -5; policy accept; + counter jump VYOS_CT_HELPER + } +{% endif %} + {{ helper_tmpl.conntrack_helpers(module_map, modules, ipv4=False) }} chain FW_CONNTRACK { diff --git a/data/templates/conntrack/nftables-helpers.j2 b/data/templates/conntrack/nftables-helpers.j2 index 433931162..63a0cc855 100644 --- a/data/templates/conntrack/nftables-helpers.j2 +++ b/data/templates/conntrack/nftables-helpers.j2 @@ -31,6 +31,12 @@ } {% endif %} +{% if modules.rtsp is vyos_defined and ipv4 %} + ct helper rtsp_tcp { + type "rtsp" protocol tcp; + } +{% endif %} + {% if modules.sip is vyos_defined %} ct helper sip_tcp { type "sip" protocol tcp; diff --git a/data/templates/conntrackd/conntrackd.conf.j2 b/data/templates/conntrackd/conntrackd.conf.j2 index 8f56c8171..669b20877 100644 --- a/data/templates/conntrackd/conntrackd.conf.j2 +++ b/data/templates/conntrackd/conntrackd.conf.j2 @@ -76,7 +76,7 @@ General { HashSize {{ hash_size }} HashLimit {{ table_size | int *2 }} LogFile off - Syslog on + Syslog {{ 'off' if disable_syslog is vyos_defined else 'on' }} LockFile /var/lock/conntrack.lock UNIX { Path /var/run/conntrackd.ctl diff --git a/data/templates/frr/ospf6d.frr.j2 b/data/templates/frr/ospf6d.frr.j2 index b0b5663dd..5f758f9e5 100644 --- a/data/templates/frr/ospf6d.frr.j2 +++ b/data/templates/frr/ospf6d.frr.j2 @@ -109,7 +109,7 @@ router ospf6 {{ 'vrf ' ~ vrf if vrf is vyos_defined }} {% endif %} {% if redistribute is vyos_defined %} {% for protocol, options in redistribute.items() %} - redistribute {{ protocol }} {{ 'route-map ' ~ options.route_map if options.route_map is vyos_defined }} + redistribute {{ protocol }} {{ 'metric ' ~ options.metric if options.metric is vyos_defined }} {{ 'metric-type ' ~ options.metric_type if options.metric_type is vyos_defined }} {{ 'route-map ' ~ options.route_map if options.route_map is vyos_defined }} {% endfor %} {% endif %} exit diff --git a/data/templates/frr/ospfd.frr.j2 b/data/templates/frr/ospfd.frr.j2 index 040628e82..ab074b6a2 100644 --- a/data/templates/frr/ospfd.frr.j2 +++ b/data/templates/frr/ospfd.frr.j2 @@ -214,13 +214,13 @@ router ospf {{ 'vrf ' ~ vrf if vrf is vyos_defined }} passive-interface default {% endif %} {% if redistribute is vyos_defined %} -{% for protocol, protocols_options in redistribute.items() %} +{% for protocol, options in redistribute.items() %} {% if protocol == 'table' %} -{% for table, table_options in protocols_options.items() %} - redistribute {{ protocol }} {{ table }} {{ 'metric ' + table_options.metric if table_options.metric is vyos_defined }} {{ 'metric-type ' + table_options.metric_type if table_options.metric_type is vyos_defined }} {{ 'route-map ' + table_options.route_map if table_options.route_map is vyos_defined }} +{% for table, table_options in options.items() %} + redistribute {{ protocol }} {{ table }} {{ 'metric ' ~ table_options.metric if table_options.metric is vyos_defined }} {{ 'metric-type ' ~ table_options.metric_type if table_options.metric_type is vyos_defined }} {{ 'route-map ' ~ table_options.route_map if table_options.route_map is vyos_defined }} {% endfor %} {% else %} - redistribute {{ protocol }} {{ 'metric ' + protocols_options.metric if protocols_options.metric is vyos_defined }} {{ 'metric-type ' + protocols_options.metric_type if protocols_options.metric_type is vyos_defined }} {{ 'route-map ' + protocols_options.route_map if protocols_options.route_map is vyos_defined }} + redistribute {{ protocol }} {{ 'metric ' ~ options.metric if options.metric is vyos_defined }} {{ 'metric-type ' ~ options.metric_type if options.metric_type is vyos_defined }} {{ 'route-map ' ~ options.route_map if options.route_map is vyos_defined }} {% endif %} {% endfor %} {% endif %} diff --git a/data/templates/high-availability/keepalived.conf.j2 b/data/templates/high-availability/keepalived.conf.j2 index d54f575b5..240161748 100644 --- a/data/templates/high-availability/keepalived.conf.j2 +++ b/data/templates/high-availability/keepalived.conf.j2 @@ -33,6 +33,24 @@ global_defs { notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py } +{# Sync group has own health-check scripts T6020 #} +{% if vrrp.sync_group is vyos_defined %} +{% for name, sync_group_config in vrrp.sync_group.items() if sync_group_config.disable is not vyos_defined %} +{% if sync_group_config.health_check is vyos_defined %} +vrrp_script healthcheck_sg_{{ name }} { +{% if sync_group_config.health_check.script is vyos_defined %} + script "{{ sync_group_config.health_check.script }}" +{% elif sync_group_config.health_check.ping is vyos_defined %} + script "/usr/bin/ping -c1 {{ sync_group_config.health_check.ping }}" +{% endif %} + interval {{ sync_group_config.health_check.interval }} + fall {{ sync_group_config.health_check.failure_count }} + rise 1 +} +{% endif %} +{% endfor %} +{% endif %} + {% if vrrp.group is vyos_defined %} {% for name, group_config in vrrp.group.items() if group_config.disable is not vyos_defined %} {% if group_config.health_check is vyos_defined %} @@ -82,7 +100,11 @@ vrrp_instance {{ name }} { nopreempt {% endif %} {% if group_config.peer_address is vyos_defined %} - unicast_peer { {{ group_config.peer_address }} } + unicast_peer { +{% for peer_address in group_config.peer_address %} + {{ peer_address }} +{% endfor %} + } {% endif %} {% if group_config.hello_source_address is vyos_defined %} {% if group_config.peer_address is vyos_defined %} @@ -128,7 +150,8 @@ vrrp_instance {{ name }} { {% endfor %} } {% endif %} -{% if group_config.health_check is vyos_defined %} +{# Sync group member can't use own health check script #} +{% if group_config.health_check is vyos_defined and group_config._is_sync_group_member is not vyos_defined %} track_script { healthcheck_{{ name }} } @@ -148,16 +171,12 @@ vrrp_sync_group {{ name }} { {% endif %} } -{# Health-check scripts should be in section sync-group if member is part of the sync-group T4081 #} -{% if vrrp.group is vyos_defined %} -{% for name, group_config in vrrp.group.items() if group_config.disable is not vyos_defined %} -{% if group_config.health_check.script is vyos_defined and name in sync_group_config.member %} +{% if sync_group_config.health_check is vyos_defined %} track_script { - healthcheck_{{ name }} + healthcheck_sg_{{ name }} } -{% endif %} -{% endfor %} {% endif %} + {% if conntrack_sync_group is vyos_defined(name) %} {% set vyos_helper = "/usr/libexec/vyos/vyos-vrrp-conntracksync.sh" %} notify_master "{{ vyos_helper }} master {{ name }}" diff --git a/data/templates/https/nginx.default.j2 b/data/templates/https/nginx.default.j2 index 5d17df001..4619361e5 100644 --- a/data/templates/https/nginx.default.j2 +++ b/data/templates/https/nginx.default.j2 @@ -21,6 +21,10 @@ server { server_name {{ hostname }}; root /srv/localui; +{% if request_body_size_limit is vyos_defined %} + client_max_body_size {{ request_body_size_limit }}M; +{% endif %} + # SSL configuration {% if certificates.cert_path is vyos_defined and certificates.key_path is vyos_defined %} ssl_certificate {{ certificates.cert_path }}; diff --git a/data/templates/load-balancing/wlb.conf.j2 b/data/templates/load-balancing/wlb.conf.j2 index 6557b6f4c..7f04d797e 100644 --- a/data/templates/load-balancing/wlb.conf.j2 +++ b/data/templates/load-balancing/wlb.conf.j2 @@ -93,6 +93,8 @@ rule {{ rule }} { {% if rule_config.destination.port is vyos_defined %} {% if '-' in rule_config.destination.port %} port-ipt "-m multiport --dports {{ rule_config.destination.port | replace('-', ':') }}" +{% elif ',' in rule_config.destination.port %} + port-ipt "-m multiport --dports {{ rule_config.destination.port }}" {% else %} port-ipt " --dport {{ rule_config.destination.port }}" {% endif %} @@ -107,6 +109,8 @@ rule {{ rule }} { {% if rule_config.source.port is vyos_defined %} {% if '-' in rule_config.source.port %} port-ipt "-m multiport --sports {{ rule_config.source.port | replace('-', ':') }}" +{% elif ',' in rule_config.destination.port %} + port-ipt "-m multiport --sports {{ rule_config.source.port }}" {% else %} port.ipt " --sport {{ rule_config.source.port }}" {% endif %} diff --git a/data/templates/login/default_motd.j2 b/data/templates/login/default_motd.j2 new file mode 100644 index 000000000..543c6f8e0 --- /dev/null +++ b/data/templates/login/default_motd.j2 @@ -0,0 +1,14 @@ +Welcome to VyOS! + + ┌── ┐ + . VyOS {{ version_data.version }} + └ ──┘ {{ version_data.release_train }} + + * Documentation: {{ version_data.documentation_url }} + * Project news: {{ version_data.project_news_url }} + * Bug reports: {{ version_data.bugtracker_url }} + +You can change this banner using "set system login banner post-login" command. + +VyOS is a free software distribution that includes multiple components, +you can check individual component licenses under /usr/share/doc/*/copyright diff --git a/data/templates/router-advert/radvd.conf.j2 b/data/templates/router-advert/radvd.conf.j2 index 4ef4751dd..97180d164 100644 --- a/data/templates/router-advert/radvd.conf.j2 +++ b/data/templates/router-advert/radvd.conf.j2 @@ -50,6 +50,13 @@ interface {{ iface }} { {% endfor %} }; {% endif %} +{% if iface_config.nat64prefix is vyos_defined %} +{% for nat64prefix, nat64prefix_options in iface_config.nat64prefix.items() %} + nat64prefix {{ nat64prefix }} { + AdvValidLifetime {{ nat64prefix_options.valid_lifetime }}; + }; +{% endfor %} +{% endif %} {% if iface_config.prefix is vyos_defined %} {% for prefix, prefix_options in iface_config.prefix.items() %} prefix {{ prefix }} { diff --git a/data/templates/snmp/etc.snmpd.conf.j2 b/data/templates/snmp/etc.snmpd.conf.j2 index b1ceb0451..9d91192fc 100644 --- a/data/templates/snmp/etc.snmpd.conf.j2 +++ b/data/templates/snmp/etc.snmpd.conf.j2 @@ -141,8 +141,13 @@ trap2sink {{ trap }}:{{ trap_config.port }} {{ trap_config.community }} # views {% for view, view_config in v3.view.items() %} {% if view_config.oid is vyos_defined %} -{% for oid in view_config.oid %} +{% for oid, oid_config in view_config.oid.items() %} view {{ view }} included .{{ oid }} +{% if oid_config.exclude is vyos_defined %} +{% for excluded in oid_config.exclude %} +view {{ view }} excluded .{{ excluded }} +{% endfor %} +{% endif %} {% endfor %} {% endif %} {% endfor %} diff --git a/data/templates/vyos-hostsd/hosts.j2 b/data/templates/vyos-hostsd/hosts.j2 index 5cad983b4..62ecf3ad0 100644 --- a/data/templates/vyos-hostsd/hosts.j2 +++ b/data/templates/vyos-hostsd/hosts.j2 @@ -4,7 +4,7 @@ # Local host 127.0.0.1 localhost -127.0.1.1 {{ host_name }}{% if domain_name %}.{{ domain_name }} {{ host_name }}{% endif %} +127.0.1.1 {{ host_name }} # The following lines are desirable for IPv6 capable hosts ::1 localhost ip6-localhost ip6-loopback diff --git a/data/vyos-firewall-init.conf b/data/vyos-firewall-init.conf index 5a4e03015..3929edf0b 100644 --- a/data/vyos-firewall-init.conf +++ b/data/vyos-firewall-init.conf @@ -65,11 +65,9 @@ table inet vrf_zones { # Chain for inbound traffic chain vrf_zones_ct_in { type filter hook prerouting priority raw; policy accept; - counter ct original zone set iifname map @ct_iface_map } # Chain for locally-generated traffic chain vrf_zones_ct_out { type filter hook output priority raw; policy accept; - counter ct original zone set oifname map @ct_iface_map } } diff --git a/debian/control b/debian/control index 726a083f2..c5a60f660 100644 --- a/debian/control +++ b/debian/control @@ -256,6 +256,9 @@ Depends: # For "nat64" jool, # End "nat64" +# For "system conntrack modules rtsp" + nat-rtsp, +# End "system conntrack modules rtsp" # For "system ntp" chrony, # End "system ntp" @@ -304,6 +307,10 @@ Depends: # For "run monitor bandwidth" bmon, # End Operational mode +## TPM tools + cryptsetup, + tpm2-tools, +## End TPM tools ## Optional utilities easy-rsa, tcptraceroute, diff --git a/interface-definitions/high-availability.xml.in b/interface-definitions/high-availability.xml.in index 59f0f1052..558404882 100644 --- a/interface-definitions/high-availability.xml.in +++ b/interface-definitions/high-availability.xml.in @@ -195,6 +195,7 @@ <constraint> <validator name="ip-address"/> </constraint> + <multi/> </properties> </leafNode> <leafNode name="no-preempt"> @@ -344,6 +345,55 @@ </completionHelp> </properties> </leafNode> + <node name="health-check"> + <properties> + <help>Health check</help> + </properties> + <children> + <leafNode name="failure-count"> + <properties> + <help>Health check failure count required for transition to fault</help> + <constraint> + <validator name="numeric" argument="--positive" /> + </constraint> + </properties> + <defaultValue>3</defaultValue> + </leafNode> + <leafNode name="interval"> + <properties> + <help>Health check execution interval in seconds</help> + <constraint> + <validator name="numeric" argument="--positive"/> + </constraint> + </properties> + <defaultValue>60</defaultValue> + </leafNode> + <leafNode name="ping"> + <properties> + <help>ICMP ping health check</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 ping target address</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 ping target address</description> + </valueHelp> + <constraint> + <validator name="ip-address"/> + </constraint> + </properties> + </leafNode> + <leafNode name="script"> + <properties> + <help>Health check script file</help> + <constraint> + <validator name="script"/> + </constraint> + </properties> + </leafNode> + </children> + </node> #include <include/vrrp-transition-script.xml.i> </children> </tagNode> diff --git a/interface-definitions/include/accel-ppp/extended-scripts.xml.i b/interface-definitions/include/accel-ppp/extended-scripts.xml.i index 4bba76e32..53ff6d591 100644 --- a/interface-definitions/include/accel-ppp/extended-scripts.xml.i +++ b/interface-definitions/include/accel-ppp/extended-scripts.xml.i @@ -6,7 +6,7 @@ <children> <leafNode name="on-pre-up"> <properties> - <help>Script to run before PPPoE session interface comes up</help> + <help>Script to run before session interface comes up</help> <constraint> <validator name="script"/> </constraint> @@ -14,7 +14,7 @@ </leafNode> <leafNode name="on-up"> <properties> - <help>Script to run when PPPoE session interface is completely configured and started</help> + <help>Script to run when session interface is completely configured and started</help> <constraint> <validator name="script"/> </constraint> @@ -22,7 +22,7 @@ </leafNode> <leafNode name="on-down"> <properties> - <help>Script to run when PPPoE session interface going to terminate</help> + <help>Script to run when session interface going to terminate</help> <constraint> <validator name="script"/> </constraint> @@ -30,7 +30,7 @@ </leafNode> <leafNode name="on-change"> <properties> - <help>Script to run when PPPoE session interface changed by RADIUS CoA handling</help> + <help>Script to run when session interface changed by RADIUS CoA handling</help> <constraint> <validator name="script"/> </constraint> diff --git a/interface-definitions/include/constraint/dhcp-client-string-option.xml.i b/interface-definitions/include/constraint/dhcp-client-string-option.xml.i index 88257a9bb..0e3fb8a96 100644 --- a/interface-definitions/include/constraint/dhcp-client-string-option.xml.i +++ b/interface-definitions/include/constraint/dhcp-client-string-option.xml.i @@ -1,4 +1,4 @@ <!-- include start from constraint/dhcp-client-string-option.xml.i --> -<regex>[-_a-zA-Z0-9\s]+</regex> +<regex>[-_a-zA-Z0-9.\s]+</regex> <regex>([a-fA-F0-9][a-fA-F0-9]:){2,}[a-fA-F0-9][a-fA-F0-9]</regex> <!-- include end --> diff --git a/interface-definitions/include/firewall/common-rule-bridge.xml.i b/interface-definitions/include/firewall/common-rule-bridge.xml.i index 6de770c79..dcdd970ac 100644 --- a/interface-definitions/include/firewall/common-rule-bridge.xml.i +++ b/interface-definitions/include/firewall/common-rule-bridge.xml.i @@ -9,12 +9,7 @@ #include <include/firewall/mac-address.xml.i> </children> </node> -<leafNode name="disable"> - <properties> - <help>Option to disable firewall rule</help> - <valueless/> - </properties> -</leafNode> +#include <include/generic-disable-node.xml.i> <leafNode name="jump-target"> <properties> <help>Set jump target. Action jump must be defined to use this setting</help> diff --git a/interface-definitions/include/firewall/common-rule-inet.xml.i b/interface-definitions/include/firewall/common-rule-inet.xml.i index 85189d975..bef1c3da5 100644 --- a/interface-definitions/include/firewall/common-rule-inet.xml.i +++ b/interface-definitions/include/firewall/common-rule-inet.xml.i @@ -7,12 +7,7 @@ #include <include/firewall/connection-mark.xml.i> #include <include/firewall/conntrack-helper.xml.i> #include <include/firewall/nft-queue.xml.i> -<leafNode name="disable"> - <properties> - <help>Option to disable firewall rule</help> - <valueless/> - </properties> -</leafNode> +#include <include/generic-disable-node.xml.i> <node name="fragment"> <properties> <help>IP fragment match</help> diff --git a/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i b/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i index 0d749aa27..e7468bfba 100644 --- a/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i +++ b/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i @@ -18,12 +18,7 @@ #include <include/firewall/source-destination-group.xml.i> </children> </node> -<leafNode name="disable"> - <properties> - <help>Option to disable firewall rule</help> - <valueless/> - </properties> -</leafNode> +#include <include/generic-disable-node.xml.i> <node name="fragment"> <properties> <help>IP fragment match</help> diff --git a/interface-definitions/include/firewall/conntrack-helper.xml.i b/interface-definitions/include/firewall/conntrack-helper.xml.i index ee17f2c61..3ca1a0353 100644 --- a/interface-definitions/include/firewall/conntrack-helper.xml.i +++ b/interface-definitions/include/firewall/conntrack-helper.xml.i @@ -22,6 +22,10 @@ <description>Related traffic from NFS helper</description> </valueHelp> <valueHelp> + <format>rtsp</format> + <description>Related traffic from RTSP helper</description> + </valueHelp> + <valueHelp> <format>sip</format> <description>Related traffic from SIP helper</description> </valueHelp> @@ -34,7 +38,7 @@ <description>Related traffic from SQLNet helper</description> </valueHelp> <constraint> - <regex>(ftp|h323|pptp|nfs|sip|tftp|sqlnet)</regex> + <regex>(ftp|h323|pptp|nfs|rtsp|sip|tftp|sqlnet)</regex> </constraint> <multi/> </properties> diff --git a/interface-definitions/include/generic-description.xml.i b/interface-definitions/include/generic-description.xml.i index 63e5e174e..7e091eae7 100644 --- a/interface-definitions/include/generic-description.xml.i +++ b/interface-definitions/include/generic-description.xml.i @@ -7,9 +7,9 @@ <description>Description</description> </valueHelp> <constraint> - <regex>[[:ascii:]]{0,256}</regex> + <regex>.{0,255}</regex> </constraint> - <constraintErrorMessage>Description too long (limit 256 characters)</constraintErrorMessage> + <constraintErrorMessage>Description too long (limit 255 characters)</constraintErrorMessage> </properties> </leafNode> <!-- include end --> diff --git a/interface-definitions/include/ospfv3/protocol-common-config.xml.i b/interface-definitions/include/ospfv3/protocol-common-config.xml.i index 4c3ca68e1..72fb86d3d 100644 --- a/interface-definitions/include/ospfv3/protocol-common-config.xml.i +++ b/interface-definitions/include/ospfv3/protocol-common-config.xml.i @@ -221,11 +221,23 @@ <help>Redistribute information from another routing protocol</help> </properties> <children> + <node name="babel"> + <properties> + <help>Redistribute Babel routes</help> + </properties> + <children> + #include <include/ospf/metric.xml.i> + #include <include/ospf/metric-type.xml.i> + #include <include/route-map.xml.i> + </children> + </node> <node name="bgp"> <properties> <help>Redistribute BGP routes</help> </properties> <children> + #include <include/ospf/metric.xml.i> + #include <include/ospf/metric-type.xml.i> #include <include/route-map.xml.i> </children> </node> @@ -234,30 +246,38 @@ <help>Redistribute connected routes</help> </properties> <children> + #include <include/ospf/metric.xml.i> + #include <include/ospf/metric-type.xml.i> #include <include/route-map.xml.i> </children> </node> - <node name="kernel"> + <node name="isis"> <properties> - <help>Redistribute kernel routes</help> + <help>Redistribute IS-IS routes</help> </properties> <children> + #include <include/ospf/metric.xml.i> + #include <include/ospf/metric-type.xml.i> #include <include/route-map.xml.i> </children> </node> - <node name="ripng"> + <node name="kernel"> <properties> - <help>Redistribute RIPNG routes</help> + <help>Redistribute kernel routes</help> </properties> <children> + #include <include/ospf/metric.xml.i> + #include <include/ospf/metric-type.xml.i> #include <include/route-map.xml.i> </children> </node> - <node name="babel"> + <node name="ripng"> <properties> - <help>Redistribute Babel routes</help> + <help>Redistribute RIPNG routes</help> </properties> <children> + #include <include/ospf/metric.xml.i> + #include <include/ospf/metric-type.xml.i> #include <include/route-map.xml.i> </children> </node> @@ -266,6 +286,8 @@ <help>Redistribute static routes</help> </properties> <children> + #include <include/ospf/metric.xml.i> + #include <include/ospf/metric-type.xml.i> #include <include/route-map.xml.i> </children> </node> diff --git a/interface-definitions/include/pki/cli-private-key-base64.xml.i b/interface-definitions/include/pki/cli-private-key-base64.xml.i index 3a7ee0ce9..f57e9b198 100644 --- a/interface-definitions/include/pki/cli-private-key-base64.xml.i +++ b/interface-definitions/include/pki/cli-private-key-base64.xml.i @@ -1,4 +1,4 @@ -<!-- include start from pki/pki-cli-private-key.xml.i --> +<!-- include start from pki/cli-private-key-base64.xml.i --> <leafNode name="key"> <properties> <help>Private key in PEM format</help> diff --git a/interface-definitions/include/pki/cli-public-key-base64.xml.i b/interface-definitions/include/pki/cli-public-key-base64.xml.i index 3a7ee0ce9..f7cffae55 100644 --- a/interface-definitions/include/pki/cli-public-key-base64.xml.i +++ b/interface-definitions/include/pki/cli-public-key-base64.xml.i @@ -1,11 +1,11 @@ -<!-- include start from pki/pki-cli-private-key.xml.i --> +<!-- include start from pki/cli-public-key-base64.xml.i --> <leafNode name="key"> <properties> - <help>Private key in PEM format</help> + <help>Public key in PEM format</help> <constraint> <validator name="base64"/> </constraint> - <constraintErrorMessage>Private key is not base64-encoded</constraintErrorMessage> + <constraintErrorMessage>Public key is not base64-encoded</constraintErrorMessage> </properties> </leafNode> <!-- include end --> diff --git a/interface-definitions/include/version/dhcp-server-version.xml.i b/interface-definitions/include/version/dhcp-server-version.xml.i index d83172e72..3dcbc513a 100644 --- a/interface-definitions/include/version/dhcp-server-version.xml.i +++ b/interface-definitions/include/version/dhcp-server-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/dhcp-server-version.xml.i --> -<syntaxVersion component='dhcp-server' version='9'></syntaxVersion> +<syntaxVersion component='dhcp-server' version='10'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/interfaces_wireless.xml.in b/interface-definitions/interfaces_wireless.xml.in index b5da0a556..458f7ebb3 100644 --- a/interface-definitions/interfaces_wireless.xml.in +++ b/interface-definitions/interfaces_wireless.xml.in @@ -455,14 +455,18 @@ <properties> <help>Indicate country in which device is operating</help> <completionHelp> - <list>us eu jp de uk cn es fr ru</list> + <list>00 ad ae af ai al am an ar as at au aw az ba bb bd be bf bg bh bl bm bn bo br bs bt by bz ca cf ch ci cl cn co cr cu cx cy cz de dk dm do dz ec ee eg es et fi fm fr gb gd ge gf gh gl gp gr gt gu gy hk hn hr ht hu id ie il in ir is it jm jo jp ke kh kn kp kr kw ky kz lb lc li lk ls lt lu lv ma mc md me mf mh mk mn mo mp mq mr mt mu mv mw mx my ng ni nl no np nz om pa pe pf pg ph pk pl pm pr pt pw py qa re ro rs ru rw sa se sg si sk sn sr sv sy tc td tg th tn tr tt tw tz ua ug us uy uz vc ve vi vn vu wf ws ye yt za zw</list> </completionHelp> <valueHelp> + <format>00</format> + <description>World regulatory domain</description> + </valueHelp> + <valueHelp> <format>txt</format> <description>ISO/IEC 3166-1 Country Code</description> </valueHelp> <constraint> - <regex>[a-z][a-z]</regex> + <regex>(00|ad|ae|af|ai|al|am|an|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bl|bm|bn|bo|br|bs|bt|by|bz|ca|cf|ch|ci|cl|cn|co|cr|cu|cx|cy|cz|de|dk|dm|do|dz|ec|ee|eg|es|et|fi|fm|fr|gb|gd|ge|gf|gh|gl|gp|gr|gt|gu|gy|hk|hn|hr|ht|hu|id|ie|il|in|ir|is|it|jm|jo|jp|ke|kh|kn|kp|kr|kw|ky|kz|lb|lc|li|lk|ls|lt|lu|lv|ma|mc|md|me|mf|mh|mk|mn|mo|mp|mq|mr|mt|mu|mv|mw|mx|my|ng|ni|nl|no|np|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pr|pt|pw|py|qa|re|ro|rs|ru|rw|sa|se|sg|si|sk|sn|sr|sv|sy|tc|td|tg|th|tn|tr|tt|tw|tz|ua|ug|us|uy|uz|vc|ve|vi|vn|vu|wf|ws|ye|yt|za|zw)</regex> </constraint> <constraintErrorMessage>Invalid ISO/IEC 3166-1 Country Code</constraintErrorMessage> </properties> diff --git a/interface-definitions/nat64.xml.in b/interface-definitions/nat64.xml.in index dfdd295d2..4b3c157cc 100644 --- a/interface-definitions/nat64.xml.in +++ b/interface-definitions/nat64.xml.in @@ -2,7 +2,7 @@ <interfaceDefinition> <node name="nat64" owner="${vyos_conf_scripts_dir}/nat64.py"> <properties> - <help>IPv6-to-IPv4 Network Address Translation (NAT64) Settings</help> + <help>Network Address Translation (NAT64) parameters</help> <priority>501</priority> </properties> <children> diff --git a/interface-definitions/nat66.xml.in b/interface-definitions/nat66.xml.in index 1518de8bd..32d501cce 100644 --- a/interface-definitions/nat66.xml.in +++ b/interface-definitions/nat66.xml.in @@ -2,7 +2,7 @@ <interfaceDefinition> <node name="nat66" owner="${vyos_conf_scripts_dir}/nat66.py"> <properties> - <help>IPv6-to-IPv6 Network Prefix Translation (NAT66/NPT) Settings</help> + <help>Network Prefix Translation (NAT66/NPTv6) parameters</help> <priority>500</priority> </properties> <children> diff --git a/interface-definitions/pki.xml.in b/interface-definitions/pki.xml.in index 7a0b073b4..b922771c1 100644 --- a/interface-definitions/pki.xml.in +++ b/interface-definitions/pki.xml.in @@ -2,7 +2,7 @@ <interfaceDefinition> <node name="pki" owner="${vyos_conf_scripts_dir}/pki.py"> <properties> - <help>VyOS PKI configuration</help> + <help>Public key infrastructure (PKI)</help> <priority>300</priority> </properties> <children> diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index 0d82cd3f8..791fa1d87 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -1124,12 +1124,20 @@ <leafNode name="exclude"> <properties> <help>Remove/exclude from the as-path attribute</help> + <completionHelp> + <list>all</list> + </completionHelp> <valueHelp> - <format>u32</format> + <format>u32:1-4294967295</format> <description>AS number</description> </valueHelp> + <valueHelp> + <format>all</format> + <description>Exclude all AS numbers from the as-path</description> + </valueHelp> <constraint> <validator name="as-number-list"/> + <regex>(all)</regex> </constraint> </properties> </leafNode> @@ -1137,7 +1145,7 @@ <properties> <help>Prepend to the as-path</help> <valueHelp> - <format>u32</format> + <format>u32:1-4294967295</format> <description>AS number</description> </valueHelp> <constraint> diff --git a/interface-definitions/service_config-sync.xml.in b/interface-definitions/service_config-sync.xml.in index 9955acfee..9e9dcdb69 100644 --- a/interface-definitions/service_config-sync.xml.in +++ b/interface-definitions/service_config-sync.xml.in @@ -73,30 +73,382 @@ </constraint> </properties> </leafNode> - <leafNode name="section"> + <node name="section"> <properties> <help>Section for synchronization</help> - <completionHelp> - <list>nat nat66 firewall</list> - </completionHelp> - <valueHelp> - <format>nat</format> - <description>NAT</description> - </valueHelp> - <valueHelp> - <format>nat66</format> - <description>NAT66</description> - </valueHelp> - <valueHelp> - <format>firewall</format> - <description>firewall</description> - </valueHelp> - <constraint> - <regex>(nat|nat66|firewall)</regex> - </constraint> - <multi/> </properties> - </leafNode> + <children> + <leafNode name="firewall"> + <properties> + <help>Firewall</help> + <valueless/> + </properties> + </leafNode> + <node name="interfaces"> + <properties> + <help>Interfaces</help> + </properties> + <children> + <leafNode name="bonding"> + <properties> + <help>Bonding interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="bridge"> + <properties> + <help>Bridge interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="dummy"> + <properties> + <help>Dummy interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="ethernet"> + <properties> + <help>Ethernet interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="geneve"> + <properties> + <help>GENEVE interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="input"> + <properties> + <help>Input interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="l2tpv3"> + <properties> + <help>L2TPv3 interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="loopback"> + <properties> + <help>Loopback interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="macsec"> + <properties> + <help>MACsec interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="openvpn"> + <properties> + <help>OpenVPN interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="pppoe"> + <properties> + <help>PPPoE interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="pseudo-ethernet"> + <properties> + <help>Pseudo-Ethernet interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="sstpc"> + <properties> + <help>SSTP client interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="tunnel"> + <properties> + <help>Tunnel interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="virtual-ethernet"> + <properties> + <help>Virtual Ethernet interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="vti"> + <properties> + <help>Virtual tunnel interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="vxlan"> + <properties> + <help>VXLAN interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="wireguard"> + <properties> + <help>Wireguard interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="wireless"> + <properties> + <help>Wireless interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="wwan"> + <properties> + <help>WWAN interface</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <leafNode name="nat"> + <properties> + <help>NAT</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="nat66"> + <properties> + <help>NAT66</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="pki"> + <properties> + <help>Public key infrastructure (PKI)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="policy"> + <properties> + <help>Routing policy</help> + <valueless/> + </properties> + </leafNode> + <node name="protocols"> + <properties> + <help>Routing protocols</help> + </properties> + <children> + <leafNode name="babel"> + <properties> + <help>Babel Routing Protocol</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="bfd"> + <properties> + <help>Bidirectional Forwarding Detection (BFD)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="bgp"> + <properties> + <help>Border Gateway Protocol (BGP)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="failover"> + <properties> + <help>Failover route</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="igmp-proxy"> + <properties> + <help>Internet Group Management Protocol (IGMP) proxy</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="isis"> + <properties> + <help>Intermediate System to Intermediate System (IS-IS)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="mpls"> + <properties> + <help>Multiprotocol Label Switching (MPLS)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="nhrp"> + <properties> + <help>Next Hop Resolution Protocol (NHRP) parameters</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="ospf"> + <properties> + <help>Open Shortest Path First (OSPF)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="ospfv3"> + <properties> + <help>Open Shortest Path First (OSPF) for IPv6</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="pim"> + <properties> + <help>Protocol Independent Multicast (PIM) and IGMP</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="pim6"> + <properties> + <help>Protocol Independent Multicast for IPv6 (PIMv6) and MLD</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="rip"> + <properties> + <help>Routing Information Protocol (RIP) parameters</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="ripng"> + <properties> + <help>Routing Information Protocol (RIPng) parameters</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="rpki"> + <properties> + <help>Resource Public Key Infrastructure (RPKI)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="segment-routing"> + <properties> + <help>Segment Routing</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="static"> + <properties> + <help>Static Routing</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <node name="service"> + <properties> + <help>System services</help> + </properties> + <children> + <leafNode name="console-server"> + <properties> + <help>Serial Console Server</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="dhcp-relay"> + <properties> + <help>Host Configuration Protocol (DHCP) relay agent</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="dhcp-server"> + <properties> + <help>Dynamic Host Configuration Protocol (DHCP) for DHCP server</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="dhcpv6-relay"> + <properties> + <help>DHCPv6 Relay Agent parameters</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="dhcpv6-server"> + <properties> + <help>DHCP for IPv6 (DHCPv6) server</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="dns"> + <properties> + <help>Domain Name System (DNS) related services</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="lldp"> + <properties> + <help>LLDP settings</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="mdns"> + <properties> + <help>Multicast DNS (mDNS) parameters</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="monitoring"> + <properties> + <help>Monitoring services</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="ndp-proxy"> + <properties> + <help>Neighbor Discovery Protocol (NDP) Proxy</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="ntp"> + <properties> + <help>Network Time Protocol (NTP) configuration</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="snmp"> + <properties> + <help>Simple Network Management Protocol (SNMP)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="tftp-server"> + <properties> + <help>Trivial File Transfer Protocol (TFTP) server</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="webproxy"> + <properties> + <help>Webproxy service settings</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <leafNode name="vpn"> + <properties> + <help>Virtual Private Network (VPN)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="vrf"> + <properties> + <help>Virtual Routing and Forwarding</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> </children> </node> </children> diff --git a/interface-definitions/service_conntrack-sync.xml.in b/interface-definitions/service_conntrack-sync.xml.in index 46dc8adc0..397864867 100644 --- a/interface-definitions/service_conntrack-sync.xml.in +++ b/interface-definitions/service_conntrack-sync.xml.in @@ -52,6 +52,12 @@ <valueless/> </properties> </leafNode> + <leafNode name="disable-syslog"> + <properties> + <help>Disable connection logging via Syslog</help> + <valueless/> + </properties> + </leafNode> <leafNode name="event-listen-queue-size"> <properties> <help>Queue size for local conntrack events</help> diff --git a/interface-definitions/service_dhcp-server.xml.in b/interface-definitions/service_dhcp-server.xml.in index 5c9d4a360..1c10a462d 100644 --- a/interface-definitions/service_dhcp-server.xml.in +++ b/interface-definitions/service_dhcp-server.xml.in @@ -122,6 +122,12 @@ <multi/> </properties> </leafNode> + <leafNode name="ignore-client-id"> + <properties> + <help>Ignore client identifier for lease lookups</help> + <valueless/> + </properties> + </leafNode> <leafNode name="lease"> <properties> <help>Lease timeout in seconds</help> diff --git a/interface-definitions/service_https.xml.in b/interface-definitions/service_https.xml.in index b60c7ff2e..afe430c0c 100644 --- a/interface-definitions/service_https.xml.in +++ b/interface-definitions/service_https.xml.in @@ -138,6 +138,19 @@ <leafNode name='port'> <defaultValue>443</defaultValue> </leafNode> + <leafNode name="request-body-size-limit"> + <properties> + <help>Maximum request body size in megabytes</help> + <valueHelp> + <format>u32:1-256</format> + <description>Request body size in megabytes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-256"/> + </constraint> + </properties> + <defaultValue>1</defaultValue> + </leafNode> <node name="certificates"> <properties> <help>TLS certificates</help> diff --git a/interface-definitions/service_router-advert.xml.in b/interface-definitions/service_router-advert.xml.in index 16c29022d..166a4a0cf 100644 --- a/interface-definitions/service_router-advert.xml.in +++ b/interface-definitions/service_router-advert.xml.in @@ -225,6 +225,36 @@ </leafNode> </children> </tagNode> + <tagNode name="nat64prefix"> + <properties> + <help>NAT64 prefix included in the router advertisements</help> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix to be advertized</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + <children> + <leafNode name="valid-lifetime"> + <properties> + <help>Time in seconds that the prefix will remain valid</help> + <completionHelp> + <list>infinity</list> + </completionHelp> + <valueHelp> + <format>u32:4-65528</format> + <description>Time in seconds that the prefix will remain valid</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 4-65528"/> + </constraint> + </properties> + <defaultValue>65528</defaultValue> + </leafNode> + </children> + </tagNode> <tagNode name="prefix"> <properties> <help>IPv6 prefix to be advertised in Router Advertisements (RAs)</help> diff --git a/interface-definitions/service_snmp.xml.in b/interface-definitions/service_snmp.xml.in index e16e9daa1..f23151ef9 100644 --- a/interface-definitions/service_snmp.xml.in +++ b/interface-definitions/service_snmp.xml.in @@ -543,6 +543,7 @@ <leafNode name="exclude"> <properties> <help>Exclude is an optional argument</help> + <multi/> </properties> </leafNode> <leafNode name="mask"> diff --git a/interface-definitions/system_conntrack.xml.in b/interface-definitions/system_conntrack.xml.in index a348097cc..219c6e28e 100644 --- a/interface-definitions/system_conntrack.xml.in +++ b/interface-definitions/system_conntrack.xml.in @@ -289,6 +289,12 @@ <valueless/> </properties> </leafNode> + <leafNode name="rtsp"> + <properties> + <help>RTSP connection tracking</help> + <valueless/> + </properties> + </leafNode> <leafNode name="sip"> <properties> <help>SIP connection tracking</help> diff --git a/interface-definitions/system_option.xml.in b/interface-definitions/system_option.xml.in index 602d7d100..fe517d17d 100644 --- a/interface-definitions/system_option.xml.in +++ b/interface-definitions/system_option.xml.in @@ -43,6 +43,12 @@ <valueless/> </properties> </leafNode> + <leafNode name="disable-power-saving"> + <properties> + <help>Disable CPU power saving mechanisms also known as C states</help> + <valueless/> + </properties> + </leafNode> </children> </node> <leafNode name="keyboard-layout"> diff --git a/op-mode-definitions/clear-dhcp-server-lease.xml.in b/op-mode-definitions/clear-dhcp-server-lease.xml.in deleted file mode 100644 index aef0eb22a..000000000 --- a/op-mode-definitions/clear-dhcp-server-lease.xml.in +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> - <node name="clear"> - <children> - <node name="dhcp-server"> - <properties> - <help>Clear DHCP server lease</help> - </properties> - <children> - <tagNode name="lease"> - <properties> - <help>DHCP server lease</help> - </properties> - <command>sudo ${vyos_op_scripts_dir}/clear_dhcp_lease.py --ip $4</command> - </tagNode> - </children> - </node> - </children> - </node> -</interfaceDefinition> diff --git a/op-mode-definitions/container.xml.in b/op-mode-definitions/container.xml.in index 96c582a83..4aa13e913 100644 --- a/op-mode-definitions/container.xml.in +++ b/op-mode-definitions/container.xml.in @@ -41,6 +41,7 @@ <properties> <help>Delete container image</help> <completionHelp> + <list>all</list> <script>sudo podman image ls -q</script> </completionHelp> </properties> diff --git a/op-mode-definitions/crypt.xml.in b/op-mode-definitions/crypt.xml.in new file mode 100644 index 000000000..105592a1a --- /dev/null +++ b/op-mode-definitions/crypt.xml.in @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="encryption"> + <properties> + <help>Manage config encryption</help> + </properties> + <children> + <node name="disable"> + <properties> + <help>Disable config encryption using TPM or recovery key</help> + </properties> + <command>sudo ${vyos_libexec_dir}/vyos-config-encrypt.py --disable</command> + </node> + <node name="enable"> + <properties> + <help>Enable config encryption using TPM</help> + </properties> + <command>sudo ${vyos_libexec_dir}/vyos-config-encrypt.py --enable</command> + </node> + <node name="load"> + <properties> + <help>Load encrypted config volume using TPM or recovery key</help> + </properties> + <command>sudo ${vyos_libexec_dir}/vyos-config-encrypt.py --load</command> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/dhcp.xml.in b/op-mode-definitions/dhcp.xml.in index 0db7471e5..3c42c8e8f 100644 --- a/op-mode-definitions/dhcp.xml.in +++ b/op-mode-definitions/dhcp.xml.in @@ -1,5 +1,35 @@ <?xml version="1.0" encoding="UTF-8"?> <interfaceDefinition> + <node name="clear"> + <children> + <node name="dhcp-server"> + <properties> + <help>Clear DHCP server lease</help> + </properties> + <children> + <tagNode name="lease"> + <properties> + <help>DHCP server lease</help> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py clear_dhcp_server_lease --family inet --address $4</command> + </tagNode> + </children> + </node> + <node name="dhcpv6-server"> + <properties> + <help>Clear DHCPv6 server lease</help> + </properties> + <children> + <tagNode name="lease"> + <properties> + <help>DHCPv6 server lease</help> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py clear_dhcp_server_lease --family inet6 --address $4</command> + </tagNode> + </children> + </node> + </children> + </node> <node name="show"> <children> <node name="dhcp"> diff --git a/op-mode-definitions/force-commit-archive.xml.in b/op-mode-definitions/force-commit-archive.xml.in new file mode 100644 index 000000000..162323c20 --- /dev/null +++ b/op-mode-definitions/force-commit-archive.xml.in @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="force"> + <children> + <leafNode name="commit-archive"> + <properties> + <help>Manually archive configuration</help> + </properties> + <command>/usr/bin/config-mgmt</command> + </leafNode> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/pki.xml.in b/op-mode-definitions/pki.xml.in index 4b8d9c47a..a5e01bade 100644 --- a/op-mode-definitions/pki.xml.in +++ b/op-mode-definitions/pki.xml.in @@ -4,7 +4,7 @@ <children> <node name="pki"> <properties> - <help>Generate PKI certificates and keys</help> + <help>Generate public key infrastructure (PKI) certificates and keys</help> </properties> <children> <node name="ca"> @@ -27,7 +27,7 @@ <list><filename></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --ca "$7" --sign "$5" --file</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action generate --ca "$7" --sign "$5" --file</command> </tagNode> <tagNode name="install"> <properties> @@ -36,10 +36,10 @@ <list><certificate name></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --ca "$7" --sign "$5" --install</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --ca "$7" --sign "$5" --install</command> </tagNode> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --ca "noname" --sign "$5"</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --ca "noname" --sign "$5"</command> </tagNode> <tagNode name="file"> <properties> @@ -48,7 +48,7 @@ <list><filename></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --ca "$5" --file</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action generate --ca "$5" --file</command> </tagNode> <tagNode name="install"> <properties> @@ -57,10 +57,10 @@ <list><CA name></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --ca "$5" --install</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --ca "$5" --install</command> </tagNode> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --ca "noname"</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --ca "noname"</command> </node> <node name="certificate"> <properties> @@ -79,7 +79,7 @@ <list><filename></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --certificate "$6" --self-sign --file</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action generate --certificate "$6" --self-sign --file</command> </tagNode> <tagNode name="install"> <properties> @@ -88,10 +88,10 @@ <list><certificate name></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --certificate "$6" --self-sign --install</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --certificate "$6" --self-sign --install</command> </tagNode> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --certificate "noname" --self-sign</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --certificate "noname" --self-sign</command> </node> <tagNode name="sign"> <properties> @@ -108,7 +108,7 @@ <list><filename></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --certificate "$7" --sign "$5" --file</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action generate --certificate "$7" --sign "$5" --file</command> </tagNode> <tagNode name="install"> <properties> @@ -117,10 +117,10 @@ <list><certificate name></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --certificate "$7" --sign "$5" --install</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --certificate "$7" --sign "$5" --install</command> </tagNode> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --certificate "noname" --sign "$5"</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --certificate "noname" --sign "$5"</command> </tagNode> <tagNode name="file"> <properties> @@ -129,7 +129,7 @@ <list><filename></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --certificate "$5" --file</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action generate --certificate "$5" --file</command> </tagNode> <tagNode name="install"> <properties> @@ -138,10 +138,10 @@ <list><certificate name></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --certificate "$5" --install</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --certificate "$5" --install</command> </tagNode> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --certificate "noname"</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --certificate "noname"</command> </node> <tagNode name="crl"> <properties> @@ -158,16 +158,16 @@ <list><filename></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --crl "$4" --file</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action generate --crl "$4" --file</command> </tagNode> <leafNode name="install"> <properties> <help>Commands for installing generated CRL into running configuration</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --crl "$4" --install</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --crl "$4" --install</command> </leafNode> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --crl "$4"</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --crl "$4"</command> </tagNode> <node name="dh"> <properties> @@ -181,7 +181,7 @@ <list><filename></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --dh "$5" --file</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action generate --dh "$5" --file</command> </tagNode> <tagNode name="install"> <properties> @@ -190,10 +190,10 @@ <list><DH name></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --dh "$5" --install</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --dh "$5" --install</command> </tagNode> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --dh "noname"</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --dh "noname"</command> </node> <node name="key-pair"> <properties> @@ -207,7 +207,7 @@ <list><filename></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --keypair "$5" --file</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action generate --keypair "$5" --file</command> </tagNode> <tagNode name="install"> <properties> @@ -216,10 +216,10 @@ <list><key name></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --keypair "$5" --install</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --keypair "$5" --install</command> </tagNode> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --keypair "noname"</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --keypair "noname"</command> </node> <node name="openvpn"> <properties> @@ -238,7 +238,7 @@ <list><filename></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --openvpn "$6" --file</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action generate --openvpn "$6" --file</command> </tagNode> <tagNode name="install"> <properties> @@ -247,10 +247,10 @@ <list><key name></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --openvpn "$6" --install</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --openvpn "$6" --install</command> </tagNode> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --openvpn "noname"</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --openvpn "noname"</command> </node> </children> </node> @@ -266,7 +266,7 @@ <list><filename></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --ssh "$5" --file</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action generate --ssh "$5" --file</command> </tagNode> <tagNode name="install"> <properties> @@ -275,10 +275,10 @@ <list><key name></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --ssh "$5" --install</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --ssh "$5" --install</command> </tagNode> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --ssh "noname"</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --ssh "noname"</command> </node> <node name="wireguard"> <properties> @@ -302,12 +302,12 @@ <path>interfaces wireguard</path> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key --interface "$7" --install</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key --interface "$7" --install</command> </tagNode> </children> </node> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key</command> </node> <node name="preshared-key"> <properties> @@ -334,14 +334,14 @@ <path>interfaces wireguard ${COMP_WORDS[COMP_CWORD-2]} peer</path> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk --interface "$7" --peer "$9" --install</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk --interface "$7" --peer "$9" --install</command> </tagNode> </children> </tagNode> </children> </node> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk</command> + <command>${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk</command> </node> </children> </node> @@ -371,13 +371,13 @@ <properties> <help>Path to CA certificate file</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action import --ca "$4" --filename "$6"</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action import --ca "$4" --filename "$6"</command> </tagNode> <tagNode name="key-file"> <properties> <help>Path to private key file</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action import --ca "$4" --key-filename "$6"</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action import --ca "$4" --key-filename "$6"</command> </tagNode> </children> </tagNode> @@ -393,13 +393,13 @@ <properties> <help>Path to certificate file</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action import --certificate "$4" --filename "$6"</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action import --certificate "$4" --filename "$6"</command> </tagNode> <tagNode name="key-file"> <properties> <help>Path to private key file</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action import --certificate "$4" --key-filename "$6"</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action import --certificate "$4" --key-filename "$6"</command> </tagNode> </children> </tagNode> @@ -415,7 +415,7 @@ <properties> <help>Path to CRL file</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action import --crl "$4" --filename "$6"</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action import --crl "$4" --filename "$6"</command> </tagNode> </children> </tagNode> @@ -431,7 +431,7 @@ <properties> <help>Path to DH parameters file</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action import --dh "$4" --filename "$6"</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action import --dh "$4" --filename "$6"</command> </tagNode> </children> </tagNode> @@ -447,13 +447,13 @@ <properties> <help>Path to public key file</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action import --keypair "$4" --filename "$6"</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action import --keypair "$4" --filename "$6"</command> </tagNode> <tagNode name="private-file"> <properties> <help>Path to private key file</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action import --keypair "$4" --key-filename "$6"</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action import --keypair "$4" --key-filename "$6"</command> </tagNode> </children> </tagNode> @@ -474,7 +474,7 @@ <properties> <help>Path to shared secret key file</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action import --openvpn "$5" --filename "$7"</command> + <command>sudo -E ${vyos_op_scripts_dir}/pki.py --action import --openvpn "$5" --filename "$7"</command> </tagNode> </children> </tagNode> @@ -495,7 +495,7 @@ <properties> <help>Show x509 CA certificates</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --ca "all"</command> + <command>${vyos_op_scripts_dir}/pki.py --action show --ca "all"</command> </leafNode> <tagNode name="ca"> <properties> @@ -504,13 +504,13 @@ <path>pki ca</path> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --ca "$4"</command> + <command>${vyos_op_scripts_dir}/pki.py --action show --ca "$4"</command> <children> <leafNode name="pem"> <properties> <help>Show x509 CA certificate in PEM format</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --ca "$4" --pem</command> + <command>${vyos_op_scripts_dir}/pki.py --action show --ca "$4" --pem</command> </leafNode> </children> </tagNode> @@ -518,7 +518,7 @@ <properties> <help>Show x509 certificates</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "all"</command> + <command>${vyos_op_scripts_dir}/pki.py --action show --certificate "all"</command> </leafNode> <tagNode name="certificate"> <properties> @@ -527,7 +527,7 @@ <path>pki certificate</path> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "$4"</command> + <command>${vyos_op_scripts_dir}/pki.py --action show --certificate "$4"</command> <children> <leafNode name="pem"> <properties> @@ -542,7 +542,7 @@ <list>sha256 sha384 sha512</list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "$4" --fingerprint "$6"</command> + <command>${vyos_op_scripts_dir}/pki.py --action show --certificate "$4" --fingerprint "$6"</command> </tagNode> </children> </tagNode> @@ -550,7 +550,7 @@ <properties> <help>Show x509 certificate revocation lists</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --crl "all"</command> + <command>${vyos_op_scripts_dir}/pki.py --action show --crl "all"</command> </leafNode> <tagNode name="crl"> <properties> @@ -559,18 +559,18 @@ <path>pki ca</path> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --crl "$4"</command> + <command>${vyos_op_scripts_dir}/pki.py --action show --crl "$4"</command> <children> <leafNode name="pem"> <properties> <help>Show x509 certificate revocation lists by CA name in PEM format</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --crl "$4" --pem</command> + <command>${vyos_op_scripts_dir}/pki.py --action show --crl "$4" --pem</command> </leafNode> </children> </tagNode> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action show</command> + <command>${vyos_op_scripts_dir}/pki.py --action show</command> </node> </children> </node> diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in index a6ce04624..e13270364 100644 --- a/op-mode-definitions/show-log.xml.in +++ b/op-mode-definitions/show-log.xml.in @@ -219,7 +219,7 @@ <path>firewall ipv4 forward filter rule</path> </completionHelp> </properties> - <command>journalctl --no-hostname --boot -k | egrep "\[ipv4-FWD-filter-$8-[ADRJC]\]"</command> + <command>journalctl --no-hostname --boot -k | egrep "\[ipv4-FWD-filter-$8-[ADRJCO]\]"</command> </tagNode> </children> </node> @@ -322,7 +322,7 @@ <path>firewall ipv6 forward filter rule</path> </completionHelp> </properties> - <command>journalctl --no-hostname --boot -k | egrep "\[ipv6-FWD-filter-$8-[ADRJC]\]"</command> + <command>journalctl --no-hostname --boot -k | egrep "\[ipv6-FWD-filter-$8-[ADRJCO]\]"</command> </tagNode> </children> </node> diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py index ff078649d..28ccee769 100644 --- a/python/vyos/config_mgmt.py +++ b/python/vyos/config_mgmt.py @@ -132,6 +132,9 @@ class ConfigMgmt: {}).get('source_address', '') if config.exists(['system', 'host-name']): self.hostname = config.return_value(['system', 'host-name']) + if config.exists(['system', 'domain-name']): + tmp = config.return_value(['system', 'domain-name']) + self.hostname += f'.{tmp}' else: self.hostname = 'vyos' diff --git a/python/vyos/configdep.py b/python/vyos/configdep.py index 64727d355..73bd9ea96 100644 --- a/python/vyos/configdep.py +++ b/python/vyos/configdep.py @@ -1,4 +1,4 @@ -# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -33,7 +33,14 @@ if typing.TYPE_CHECKING: dependency_dir = os.path.join(directories['data'], 'config-mode-dependencies') -dependent_func: dict[str, list[typing.Callable]] = {} +local_dependent_func: dict[str, list[typing.Callable]] = {} + +DEBUG = False +FORCE_LOCAL = False + +def debug_print(s: str): + if DEBUG: + print(s) def canon_name(name: str) -> str: return os.path.splitext(name)[0].replace('-', '_') @@ -45,6 +52,26 @@ def canon_name_of_path(path: str) -> str: def caller_name() -> str: return stack()[2].filename +def name_of(f: typing.Callable) -> str: + return f.__name__ + +def names_of(l: list[typing.Callable]) -> list[str]: + return [name_of(f) for f in l] + +def remove_redundant(l: list[typing.Callable]) -> list[typing.Callable]: + names = set() + for e in reversed(l): + _ = l.remove(e) if name_of(e) in names else names.add(name_of(e)) + +def append_uniq(l: list[typing.Callable], e: typing.Callable): + """Append an element, removing earlier occurrences + + The list of dependencies is generally short and traversing the list on + each append is preferable to the cost of redundant script invocation. + """ + l.append(e) + remove_redundant(l) + def read_dependency_dict(dependency_dir: str = dependency_dir) -> dict: res = {} for dep_file in os.listdir(dependency_dir): @@ -95,16 +122,30 @@ def set_dependents(case: str, config: 'Config', tagnode: typing.Optional[str] = None): d = get_dependency_dict(config) k = canon_name_of_path(caller_name()) - l = dependent_func.setdefault(k, []) + tag_ext = f'_{tagnode}' if tagnode is not None else '' + if hasattr(config, 'dependent_func') and not FORCE_LOCAL: + dependent_func = getattr(config, 'dependent_func') + l = dependent_func.setdefault('vyos_configd', []) + else: + dependent_func = local_dependent_func + l = dependent_func.setdefault(k, []) for target in d[k][case]: func = def_closure(target, config, tagnode) - l.append(func) + func.__name__ = f'{target}{tag_ext}' + append_uniq(l, func) + debug_print(f'set_dependents: caller {k}, dependents {names_of(l)}') -def call_dependents(): +def call_dependents(dependent_func: dict = None): k = canon_name_of_path(caller_name()) - l = dependent_func.get(k, []) + if dependent_func is None or FORCE_LOCAL: + dependent_func = local_dependent_func + l = dependent_func.get(k, []) + else: + l = dependent_func.get('vyos_configd', []) + debug_print(f'call_dependents: caller {k}, dependents {names_of(l)}') while l: f = l.pop(0) + debug_print(f'calling: {f.__name__}') f() def called_as_dependent() -> bool: diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index ba638b280..473c98d0c 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -1,4 +1,4 @@ -# Copyright 2021-2023 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -16,6 +16,7 @@ import os import re +from json import loads from vyos.utils.process import popen # These drivers do not support using ethtool to change the speed, duplex, or @@ -31,16 +32,24 @@ class Ethtool: """ # dictionary containing driver featurs, it will be populated on demand and # the content will look like: - # { - # 'tls-hw-tx-offload': {'fixed': True, 'enabled': False}, - # 'tx-checksum-fcoe-crc': {'fixed': True, 'enabled': False}, - # 'tx-checksum-ip-generic': {'fixed': False, 'enabled': True}, - # 'tx-checksum-ipv4': {'fixed': True, 'enabled': False}, - # 'tx-checksum-ipv6': {'fixed': True, 'enabled': False}, - # 'tx-checksum-sctp': {'fixed': True, 'enabled': False}, - # 'tx-checksumming': {'fixed': False, 'enabled': True}, - # 'tx-esp-segmentation': {'fixed': True, 'enabled': False}, - # } + # [{'esp-hw-offload': {'active': False, 'fixed': True, 'requested': False}, + # 'esp-tx-csum-hw-offload': {'active': False, + # 'fixed': True, + # 'requested': False}, + # 'fcoe-mtu': {'active': False, 'fixed': True, 'requested': False}, + # 'generic-receive-offload': {'active': True, + # 'fixed': False, + # 'requested': True}, + # 'generic-segmentation-offload': {'active': True, + # 'fixed': False, + # 'requested': True}, + # 'highdma': {'active': True, 'fixed': False, 'requested': True}, + # 'ifname': 'eth0', + # 'l2-fwd-offload': {'active': False, 'fixed': True, 'requested': False}, + # 'large-receive-offload': {'active': False, + # 'fixed': False, + # 'requested': False}, + # ... _features = { } # dictionary containing available interface speed and duplex settings # { @@ -49,13 +58,11 @@ class Ethtool: # '1000': {'full': ''} # } _speed_duplex = {'auto': {'auto': ''}} - _ring_buffers = { } - _ring_buffers_max = { } + _ring_buffer = None _driver_name = None _auto_negotiation = False _auto_negotiation_supported = None - _flow_control = False - _flow_control_enabled = None + _flow_control = None _eee = False _eee_enabled = None @@ -97,51 +104,19 @@ class Ethtool: tmp = line.split()[-1] self._auto_negotiation = bool(tmp == 'on') - # Now populate features dictionaty - out, _ = popen(f'ethtool --show-features {ifname}') - # skip the first line, it only says: "Features for eth0": - for line in out.splitlines()[1:]: - if ":" in line: - key, value = [s.strip() for s in line.strip().split(":", 1)] - fixed = bool('fixed' in value) - if fixed: - value = value.split()[0].strip() - self._features[key.strip()] = { - 'enabled' : bool(value == 'on'), - 'fixed' : fixed - } - - out, _ = popen(f'ethtool --show-ring {ifname}') - # We are only interested in line 2-5 which contains the device maximum - # ringbuffers - for line in out.splitlines()[2:6]: - if ':' in line: - key, value = [s.strip() for s in line.strip().split(":", 1)] - key = key.lower().replace(' ', '_') - # T3645: ethtool version used on Debian Bullseye changed the - # output format from 0 -> n/a. As we are only interested in the - # tx/rx keys we do not care about RX Mini/Jumbo. - if value.isdigit(): - self._ring_buffers_max[key] = value - # Now we wan't to get the current RX/TX ringbuffer values - used for - for line in out.splitlines()[7:11]: - if ':' in line: - key, value = [s.strip() for s in line.strip().split(":", 1)] - key = key.lower().replace(' ', '_') - # T3645: ethtool version used on Debian Bullseye changed the - # output format from 0 -> n/a. As we are only interested in the - # tx/rx keys we do not care about RX Mini/Jumbo. - if value.isdigit(): - self._ring_buffers[key] = value + # Now populate driver features + out, _ = popen(f'ethtool --json --show-features {ifname}') + self._features = loads(out) + + # Get information about NIC ring buffers + out, _ = popen(f'ethtool --json --show-ring {ifname}') + self._ring_buffer = loads(out) # Get current flow control settings, but this is not supported by # all NICs (e.g. vmxnet3 does not support is) - out, _ = popen(f'ethtool --show-pause {ifname}') - if len(out.splitlines()) > 1: - self._flow_control = True - # read current flow control setting, this returns: - # ['Autonegotiate:', 'on'] - self._flow_control_enabled = out.splitlines()[1].split()[-1] + out, err = popen(f'ethtool --json --show-pause {ifname}') + if not bool(err): + self._flow_control = loads(out) # Get current Energy Efficient Ethernet (EEE) settings, but this is # not supported by all NICs (e.g. vmxnet3 does not support is) @@ -150,7 +125,7 @@ class Ethtool: self._eee = True # read current EEE setting, this returns: # EEE status: disabled || EEE status: enabled - inactive || EEE status: enabled - active - self._eee_enabled = bool('enabled' in out.splitlines()[2]) + self._eee_enabled = bool('enabled' in out.splitlines()[1]) def check_auto_negotiation_supported(self): """ Check if the NIC supports changing auto-negotiation """ @@ -169,14 +144,12 @@ class Ethtool: In case of a missing key, return "fixed = True and enabled = False" """ + active = False fixed = True - enabled = False - if feature in self._features: - if 'enabled' in self._features[feature]: - enabled = self._features[feature]['enabled'] - if 'fixed' in self._features[feature]: - fixed = self._features[feature]['fixed'] - return enabled, fixed + if feature in self._features[0]: + active = bool(self._features[0][feature]['active']) + fixed = bool(self._features[0][feature]['fixed']) + return active, fixed def get_generic_receive_offload(self): return self._get_generic('generic-receive-offload') @@ -201,14 +174,14 @@ class Ethtool: # thus when it's impossible return None if rx_tx not in ['rx', 'tx']: ValueError('Ring-buffer type must be either "rx" or "tx"') - return self._ring_buffers_max.get(rx_tx, None) + return str(self._ring_buffer[0].get(f'{rx_tx}-max', None)) def get_ring_buffer(self, rx_tx): # Configuration of RX/TX ring-buffers is not supported on every device, # thus when it's impossible return None if rx_tx not in ['rx', 'tx']: ValueError('Ring-buffer type must be either "rx" or "tx"') - return str(self._ring_buffers.get(rx_tx, None)) + return str(self._ring_buffer[0].get(rx_tx, None)) def check_speed_duplex(self, speed, duplex): """ Check if the passed speed and duplex combination is supported by @@ -230,15 +203,14 @@ class Ethtool: def check_flow_control(self): """ Check if the NIC supports flow-control """ - if self.get_driver_name() in _drivers_without_speed_duplex_flow: - return False - return self._flow_control + return bool(self._flow_control) def get_flow_control(self): - if self._flow_control_enabled == None: + if self._flow_control == None: raise ValueError('Interface does not support changing '\ 'flow-control settings!') - return self._flow_control_enabled + + return 'on' if bool(self._flow_control[0]['autonegotiate']) else 'off' def check_eee(self): """ Check if the NIC supports eee """ diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index eee11bd2d..e70b4f0d9 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -34,6 +34,24 @@ from vyos.utils.process import call from vyos.utils.process import cmd from vyos.utils.process import run +# Conntrack + +def conntrack_required(conf): + required_nodes = ['nat', 'nat66', 'load-balancing wan'] + + for path in required_nodes: + if conf.exists(path): + return True + + firewall = conf.get_config_dict(['firewall'], key_mangling=('-', '_'), + no_tag_node_value_mangle=True, get_first_key=True) + + for rules, path in dict_search_recursive(firewall, 'rule'): + if any(('state' in rule_conf or 'connection_status' in rule_conf or 'offload_target' in rule_conf) for rule_conf in rules.values()): + return True + + return False + # Domain Resolver def fqdn_config_parse(firewall): @@ -118,10 +136,10 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): if 'connection_status' in rule_conf and rule_conf['connection_status']: status = rule_conf['connection_status'] if status['nat'] == 'destination': - nat_status = '{dnat}' + nat_status = 'dnat' output.append(f'ct status {nat_status}') if status['nat'] == 'source': - nat_status = '{snat}' + nat_status = 'snat' output.append(f'ct status {nat_status}') if 'protocol' in rule_conf and rule_conf['protocol'] != 'all': diff --git a/python/vyos/kea.py b/python/vyos/kea.py index 894ac9e9a..89ae7ca81 100644 --- a/python/vyos/kea.py +++ b/python/vyos/kea.py @@ -56,6 +56,8 @@ kea6_options = { 'captive_portal': 'v6-captive-portal' } +kea_ctrl_socket = '/run/kea/dhcp{inet}-ctrl-socket' + def kea_parse_options(config): options = [] @@ -113,6 +115,9 @@ def kea_parse_subnet(subnet, config): if 'bootfile_server' in config['option']: out['next-server'] = config['option']['bootfile_server'] + if 'ignore_client_id' in config: + out['match-client-id'] = False + if 'lease' in config: out['valid-lifetime'] = int(config['lease']) out['max-valid-lifetime'] = int(config['lease']) @@ -291,7 +296,9 @@ def kea6_parse_subnet(subnet, config): return out -def _ctrl_socket_command(path, command, args=None): +def _ctrl_socket_command(inet, command, args=None): + path = kea_ctrl_socket.format(inet=inet) + if not os.path.exists(path): return None @@ -316,19 +323,25 @@ def _ctrl_socket_command(path, command, args=None): return json.loads(result.decode('utf-8')) def kea_get_leases(inet): - ctrl_socket = f'/run/kea/dhcp{inet}-ctrl-socket' - - leases = _ctrl_socket_command(ctrl_socket, f'lease{inet}-get-all') + leases = _ctrl_socket_command(inet, f'lease{inet}-get-all') if not leases or 'result' not in leases or leases['result'] != 0: return [] return leases['arguments']['leases'] -def kea_get_active_config(inet): - ctrl_socket = f'/run/kea/dhcp{inet}-ctrl-socket' +def kea_delete_lease(inet, ip_address): + args = {'ip-address': ip_address} - config = _ctrl_socket_command(ctrl_socket, 'config-get') + result = _ctrl_socket_command(inet, f'lease{inet}-del', args) + + if result and 'result' in result: + return result['result'] == 0 + + return False + +def kea_get_active_config(inet): + config = _ctrl_socket_command(inet, 'config-get') if not config or 'result' not in config or config['result'] != 0: return None diff --git a/python/vyos/nat.py b/python/vyos/nat.py index 7215aac88..da2613b16 100644 --- a/python/vyos/nat.py +++ b/python/vyos/nat.py @@ -89,11 +89,14 @@ def parse_nat_rule(rule_conf, rule_id, nat_type, ipv6=False): if addr and is_ip_network(addr): if not ipv6: map_addr = dict_search_args(rule_conf, nat_type, 'address') - if port: - translation_output.append(f'{ip_prefix} prefix to {ip_prefix} {translation_prefix}addr map {{ {map_addr} : {addr} . {port} }}') + if map_addr: + if port: + translation_output.append(f'{ip_prefix} prefix to {ip_prefix} {translation_prefix}addr map {{ {map_addr} : {addr} . {port} }}') + else: + translation_output.append(f'{ip_prefix} prefix to {ip_prefix} {translation_prefix}addr map {{ {map_addr} : {addr} }}') + ignore_type_addr = True else: - translation_output.append(f'{ip_prefix} prefix to {ip_prefix} {translation_prefix}addr map {{ {map_addr} : {addr} }}') - ignore_type_addr = True + translation_output.append(f'prefix to {addr}') else: translation_output.append(f'prefix to {addr}') elif addr == 'masquerade': diff --git a/python/vyos/qos/base.py b/python/vyos/qos/base.py index a22039e52..47318122b 100644 --- a/python/vyos/qos/base.py +++ b/python/vyos/qos/base.py @@ -129,16 +129,13 @@ class QoSBase: if tmp: default_tc += f' flows {tmp}' tmp = dict_search('interval', config) - if tmp: default_tc += f' interval {tmp}' - - tmp = dict_search('interval', config) - if tmp: default_tc += f' interval {tmp}' + if tmp: default_tc += f' interval {tmp}ms' tmp = dict_search('queue_limit', config) if tmp: default_tc += f' limit {tmp}' tmp = dict_search('target', config) - if tmp: default_tc += f' target {tmp}' + if tmp: default_tc += f' target {tmp}ms' default_tc += f' noecn' @@ -331,15 +328,6 @@ class QoSBase: filter_cmd += f' flowid {self._parent:x}:{cls:x}' self._cmd(filter_cmd) - else: - - filter_cmd += ' basic' - - cls = int(cls) - filter_cmd += f' flowid {self._parent:x}:{cls:x}' - self._cmd(filter_cmd) - - # The police block allows limiting of the byte or packet rate of # traffic matched by the filter it is attached to. # https://man7.org/linux/man-pages/man8/tc-police.8.html diff --git a/python/vyos/remote.py b/python/vyos/remote.py index 129e65772..d87fd24f6 100644 --- a/python/vyos/remote.py +++ b/python/vyos/remote.py @@ -43,6 +43,7 @@ from vyos.utils.io import print_error from vyos.utils.misc import begin from vyos.utils.process import cmd, rc_cmd from vyos.version import get_version +from vyos.base import Warning CHUNK_SIZE = 8192 @@ -54,13 +55,16 @@ class InteractivePolicy(MissingHostKeyPolicy): def missing_host_key(self, client, hostname, key): print_error(f"Host '{hostname}' not found in known hosts.") print_error('Fingerprint: ' + key.get_fingerprint().hex()) - if sys.stdin.isatty() and ask_yes_no('Do you wish to continue?'): - if client._host_keys_filename\ - and ask_yes_no('Do you wish to permanently add this host/key pair to known hosts?'): - client._host_keys.add(hostname, key.get_name(), key) - client.save_host_keys(client._host_keys_filename) - else: + if not sys.stdin.isatty(): + return + if not ask_yes_no('Do you wish to continue?'): raise SSHException(f"Cannot connect to unknown host '{hostname}'.") + if client._host_keys_filename is None: + Warning('no \'known_hosts\' file; create to store keys permanently') + return + if ask_yes_no('Do you wish to permanently add this host/key pair to known_hosts file?'): + client._host_keys.add(hostname, key.get_name(), key) + client.save_host_keys(client._host_keys_filename) class SourceAdapter(HTTPAdapter): """ diff --git a/python/vyos/system/grub.py b/python/vyos/system/grub.py index 2e8b20972..864ed65aa 100644 --- a/python/vyos/system/grub.py +++ b/python/vyos/system/grub.py @@ -17,6 +17,7 @@ import platform from pathlib import Path from re import MULTILINE, compile as re_compile +from shutil import copy2 from typing import Union from uuid import uuid5, NAMESPACE_URL, UUID @@ -422,3 +423,38 @@ def set_kernel_cmdline_options(cmdline_options: str, version_name: str, version_add(version_name=version_name, root_dir=root_dir, boot_opts_config=cmdline_options) + + +def sort_inodes(dir_path: str) -> None: + """Sort inodes for files inside a folder + Regenerate inodes for each file to get the same order for both inodes + and file names + + GRUB iterates files by inodes, not alphabetically. Therefore, if we + want to read them in proper order, we need to sort inodes for all + config files in a folder. + + Args: + dir_path (str): a path to directory + """ + dir_content: list[Path] = sorted(Path(dir_path).iterdir()) + temp_list_old: list[Path] = [] + temp_list_new: list[Path] = [] + + # create a copy of all files, to get new inodes + for item in dir_content: + # skip directories + if item.is_dir(): + continue + # create a new copy of file with a temporary name + copy_path = Path(f'{item.as_posix()}_tmp') + copy2(item, Path(copy_path)) + temp_list_old.append(item) + temp_list_new.append(copy_path) + + # delete old files and rename new ones + for item in temp_list_old: + item.unlink() + for item in temp_list_new: + new_name = Path(f'{item.as_posix()[0:-4]}') + item.rename(new_name) diff --git a/python/vyos/tpm.py b/python/vyos/tpm.py new file mode 100644 index 000000000..f120e10c4 --- /dev/null +++ b/python/vyos/tpm.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import tempfile + +from vyos.util import rc_cmd + +default_pcrs = ['0','2','4','7'] +tpm_handle = 0x81000000 + +def init_tpm(clear=False): + """ + Initialize TPM + """ + code, output = rc_cmd('tpm2_startup' + (' -c' if clear else '')) + if code != 0: + raise Exception('init_tpm: Failed to initialize TPM') + +def clear_tpm_key(): + """ + Clear existing key on TPM + """ + code, output = rc_cmd(f'tpm2_evictcontrol -C o -c {tpm_handle}') + if code != 0: + raise Exception('clear_tpm_key: Failed to clear TPM key') + +def read_tpm_key(index=0, pcrs=default_pcrs): + """ + Read existing key on TPM + """ + with tempfile.TemporaryDirectory() as tpm_dir: + pcr_str = ",".join(pcrs) + + tpm_key_file = os.path.join(tpm_dir, 'tpm_key.key') + code, output = rc_cmd(f'tpm2_unseal -c {tpm_handle + index} -p pcr:sha256:{pcr_str} -o {tpm_key_file}') + if code != 0: + raise Exception('read_tpm_key: Failed to read key from TPM') + + with open(tpm_key_file, 'rb') as f: + tpm_key = f.read() + + return tpm_key + +def write_tpm_key(key, index=0, pcrs=default_pcrs): + """ + Saves key to TPM + """ + with tempfile.TemporaryDirectory() as tpm_dir: + pcr_str = ",".join(pcrs) + + policy_file = os.path.join(tpm_dir, 'policy.digest') + code, output = rc_cmd(f'tpm2_createpolicy --policy-pcr -l sha256:{pcr_str} -L {policy_file}') + if code != 0: + raise Exception('write_tpm_key: Failed to create policy digest') + + primary_context_file = os.path.join(tpm_dir, 'primary.ctx') + code, output = rc_cmd(f'tpm2_createprimary -C e -g sha256 -G rsa -c {primary_context_file}') + if code != 0: + raise Exception('write_tpm_key: Failed to create primary key') + + key_file = os.path.join(tpm_dir, 'crypt.key') + with open(key_file, 'wb') as f: + f.write(key) + + public_obj = os.path.join(tpm_dir, 'obj.pub') + private_obj = os.path.join(tpm_dir, 'obj.key') + code, output = rc_cmd( + f'tpm2_create -g sha256 \ + -u {public_obj} -r {private_obj} \ + -C {primary_context_file} -L {policy_file} -i {key_file}') + + if code != 0: + raise Exception('write_tpm_key: Failed to create object') + + load_context_file = os.path.join(tpm_dir, 'load.ctx') + code, output = rc_cmd(f'tpm2_load -C {primary_context_file} -u {public_obj} -r {private_obj} -c {load_context_file}') + + if code != 0: + raise Exception('write_tpm_key: Failed to load object') + + code, output = rc_cmd(f'tpm2_evictcontrol -c {load_context_file} -C o {tpm_handle + index}') + + if code != 0: + raise Exception('write_tpm_key: Failed to write object to TPM') diff --git a/smoketest/config-tests/basic-vyos b/smoketest/config-tests/basic-vyos index 0bb68b75d..d676c663d 100644 --- a/smoketest/config-tests/basic-vyos +++ b/smoketest/config-tests/basic-vyos @@ -1,13 +1,9 @@ set interfaces ethernet eth0 address '192.168.0.1/24' -set interfaces ethernet eth0 duplex 'auto' -set interfaces ethernet eth0 speed 'auto' -set interfaces ethernet eth1 duplex 'auto' -set interfaces ethernet eth1 speed 'auto' -set interfaces ethernet eth2 duplex 'auto' -set interfaces ethernet eth2 speed 'auto' +set interfaces ethernet eth0 address 'fe88::1/56' set interfaces ethernet eth2 vif 100 address '100.100.0.1/24' set interfaces ethernet eth2 vif-s 200 address '100.64.200.254/24' set interfaces ethernet eth2 vif-s 200 vif-c 201 address '100.64.201.254/24' +set interfaces ethernet eth2 vif-s 200 vif-c 201 address 'fe89::1/56' set interfaces ethernet eth2 vif-s 200 vif-c 202 address '100.64.202.254/24' set interfaces loopback lo set protocols static arp interface eth0 address 192.168.0.20 mac '00:50:00:00:00:20' @@ -23,18 +19,6 @@ set protocols static arp interface eth2.200.201 address 100.64.201.20 mac '00:50 set protocols static arp interface eth2.200.202 address 100.64.202.30 mac '00:50:00:00:00:30' set protocols static arp interface eth2.200.202 address 100.64.202.40 mac '00:50:00:00:00:40' set protocols static route 0.0.0.0/0 next-hop 100.64.0.1 -set service dhcp-server shared-network-name LAN authoritative -set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 option default-router '192.168.0.1' -set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 option domain-name 'vyos.net' -set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 option domain-search 'vyos.net' -set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 option name-server '192.168.0.1' -set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 range LANDynamic start '192.168.0.20' -set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 range LANDynamic stop '192.168.0.240' -set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 subnet-id '1' -set service dns forwarding allow-from '192.168.0.0/16' -set service dns forwarding cache-size '10000' -set service dns forwarding dnssec 'off' -set service dns forwarding listen-address '192.168.0.1' set service ssh ciphers 'aes128-ctr' set service ssh ciphers 'aes192-ctr' set service ssh ciphers 'aes256-ctr' @@ -46,18 +30,55 @@ set service ssh key-exchange 'diffie-hellman-group-exchange-sha1' set service ssh key-exchange 'diffie-hellman-group-exchange-sha256' set service ssh listen-address '192.168.0.1' set service ssh port '22' +set service dhcp-server shared-network-name LAN authoritative +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 option default-router '192.168.0.1' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 option domain-name 'vyos.net' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 option domain-search 'vyos.net' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 option name-server '192.168.0.1' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 range LANDynamic start '192.168.0.30' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 range LANDynamic stop '192.168.0.240' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping TEST1-1 ip-address '192.168.0.11' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping TEST1-1 mac '00:01:02:03:04:05' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping TEST1-2 disable +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping TEST1-2 ip-address '192.168.0.12' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping TEST1-2 mac '00:01:02:03:04:05' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping TEST2-1 ip-address '192.168.0.21' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping TEST2-1 mac '00:01:02:03:04:21' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping TEST2-2 disable +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping TEST2-2 ip-address '192.168.0.21' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping TEST2-2 mac '00:01:02:03:04:22' +set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 subnet-id '1' +set service dhcpv6-server shared-network-name LAN6 subnet fe88::/56 interface 'eth0' +set service dhcpv6-server shared-network-name LAN6 subnet fe88::/56 option domain-search 'vyos.net' +set service dhcpv6-server shared-network-name LAN6 subnet fe88::/56 option name-server 'fe88::1' +set service dhcpv6-server shared-network-name LAN6 subnet fe88::/56 range 1 prefix 'fe88::/60' +set service dhcpv6-server shared-network-name LAN6 subnet fe88::/56 range 2 start 'fe88:0000:0000:fe::' +set service dhcpv6-server shared-network-name LAN6 subnet fe88::/56 range 2 stop 'fe88:0000:0000:ff::' +set service dhcpv6-server shared-network-name LAN6 subnet fe88::/56 subnet-id '1' +set service dhcpv6-server shared-network-name LAN6 subnet fe89::/56 interface 'eth2.200.201' +set service dhcpv6-server shared-network-name LAN6 subnet fe89::/56 option domain-search 'vyos.net' +set service dhcpv6-server shared-network-name LAN6 subnet fe89::/56 option name-server 'fe89::1' +set service dhcpv6-server shared-network-name LAN6 subnet fe89::/56 range 1 prefix 'fe89::/60' +set service dhcpv6-server shared-network-name LAN6 subnet fe89::/56 range 2 start 'fe89:0000:0000:fe::' +set service dhcpv6-server shared-network-name LAN6 subnet fe89::/56 range 2 stop 'fe89:0000:0000:ff::' +set service dhcpv6-server shared-network-name LAN6 subnet fe89::/56 subnet-id '2' +set service dns forwarding allow-from '192.168.0.0/16' +set service dns forwarding cache-size '10000' +set service dns forwarding dnssec 'off' +set service dns forwarding listen-address '192.168.0.1' set system config-management commit-revisions '100' -set system console device ttyS0 speed '115200' +set system conntrack ignore ipv4 rule 1 destination address '192.0.2.2' +set system conntrack ignore ipv4 rule 1 source address '192.0.2.1' set system host-name 'vyos' +set system login user vyos authentication encrypted-password '$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0' +set system login user vyos authentication plaintext-password '' set system name-server '192.168.0.1' -set system syslog console facility all level 'emerg' -set system syslog console facility mail level 'info' -set system syslog global facility all level 'info' set system syslog global facility auth level 'info' -set system syslog global facility local7 level 'debug' set system syslog global preserve-fqdn +set system syslog console facility all level 'emerg' +set system syslog console facility mail level 'info' set system syslog host syslog.vyos.net facility auth level 'warning' set system syslog host syslog.vyos.net facility local7 level 'notice' set system syslog host syslog.vyos.net format octet-counted set system syslog host syslog.vyos.net port '8000' -set system time-zone 'Europe/Berlin' +set system console device ttyS0 speed '115200' diff --git a/smoketest/config-tests/dialup-router-wireguard-ipv6 b/smoketest/config-tests/dialup-router-wireguard-ipv6 new file mode 100644 index 000000000..c054b4650 --- /dev/null +++ b/smoketest/config-tests/dialup-router-wireguard-ipv6 @@ -0,0 +1,674 @@ +set interfaces dummy dum0 address '172.16.254.30/32' +set interfaces ethernet eth0 vif 10 address '172.16.33.254/24' +set interfaces ethernet eth0 vif 10 address '172.16.40.254/24' +set interfaces ethernet eth0 vif 5 address '172.16.37.254/24' +set interfaces ethernet eth0 vif 50 address '172.16.36.254/24' +set interfaces ethernet eth0 ring-buffer rx '256' +set interfaces ethernet eth0 ring-buffer tx '256' +set interfaces ethernet eth1 offload gro +set interfaces ethernet eth1 offload gso +set interfaces ethernet eth1 offload sg +set interfaces ethernet eth1 offload tso +set interfaces ethernet eth1 vif 20 address '172.31.0.254/24' +set interfaces ethernet eth2 disable +set interfaces ethernet eth2 offload gro +set interfaces ethernet eth2 offload gso +set interfaces ethernet eth2 offload sg +set interfaces ethernet eth2 offload tso +set interfaces ethernet eth3 offload gro +set interfaces ethernet eth3 offload gso +set interfaces ethernet eth3 offload sg +set interfaces ethernet eth3 offload tso +set interfaces ethernet eth3 ring-buffer rx '256' +set interfaces ethernet eth3 ring-buffer tx '256' +set interfaces ethernet eth3 vif 7 +set interfaces loopback lo address '172.16.254.30/32' +set interfaces pppoe pppoe0 authentication password 'vyos' +set interfaces pppoe pppoe0 authentication username 'vyos' +set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth0.10 address '1' +set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth0.10 sla-id '10' +set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth1.20 address '1' +set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth1.20 sla-id '20' +set interfaces pppoe pppoe0 dhcpv6-options pd 0 length '56' +set interfaces pppoe pppoe0 ip adjust-mss '1452' +set interfaces pppoe pppoe0 ipv6 address autoconf +set interfaces pppoe pppoe0 ipv6 adjust-mss '1432' +set interfaces pppoe pppoe0 no-peer-dns +set interfaces pppoe pppoe0 source-interface 'eth3.7' +set interfaces wireguard wg100 address '172.16.252.128/31' +set interfaces wireguard wg100 mtu '1500' +set interfaces wireguard wg100 peer HR6 address '100.65.151.213' +set interfaces wireguard wg100 peer HR6 allowed-ips '0.0.0.0/0' +set interfaces wireguard wg100 peer HR6 port '10100' +set interfaces wireguard wg100 port '10100' +set interfaces wireguard wg200 address '172.16.252.130/31' +set interfaces wireguard wg200 mtu '1500' +set interfaces wireguard wg200 peer WH56 address '80.151.69.205' +set interfaces wireguard wg200 peer WH56 allowed-ips '0.0.0.0/0' +set interfaces wireguard wg200 peer WH56 port '10200' +set interfaces wireguard wg200 port '10200' +set interfaces wireguard wg666 address '172.29.0.1/31' +set interfaces wireguard wg666 mtu '1500' +set interfaces wireguard wg666 peer WH34 address '100.65.55.1' +set interfaces wireguard wg666 peer WH34 allowed-ips '0.0.0.0/0' +set interfaces wireguard wg666 peer WH34 port '10666' +set interfaces wireguard wg666 port '10666' +set protocols ospf area 0 network '172.16.37.0/24' +set protocols ospf area 0 network '172.16.254.30/32' +set protocols ospf area 0 network '172.18.202.0/24' +set protocols ospf area 0 network '172.18.203.0/24' +set protocols ospf area 0 network '172.18.204.0/24' +set protocols ospf interface eth0.5 authentication md5 key-id 10 md5-key 'ospf' +set protocols ospf interface eth0.5 dead-interval '40' +set protocols ospf interface eth0.5 hello-interval '10' +set protocols ospf interface eth0.5 passive disable +set protocols ospf interface eth0.5 priority '1' +set protocols ospf interface eth0.5 retransmit-interval '5' +set protocols ospf interface eth0.5 transmit-delay '1' +set protocols ospf log-adjacency-changes detail +set protocols ospf parameters router-id '172.16.254.30' +set protocols ospf default-information originate always +set protocols ospf default-information originate metric-type '2' +set protocols ospf redistribute connected metric-type '2' +set protocols ospf redistribute connected route-map 'MAP-OSPF-CONNECTED' +set protocols static route 10.0.0.0/8 blackhole distance '254' +set protocols static route 169.254.0.0/16 blackhole distance '254' +set protocols static route 172.16.0.0/12 blackhole distance '254' +set protocols static route 172.16.32.0/21 blackhole +set protocols static route 172.18.0.0/16 blackhole +set protocols static route 172.29.0.2/31 next-hop 172.29.0.0 +set protocols static route 192.168.0.0/16 blackhole distance '254' +set protocols static route 192.168.189.0/24 next-hop 172.29.0.0 +set protocols static route6 2000::/3 interface pppoe0 +set protocols bfd peer 172.16.252.129 +set protocols bfd peer 172.16.252.131 +set protocols bfd peer 172.18.254.201 +set protocols bgp address-family ipv4-unicast network 172.16.32.0/21 +set protocols bgp address-family ipv4-unicast network 172.16.100.0/24 +set protocols bgp address-family ipv4-unicast network 172.16.252.128/31 +set protocols bgp address-family ipv4-unicast network 172.16.252.130/31 +set protocols bgp address-family ipv4-unicast network 172.16.254.30/32 +set protocols bgp address-family ipv4-unicast network 172.18.0.0/16 +set protocols bgp neighbor 172.16.252.129 peer-group 'WIREGUARD' +set protocols bgp neighbor 172.16.252.131 peer-group 'WIREGUARD' +set protocols bgp neighbor 172.18.254.201 address-family ipv4-unicast nexthop-self +set protocols bgp neighbor 172.18.254.201 bfd +set protocols bgp neighbor 172.18.254.201 remote-as '64503' +set protocols bgp neighbor 172.18.254.201 update-source 'dum0' +set protocols bgp parameters log-neighbor-changes +set protocols bgp peer-group WIREGUARD address-family ipv4-unicast soft-reconfiguration inbound +set protocols bgp peer-group WIREGUARD bfd +set protocols bgp peer-group WIREGUARD remote-as 'external' +set protocols bgp system-as '64503' +set protocols bgp timers holdtime '30' +set protocols bgp timers keepalive '10' +set service lldp legacy-protocols cdp +set service lldp legacy-protocols edp +set service lldp legacy-protocols fdp +set service lldp legacy-protocols sonmp +set service lldp snmp +set service ntp allow-client address '172.16.0.0/12' +set service ntp server time1.vyos.net +set service ntp server time2.vyos.net +set service dhcp-server shared-network-name BACKBONE authoritative +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 lease '86400' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option default-router '172.16.37.254' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option domain-name 'vyos.net' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option domain-search 'vyos.net' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option name-server '172.16.254.30' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option ntp-server '172.16.254.30' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 range 0 start '172.16.37.120' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 range 0 stop '172.16.37.149' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP1 ip-address '172.16.37.231' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP1 mac '02:00:00:00:ee:18' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP2 ip-address '172.16.37.232' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP2 mac '02:00:00:00:52:84' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP3 ip-address '172.16.37.233' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP3 mac '02:00:00:00:51:c0' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP4 ip-address '172.16.37.234' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP4 mac '02:00:00:00:e6:fc' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP5 ip-address '172.16.37.235' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP5 mac '02:00:00:00:c3:50' +set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 subnet-id '1' +set service dhcp-server shared-network-name GUEST authoritative +set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 lease '86400' +set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option default-router '172.31.0.254' +set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option domain-name 'vyos.net' +set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option domain-search 'vyos.net' +set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option name-server '172.31.0.254' +set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 range 0 start '172.31.0.101' +set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 range 0 stop '172.31.0.199' +set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 subnet-id '2' +set service dhcp-server shared-network-name LAN authoritative +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 lease '86400' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option default-router '172.16.33.254' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option domain-name 'vyos.net' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option domain-search 'vyos.net' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option name-server '172.16.254.30' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option ntp-server '172.16.254.30' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 range 0 start '172.16.33.100' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 range 0 stop '172.16.33.189' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping four ip-address '172.16.33.214' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping four mac '02:00:00:00:c4:33' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping one ip-address '172.16.33.221' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping one mac '02:00:00:00:eb:a6' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping three ip-address '172.16.33.212' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping three mac '02:00:00:00:12:c7' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping two ip-address '172.16.33.211' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping two mac '02:00:00:00:58:90' +set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 subnet-id '3' +set service dns dynamic name service-vyos-pppoe0 address interface 'pppoe0' +set service dns dynamic name service-vyos-pppoe0 host-name 'r1.vyos.net' +set service dns dynamic name service-vyos-pppoe0 password 'vyos' +set service dns dynamic name service-vyos-pppoe0 protocol 'dyndns2' +set service dns dynamic name service-vyos-pppoe0 server 'dyndns.vyos.io' +set service dns dynamic name service-vyos-pppoe0 username 'vyos-vyos' +set service dns forwarding allow-from '172.16.0.0/12' +set service dns forwarding domain 16.172.in-addr.arpa addnta +set service dns forwarding domain 16.172.in-addr.arpa name-server 172.16.100.10 +set service dns forwarding domain 16.172.in-addr.arpa name-server 172.16.100.20 +set service dns forwarding domain 16.172.in-addr.arpa recursion-desired +set service dns forwarding domain 18.172.in-addr.arpa addnta +set service dns forwarding domain 18.172.in-addr.arpa name-server 172.16.100.10 +set service dns forwarding domain 18.172.in-addr.arpa name-server 172.16.100.20 +set service dns forwarding domain 18.172.in-addr.arpa recursion-desired +set service dns forwarding domain vyos.net addnta +set service dns forwarding domain vyos.net name-server 172.16.100.10 +set service dns forwarding domain vyos.net name-server 172.16.100.20 +set service dns forwarding domain vyos.net recursion-desired +set service dns forwarding ignore-hosts-file +set service dns forwarding listen-address '172.16.254.30' +set service dns forwarding listen-address '172.31.0.254' +set service dns forwarding negative-ttl '60' +set service router-advert interface eth0.10 prefix ::/64 preferred-lifetime '2700' +set service router-advert interface eth0.10 prefix ::/64 valid-lifetime '5400' +set service router-advert interface eth1.20 prefix ::/64 preferred-lifetime '2700' +set service router-advert interface eth1.20 prefix ::/64 valid-lifetime '5400' +set service snmp community ro-community authorization 'ro' +set service snmp community ro-community network '172.16.100.0/24' +set service snmp contact 'VyOS' +set service snmp listen-address 172.16.254.30 port '161' +set service snmp location 'CLOUD' +set system conntrack expect-table-size '2048' +set system conntrack hash-size '32768' +set system conntrack table-size '262144' +set system conntrack timeout icmp '30' +set system conntrack timeout other '600' +set system conntrack timeout udp other '300' +set system conntrack timeout udp stream '300' +set system domain-name 'vyos.net' +set system host-name 'r1' +set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/' +set system login user vyos authentication plaintext-password '' +set system option ctrl-alt-delete 'ignore' +set system option performance 'latency' +set system option startup-beep +set system syslog global facility all level 'debug' +set system syslog host 172.16.100.1 facility all level 'warning' +set system console device ttyS0 speed '115200' +set firewall global-options all-ping 'enable' +set firewall global-options broadcast-ping 'disable' +set firewall global-options ip-src-route 'disable' +set firewall global-options ipv6-receive-redirects 'disable' +set firewall global-options ipv6-src-route 'disable' +set firewall global-options log-martians 'enable' +set firewall global-options receive-redirects 'disable' +set firewall global-options send-redirects 'enable' +set firewall global-options source-validation 'disable' +set firewall global-options syn-cookies 'enable' +set firewall global-options twa-hazards-protection 'disable' +set firewall group address-group DMZ-RDP-SERVER address '172.16.33.40' +set firewall group address-group DMZ-RDP-SERVER description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall group address-group DMZ-WEBSERVER address '172.16.36.10' +set firewall group address-group DMZ-WEBSERVER address '172.16.36.40' +set firewall group address-group DMZ-WEBSERVER address '172.16.36.20' +set firewall group address-group DMZ-WEBSERVER description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall group address-group DOMAIN-CONTROLLER address '172.16.100.10' +set firewall group address-group DOMAIN-CONTROLLER address '172.16.100.20' +set firewall group address-group DOMAIN-CONTROLLER address '172.16.110.30' +set firewall group address-group VIDEO address '172.16.33.211' +set firewall group address-group VIDEO address '172.16.33.212' +set firewall group address-group VIDEO address '172.16.33.213' +set firewall group address-group VIDEO address '172.16.33.214' +set firewall group ipv6-network-group LOCAL-ADDRESSES network 'ff02::/64' +set firewall group ipv6-network-group LOCAL-ADDRESSES network 'fe80::/10' +set firewall group network-group SSH-IN-ALLOW network '100.65.150.0/23' +set firewall group network-group SSH-IN-ALLOW network '100.64.69.205/32' +set firewall group network-group SSH-IN-ALLOW network '100.64.8.67/32' +set firewall group network-group SSH-IN-ALLOW network '100.64.55.1/32' +set firewall ipv4 name DMZ-GUEST default-action 'drop' +set firewall ipv4 name DMZ-GUEST default-log +set firewall ipv4 name DMZ-GUEST rule 1 action 'return' +set firewall ipv4 name DMZ-GUEST rule 1 state 'established' +set firewall ipv4 name DMZ-GUEST rule 1 state 'related' +set firewall ipv4 name DMZ-GUEST rule 2 action 'drop' +set firewall ipv4 name DMZ-GUEST rule 2 log +set firewall ipv4 name DMZ-GUEST rule 2 state 'invalid' +set firewall ipv4 name DMZ-LAN default-action 'drop' +set firewall ipv4 name DMZ-LAN default-log +set firewall ipv4 name DMZ-LAN description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv4 name DMZ-LAN rule 1 action 'return' +set firewall ipv4 name DMZ-LAN rule 1 description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv4 name DMZ-LAN rule 1 state 'established' +set firewall ipv4 name DMZ-LAN rule 1 state 'related' +set firewall ipv4 name DMZ-LAN rule 2 action 'drop' +set firewall ipv4 name DMZ-LAN rule 2 description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv4 name DMZ-LAN rule 2 log +set firewall ipv4 name DMZ-LAN rule 2 state 'invalid' +set firewall ipv4 name DMZ-LAN rule 100 action 'return' +set firewall ipv4 name DMZ-LAN rule 100 description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv4 name DMZ-LAN rule 100 destination group address-group 'DOMAIN-CONTROLLER' +set firewall ipv4 name DMZ-LAN rule 100 destination port '123,389,636' +set firewall ipv4 name DMZ-LAN rule 100 protocol 'tcp_udp' +set firewall ipv4 name DMZ-LAN rule 300 action 'return' +set firewall ipv4 name DMZ-LAN rule 300 description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv4 name DMZ-LAN rule 300 destination group address-group 'DMZ-RDP-SERVER' +set firewall ipv4 name DMZ-LAN rule 300 destination port '3389' +set firewall ipv4 name DMZ-LAN rule 300 protocol 'tcp_udp' +set firewall ipv4 name DMZ-LAN rule 300 source address '172.16.36.20' +set firewall ipv4 name DMZ-LOCAL default-action 'drop' +set firewall ipv4 name DMZ-LOCAL default-log +set firewall ipv4 name DMZ-LOCAL description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv4 name DMZ-LOCAL rule 1 action 'return' +set firewall ipv4 name DMZ-LOCAL rule 1 state 'established' +set firewall ipv4 name DMZ-LOCAL rule 1 state 'related' +set firewall ipv4 name DMZ-LOCAL rule 2 action 'drop' +set firewall ipv4 name DMZ-LOCAL rule 2 log +set firewall ipv4 name DMZ-LOCAL rule 2 state 'invalid' +set firewall ipv4 name DMZ-LOCAL rule 50 action 'return' +set firewall ipv4 name DMZ-LOCAL rule 50 description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv4 name DMZ-LOCAL rule 50 destination address '172.16.254.30' +set firewall ipv4 name DMZ-LOCAL rule 50 destination port '53' +set firewall ipv4 name DMZ-LOCAL rule 50 protocol 'tcp_udp' +set firewall ipv4 name DMZ-LOCAL rule 123 action 'return' +set firewall ipv4 name DMZ-LOCAL rule 123 destination port '123' +set firewall ipv4 name DMZ-LOCAL rule 123 protocol 'udp' +set firewall ipv4 name DMZ-WAN default-action 'return' +set firewall ipv4 name DMZ-WAN description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv4 name GUEST-DMZ default-action 'drop' +set firewall ipv4 name GUEST-DMZ default-log +set firewall ipv4 name GUEST-DMZ rule 1 action 'return' +set firewall ipv4 name GUEST-DMZ rule 1 state 'established' +set firewall ipv4 name GUEST-DMZ rule 1 state 'related' +set firewall ipv4 name GUEST-DMZ rule 2 action 'drop' +set firewall ipv4 name GUEST-DMZ rule 2 log +set firewall ipv4 name GUEST-DMZ rule 2 state 'invalid' +set firewall ipv4 name GUEST-LAN default-action 'drop' +set firewall ipv4 name GUEST-LAN default-log +set firewall ipv4 name GUEST-LAN rule 1 action 'return' +set firewall ipv4 name GUEST-LAN rule 1 state 'established' +set firewall ipv4 name GUEST-LAN rule 1 state 'related' +set firewall ipv4 name GUEST-LAN rule 2 action 'drop' +set firewall ipv4 name GUEST-LAN rule 2 log +set firewall ipv4 name GUEST-LAN rule 2 state 'invalid' +set firewall ipv4 name GUEST-LOCAL default-action 'drop' +set firewall ipv4 name GUEST-LOCAL default-log +set firewall ipv4 name GUEST-LOCAL rule 1 action 'return' +set firewall ipv4 name GUEST-LOCAL rule 1 state 'established' +set firewall ipv4 name GUEST-LOCAL rule 1 state 'related' +set firewall ipv4 name GUEST-LOCAL rule 2 action 'drop' +set firewall ipv4 name GUEST-LOCAL rule 2 log +set firewall ipv4 name GUEST-LOCAL rule 2 state 'invalid' +set firewall ipv4 name GUEST-LOCAL rule 10 action 'return' +set firewall ipv4 name GUEST-LOCAL rule 10 destination address '172.31.0.254' +set firewall ipv4 name GUEST-LOCAL rule 10 destination port '53' +set firewall ipv4 name GUEST-LOCAL rule 10 protocol 'tcp_udp' +set firewall ipv4 name GUEST-LOCAL rule 11 action 'return' +set firewall ipv4 name GUEST-LOCAL rule 11 destination port '67' +set firewall ipv4 name GUEST-LOCAL rule 11 protocol 'udp' +set firewall ipv4 name GUEST-LOCAL rule 15 action 'return' +set firewall ipv4 name GUEST-LOCAL rule 15 destination address '172.31.0.254' +set firewall ipv4 name GUEST-LOCAL rule 15 protocol 'icmp' +set firewall ipv4 name GUEST-LOCAL rule 100 action 'return' +set firewall ipv4 name GUEST-LOCAL rule 100 destination address '172.31.0.254' +set firewall ipv4 name GUEST-LOCAL rule 100 destination port '80,443' +set firewall ipv4 name GUEST-LOCAL rule 100 protocol 'tcp' +set firewall ipv4 name GUEST-WAN default-action 'drop' +set firewall ipv4 name GUEST-WAN default-log +set firewall ipv4 name GUEST-WAN rule 1 action 'return' +set firewall ipv4 name GUEST-WAN rule 1 state 'established' +set firewall ipv4 name GUEST-WAN rule 1 state 'related' +set firewall ipv4 name GUEST-WAN rule 2 action 'drop' +set firewall ipv4 name GUEST-WAN rule 2 log +set firewall ipv4 name GUEST-WAN rule 2 state 'invalid' +set firewall ipv4 name GUEST-WAN rule 25 action 'return' +set firewall ipv4 name GUEST-WAN rule 25 destination port '25,587' +set firewall ipv4 name GUEST-WAN rule 25 protocol 'tcp' +set firewall ipv4 name GUEST-WAN rule 53 action 'return' +set firewall ipv4 name GUEST-WAN rule 53 destination port '53' +set firewall ipv4 name GUEST-WAN rule 53 protocol 'tcp_udp' +set firewall ipv4 name GUEST-WAN rule 60 action 'return' +set firewall ipv4 name GUEST-WAN rule 60 source address '172.31.0.200' +set firewall ipv4 name GUEST-WAN rule 80 action 'return' +set firewall ipv4 name GUEST-WAN rule 80 source address '172.31.0.200' +set firewall ipv4 name GUEST-WAN rule 100 action 'return' +set firewall ipv4 name GUEST-WAN rule 100 protocol 'icmp' +set firewall ipv4 name GUEST-WAN rule 110 action 'return' +set firewall ipv4 name GUEST-WAN rule 110 destination port '110,995' +set firewall ipv4 name GUEST-WAN rule 110 protocol 'tcp' +set firewall ipv4 name GUEST-WAN rule 123 action 'return' +set firewall ipv4 name GUEST-WAN rule 123 destination port '123' +set firewall ipv4 name GUEST-WAN rule 123 protocol 'udp' +set firewall ipv4 name GUEST-WAN rule 143 action 'return' +set firewall ipv4 name GUEST-WAN rule 143 destination port '143,993' +set firewall ipv4 name GUEST-WAN rule 143 protocol 'tcp' +set firewall ipv4 name GUEST-WAN rule 200 action 'return' +set firewall ipv4 name GUEST-WAN rule 200 destination port '80,443' +set firewall ipv4 name GUEST-WAN rule 200 protocol 'tcp' +set firewall ipv4 name GUEST-WAN rule 500 action 'return' +set firewall ipv4 name GUEST-WAN rule 500 destination port '500,4500' +set firewall ipv4 name GUEST-WAN rule 500 protocol 'udp' +set firewall ipv4 name GUEST-WAN rule 600 action 'return' +set firewall ipv4 name GUEST-WAN rule 600 destination port '5222-5224' +set firewall ipv4 name GUEST-WAN rule 600 protocol 'tcp' +set firewall ipv4 name GUEST-WAN rule 601 action 'return' +set firewall ipv4 name GUEST-WAN rule 601 destination port '3478-3497,4500,16384-16387,16393-16402' +set firewall ipv4 name GUEST-WAN rule 601 protocol 'udp' +set firewall ipv4 name GUEST-WAN rule 1000 action 'return' +set firewall ipv4 name GUEST-WAN rule 1000 source address '172.31.0.184' +set firewall ipv4 name LAN-DMZ default-action 'drop' +set firewall ipv4 name LAN-DMZ default-log +set firewall ipv4 name LAN-DMZ rule 1 action 'return' +set firewall ipv4 name LAN-DMZ rule 1 state 'established' +set firewall ipv4 name LAN-DMZ rule 1 state 'related' +set firewall ipv4 name LAN-DMZ rule 2 action 'drop' +set firewall ipv4 name LAN-DMZ rule 2 log +set firewall ipv4 name LAN-DMZ rule 2 state 'invalid' +set firewall ipv4 name LAN-DMZ rule 22 action 'return' +set firewall ipv4 name LAN-DMZ rule 22 destination port '22' +set firewall ipv4 name LAN-DMZ rule 22 protocol 'tcp' +set firewall ipv4 name LAN-DMZ rule 100 action 'return' +set firewall ipv4 name LAN-DMZ rule 100 destination group address-group 'DMZ-WEBSERVER' +set firewall ipv4 name LAN-DMZ rule 100 destination port '22' +set firewall ipv4 name LAN-DMZ rule 100 protocol 'tcp' +set firewall ipv4 name LAN-GUEST default-action 'drop' +set firewall ipv4 name LAN-GUEST default-log +set firewall ipv4 name LAN-GUEST rule 1 action 'return' +set firewall ipv4 name LAN-GUEST rule 1 state 'established' +set firewall ipv4 name LAN-GUEST rule 1 state 'related' +set firewall ipv4 name LAN-GUEST rule 2 action 'drop' +set firewall ipv4 name LAN-GUEST rule 2 log +set firewall ipv4 name LAN-GUEST rule 2 state 'invalid' +set firewall ipv4 name LAN-LOCAL default-action 'return' +set firewall ipv4 name LAN-WAN default-action 'return' +set firewall ipv4 name LAN-WAN rule 90 action 'return' +set firewall ipv4 name LAN-WAN rule 90 destination address '100.65.150.0/23' +set firewall ipv4 name LAN-WAN rule 90 destination port '25' +set firewall ipv4 name LAN-WAN rule 90 protocol 'tcp_udp' +set firewall ipv4 name LAN-WAN rule 90 source group address-group 'VIDEO' +set firewall ipv4 name LAN-WAN rule 100 action 'drop' +set firewall ipv4 name LAN-WAN rule 100 source group address-group 'VIDEO' +set firewall ipv4 name LOCAL-DMZ default-action 'drop' +set firewall ipv4 name LOCAL-DMZ default-log +set firewall ipv4 name LOCAL-DMZ rule 1 action 'return' +set firewall ipv4 name LOCAL-DMZ rule 1 state 'established' +set firewall ipv4 name LOCAL-DMZ rule 1 state 'related' +set firewall ipv4 name LOCAL-DMZ rule 2 action 'drop' +set firewall ipv4 name LOCAL-DMZ rule 2 log +set firewall ipv4 name LOCAL-DMZ rule 2 state 'invalid' +set firewall ipv4 name LOCAL-DMZ rule 100 action 'return' +set firewall ipv4 name LOCAL-DMZ rule 100 destination address '172.16.36.40' +set firewall ipv4 name LOCAL-DMZ rule 100 destination port '80,443' +set firewall ipv4 name LOCAL-DMZ rule 100 protocol 'tcp' +set firewall ipv4 name LOCAL-GUEST default-action 'drop' +set firewall ipv4 name LOCAL-GUEST default-log +set firewall ipv4 name LOCAL-GUEST rule 1 action 'return' +set firewall ipv4 name LOCAL-GUEST rule 1 state 'established' +set firewall ipv4 name LOCAL-GUEST rule 1 state 'related' +set firewall ipv4 name LOCAL-GUEST rule 2 action 'drop' +set firewall ipv4 name LOCAL-GUEST rule 2 log +set firewall ipv4 name LOCAL-GUEST rule 2 state 'invalid' +set firewall ipv4 name LOCAL-GUEST rule 5 action 'return' +set firewall ipv4 name LOCAL-GUEST rule 5 protocol 'icmp' +set firewall ipv4 name LOCAL-GUEST rule 300 action 'return' +set firewall ipv4 name LOCAL-GUEST rule 300 destination port '1900' +set firewall ipv4 name LOCAL-GUEST rule 300 protocol 'udp' +set firewall ipv4 name LOCAL-LAN default-action 'return' +set firewall ipv4 name LOCAL-WAN default-action 'drop' +set firewall ipv4 name LOCAL-WAN default-log +set firewall ipv4 name LOCAL-WAN rule 1 action 'return' +set firewall ipv4 name LOCAL-WAN rule 1 state 'established' +set firewall ipv4 name LOCAL-WAN rule 1 state 'related' +set firewall ipv4 name LOCAL-WAN rule 2 action 'drop' +set firewall ipv4 name LOCAL-WAN rule 2 log +set firewall ipv4 name LOCAL-WAN rule 2 state 'invalid' +set firewall ipv4 name LOCAL-WAN rule 10 action 'return' +set firewall ipv4 name LOCAL-WAN rule 10 protocol 'icmp' +set firewall ipv4 name LOCAL-WAN rule 50 action 'return' +set firewall ipv4 name LOCAL-WAN rule 50 destination port '53' +set firewall ipv4 name LOCAL-WAN rule 50 protocol 'tcp_udp' +set firewall ipv4 name LOCAL-WAN rule 80 action 'return' +set firewall ipv4 name LOCAL-WAN rule 80 destination port '80,443' +set firewall ipv4 name LOCAL-WAN rule 80 protocol 'tcp' +set firewall ipv4 name LOCAL-WAN rule 123 action 'return' +set firewall ipv4 name LOCAL-WAN rule 123 destination port '123' +set firewall ipv4 name LOCAL-WAN rule 123 protocol 'udp' +set firewall ipv4 name LOCAL-WAN rule 800 action 'return' +set firewall ipv4 name LOCAL-WAN rule 800 destination address '100.65.151.213' +set firewall ipv4 name LOCAL-WAN rule 800 protocol 'udp' +set firewall ipv4 name LOCAL-WAN rule 805 action 'return' +set firewall ipv4 name LOCAL-WAN rule 805 destination address '100.65.151.2' +set firewall ipv4 name LOCAL-WAN rule 805 protocol 'all' +set firewall ipv4 name LOCAL-WAN rule 1010 action 'return' +set firewall ipv4 name LOCAL-WAN rule 1010 destination address '100.64.69.205' +set firewall ipv4 name LOCAL-WAN rule 1010 destination port '7705' +set firewall ipv4 name LOCAL-WAN rule 1010 protocol 'udp' +set firewall ipv4 name LOCAL-WAN rule 1010 source port '7705' +set firewall ipv4 name LOCAL-WAN rule 1990 action 'return' +set firewall ipv4 name LOCAL-WAN rule 1990 destination address '100.64.55.1' +set firewall ipv4 name LOCAL-WAN rule 1990 destination port '10666' +set firewall ipv4 name LOCAL-WAN rule 1990 protocol 'udp' +set firewall ipv4 name LOCAL-WAN rule 2000 action 'return' +set firewall ipv4 name LOCAL-WAN rule 2000 destination address '100.64.39.249' +set firewall ipv4 name LOCAL-WAN rule 10200 action 'return' +set firewall ipv4 name LOCAL-WAN rule 10200 destination address '100.64.89.98' +set firewall ipv4 name LOCAL-WAN rule 10200 destination port '10200' +set firewall ipv4 name LOCAL-WAN rule 10200 protocol 'udp' +set firewall ipv4 name LOCAL-WAN rule 10200 source port '10200' +set firewall ipv4 name WAN-DMZ default-action 'drop' +set firewall ipv4 name WAN-DMZ default-log +set firewall ipv4 name WAN-DMZ rule 1 action 'return' +set firewall ipv4 name WAN-DMZ rule 1 state 'established' +set firewall ipv4 name WAN-DMZ rule 1 state 'related' +set firewall ipv4 name WAN-DMZ rule 2 action 'drop' +set firewall ipv4 name WAN-DMZ rule 2 log +set firewall ipv4 name WAN-DMZ rule 2 state 'invalid' +set firewall ipv4 name WAN-DMZ rule 100 action 'return' +set firewall ipv4 name WAN-DMZ rule 100 destination address '172.16.36.10' +set firewall ipv4 name WAN-DMZ rule 100 destination port '80,443' +set firewall ipv4 name WAN-DMZ rule 100 protocol 'tcp' +set firewall ipv4 name WAN-GUEST default-action 'drop' +set firewall ipv4 name WAN-GUEST default-log +set firewall ipv4 name WAN-GUEST rule 1 action 'return' +set firewall ipv4 name WAN-GUEST rule 1 state 'established' +set firewall ipv4 name WAN-GUEST rule 1 state 'related' +set firewall ipv4 name WAN-GUEST rule 2 action 'drop' +set firewall ipv4 name WAN-GUEST rule 2 log +set firewall ipv4 name WAN-GUEST rule 2 state 'invalid' +set firewall ipv4 name WAN-GUEST rule 1000 action 'return' +set firewall ipv4 name WAN-GUEST rule 1000 destination address '172.31.0.184' +set firewall ipv4 name WAN-GUEST rule 8000 action 'return' +set firewall ipv4 name WAN-GUEST rule 8000 destination address '172.31.0.200' +set firewall ipv4 name WAN-GUEST rule 8000 destination port '10000' +set firewall ipv4 name WAN-GUEST rule 8000 protocol 'udp' +set firewall ipv4 name WAN-LAN default-action 'drop' +set firewall ipv4 name WAN-LAN default-log +set firewall ipv4 name WAN-LAN description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv4 name WAN-LAN rule 1 action 'return' +set firewall ipv4 name WAN-LAN rule 1 description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv4 name WAN-LAN rule 1 state 'established' +set firewall ipv4 name WAN-LAN rule 1 state 'related' +set firewall ipv4 name WAN-LAN rule 2 action 'drop' +set firewall ipv4 name WAN-LAN rule 2 log +set firewall ipv4 name WAN-LAN rule 2 state 'invalid' +set firewall ipv4 name WAN-LAN rule 1000 action 'return' +set firewall ipv4 name WAN-LAN rule 1000 destination address '172.16.33.40' +set firewall ipv4 name WAN-LAN rule 1000 destination port '3389' +set firewall ipv4 name WAN-LAN rule 1000 protocol 'tcp' +set firewall ipv4 name WAN-LAN rule 1000 source group network-group 'SSH-IN-ALLOW' +set firewall ipv4 name WAN-LOCAL default-action 'drop' +set firewall ipv4 name WAN-LOCAL rule 1 action 'return' +set firewall ipv4 name WAN-LOCAL rule 1 state 'established' +set firewall ipv4 name WAN-LOCAL rule 1 state 'related' +set firewall ipv4 name WAN-LOCAL rule 2 action 'drop' +set firewall ipv4 name WAN-LOCAL rule 2 log +set firewall ipv4 name WAN-LOCAL rule 2 state 'invalid' +set firewall ipv4 name WAN-LOCAL rule 22 action 'return' +set firewall ipv4 name WAN-LOCAL rule 22 destination port '22' +set firewall ipv4 name WAN-LOCAL rule 22 protocol 'tcp' +set firewall ipv4 name WAN-LOCAL rule 22 source group network-group 'SSH-IN-ALLOW' +set firewall ipv4 name WAN-LOCAL rule 1990 action 'return' +set firewall ipv4 name WAN-LOCAL rule 1990 destination port '10666' +set firewall ipv4 name WAN-LOCAL rule 1990 protocol 'udp' +set firewall ipv4 name WAN-LOCAL rule 1990 source address '100.64.55.1' +set firewall ipv4 name WAN-LOCAL rule 10000 action 'return' +set firewall ipv4 name WAN-LOCAL rule 10000 destination port '80,443' +set firewall ipv4 name WAN-LOCAL rule 10000 protocol 'tcp' +set firewall ipv4 name WAN-LOCAL rule 10100 action 'return' +set firewall ipv4 name WAN-LOCAL rule 10100 destination port '10100' +set firewall ipv4 name WAN-LOCAL rule 10100 protocol 'udp' +set firewall ipv4 name WAN-LOCAL rule 10100 source port '10100' +set firewall ipv4 name WAN-LOCAL rule 10200 action 'return' +set firewall ipv4 name WAN-LOCAL rule 10200 destination port '10200' +set firewall ipv4 name WAN-LOCAL rule 10200 protocol 'udp' +set firewall ipv4 name WAN-LOCAL rule 10200 source address '100.64.89.98' +set firewall ipv4 name WAN-LOCAL rule 10200 source port '10200' +set firewall ipv6 name ALLOW-ALL-6 default-action 'return' +set firewall ipv6 name ALLOW-ALL-6 description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv6 name ALLOW-BASIC-6 default-action 'drop' +set firewall ipv6 name ALLOW-BASIC-6 default-log +set firewall ipv6 name ALLOW-BASIC-6 description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv6 name ALLOW-BASIC-6 rule 1 action 'return' +set firewall ipv6 name ALLOW-BASIC-6 rule 1 description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv6 name ALLOW-BASIC-6 rule 1 state 'established' +set firewall ipv6 name ALLOW-BASIC-6 rule 1 state 'related' +set firewall ipv6 name ALLOW-BASIC-6 rule 2 action 'drop' +set firewall ipv6 name ALLOW-BASIC-6 rule 2 description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata' +set firewall ipv6 name ALLOW-BASIC-6 rule 2 state 'invalid' +set firewall ipv6 name ALLOW-BASIC-6 rule 10 action 'return' +set firewall ipv6 name ALLOW-BASIC-6 rule 10 protocol 'ipv6-icmp' +set firewall ipv6 name ALLOW-ESTABLISHED-6 default-action 'drop' +set firewall ipv6 name ALLOW-ESTABLISHED-6 default-log +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 1 action 'return' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 1 state 'established' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 1 state 'related' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 2 action 'drop' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 2 state 'invalid' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 10 action 'return' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 10 destination group network-group 'LOCAL-ADDRESSES' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 10 protocol 'ipv6-icmp' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 10 source address 'fe80::/10' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 20 action 'return' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 20 icmpv6 type-name 'echo-request' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 20 protocol 'ipv6-icmp' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 21 action 'return' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 21 icmpv6 type-name 'destination-unreachable' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 21 protocol 'ipv6-icmp' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 22 action 'return' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 22 icmpv6 type-name 'packet-too-big' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 22 protocol 'ipv6-icmp' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 23 action 'return' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 23 icmpv6 type-name 'time-exceeded' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 23 protocol 'ipv6-icmp' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 24 action 'return' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 24 icmpv6 type-name 'parameter-problem' +set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 24 protocol 'ipv6-icmp' +set firewall ipv6 name WAN-LOCAL-6 default-action 'drop' +set firewall ipv6 name WAN-LOCAL-6 default-log +set firewall ipv6 name WAN-LOCAL-6 rule 1 action 'return' +set firewall ipv6 name WAN-LOCAL-6 rule 1 state 'established' +set firewall ipv6 name WAN-LOCAL-6 rule 1 state 'related' +set firewall ipv6 name WAN-LOCAL-6 rule 2 action 'drop' +set firewall ipv6 name WAN-LOCAL-6 rule 2 state 'invalid' +set firewall ipv6 name WAN-LOCAL-6 rule 10 action 'return' +set firewall ipv6 name WAN-LOCAL-6 rule 10 destination address 'ff02::/64' +set firewall ipv6 name WAN-LOCAL-6 rule 10 protocol 'ipv6-icmp' +set firewall ipv6 name WAN-LOCAL-6 rule 10 source address 'fe80::/10' +set firewall ipv6 name WAN-LOCAL-6 rule 50 action 'return' +set firewall ipv6 name WAN-LOCAL-6 rule 50 destination address 'fe80::/10' +set firewall ipv6 name WAN-LOCAL-6 rule 50 destination port '546' +set firewall ipv6 name WAN-LOCAL-6 rule 50 protocol 'udp' +set firewall ipv6 name WAN-LOCAL-6 rule 50 source address 'fe80::/10' +set firewall ipv6 name WAN-LOCAL-6 rule 50 source port '547' +set firewall zone DMZ default-action 'drop' +set firewall zone DMZ from GUEST firewall name 'GUEST-DMZ' +set firewall zone DMZ from LAN firewall name 'LAN-DMZ' +set firewall zone DMZ from LOCAL firewall name 'LOCAL-DMZ' +set firewall zone DMZ from WAN firewall name 'WAN-DMZ' +set firewall zone DMZ interface 'eth0.50' +set firewall zone GUEST default-action 'drop' +set firewall zone GUEST from DMZ firewall name 'DMZ-GUEST' +set firewall zone GUEST from LAN firewall name 'LAN-GUEST' +set firewall zone GUEST from LOCAL firewall ipv6-name 'ALLOW-ALL-6' +set firewall zone GUEST from LOCAL firewall name 'LOCAL-GUEST' +set firewall zone GUEST from WAN firewall ipv6-name 'ALLOW-ESTABLISHED-6' +set firewall zone GUEST from WAN firewall name 'WAN-GUEST' +set firewall zone GUEST interface 'eth1.20' +set firewall zone LAN default-action 'drop' +set firewall zone LAN from DMZ firewall name 'DMZ-LAN' +set firewall zone LAN from GUEST firewall name 'GUEST-LAN' +set firewall zone LAN from LOCAL firewall ipv6-name 'ALLOW-ALL-6' +set firewall zone LAN from LOCAL firewall name 'LOCAL-LAN' +set firewall zone LAN from WAN firewall ipv6-name 'ALLOW-ESTABLISHED-6' +set firewall zone LAN from WAN firewall name 'WAN-LAN' +set firewall zone LAN interface 'eth0.5' +set firewall zone LAN interface 'eth0.10' +set firewall zone LAN interface 'wg100' +set firewall zone LAN interface 'wg200' +set firewall zone LOCAL default-action 'drop' +set firewall zone LOCAL from DMZ firewall name 'DMZ-LOCAL' +set firewall zone LOCAL from GUEST firewall ipv6-name 'ALLOW-ESTABLISHED-6' +set firewall zone LOCAL from GUEST firewall name 'GUEST-LOCAL' +set firewall zone LOCAL from LAN firewall ipv6-name 'ALLOW-ALL-6' +set firewall zone LOCAL from LAN firewall name 'LAN-LOCAL' +set firewall zone LOCAL from WAN firewall ipv6-name 'WAN-LOCAL-6' +set firewall zone LOCAL from WAN firewall name 'WAN-LOCAL' +set firewall zone LOCAL local-zone +set firewall zone WAN default-action 'drop' +set firewall zone WAN from DMZ firewall name 'DMZ-WAN' +set firewall zone WAN from GUEST firewall ipv6-name 'ALLOW-ALL-6' +set firewall zone WAN from GUEST firewall name 'GUEST-WAN' +set firewall zone WAN from LAN firewall ipv6-name 'ALLOW-ALL-6' +set firewall zone WAN from LAN firewall name 'LAN-WAN' +set firewall zone WAN from LOCAL firewall ipv6-name 'ALLOW-ALL-6' +set firewall zone WAN from LOCAL firewall name 'LOCAL-WAN' +set firewall zone WAN interface 'pppoe0' +set firewall zone WAN interface 'wg666' +set nat destination rule 8000 destination port '10000' +set nat destination rule 8000 inbound-interface name 'pppoe0' +set nat destination rule 8000 protocol 'udp' +set nat destination rule 8000 translation address '172.31.0.200' +set nat source rule 50 outbound-interface name 'pppoe0' +set nat source rule 50 source address '100.64.0.0/24' +set nat source rule 50 translation address 'masquerade' +set nat source rule 100 outbound-interface name 'pppoe0' +set nat source rule 100 source address '172.16.32.0/21' +set nat source rule 100 translation address 'masquerade' +set nat source rule 200 outbound-interface name 'pppoe0' +set nat source rule 200 source address '172.16.100.0/24' +set nat source rule 200 translation address 'masquerade' +set nat source rule 300 outbound-interface name 'pppoe0' +set nat source rule 300 source address '172.31.0.0/24' +set nat source rule 300 translation address 'masquerade' +set nat source rule 400 outbound-interface name 'pppoe0' +set nat source rule 400 source address '172.18.200.0/21' +set nat source rule 400 translation address 'masquerade' +set nat source rule 1000 destination address '192.168.189.0/24' +set nat source rule 1000 outbound-interface name 'wg666' +set nat source rule 1000 source address '172.16.32.0/21' +set nat source rule 1000 translation address '172.29.0.1' +set nat source rule 1001 destination address '192.168.189.0/24' +set nat source rule 1001 outbound-interface name 'wg666' +set nat source rule 1001 source address '172.16.100.0/24' +set nat source rule 1001 translation address '172.29.0.1' +set policy route-map MAP-OSPF-CONNECTED rule 1 action 'deny' +set policy route-map MAP-OSPF-CONNECTED rule 1 match interface 'eth1.20' +set policy route-map MAP-OSPF-CONNECTED rule 20 action 'permit' +set policy route-map MAP-OSPF-CONNECTED rule 20 match interface 'eth0.10' +set policy route-map MAP-OSPF-CONNECTED rule 40 action 'permit' +set policy route-map MAP-OSPF-CONNECTED rule 40 match interface 'eth0.50' +set qos policy shaper QoS bandwidth '50mbit' +set qos policy shaper QoS default bandwidth '100%' +set qos policy shaper QoS default burst '15k' +set qos policy shaper QoS default queue-limit '1000' +set qos policy shaper QoS default queue-type 'fq-codel' diff --git a/smoketest/configs/basic-vyos b/smoketest/configs/basic-vyos index 76aa52039..e95d7458f 100644 --- a/smoketest/configs/basic-vyos +++ b/smoketest/configs/basic-vyos @@ -86,9 +86,25 @@ service { domain-name vyos.net domain-search vyos.net range LANDynamic { - start 192.168.0.20 + start 192.168.0.30 stop 192.168.0.240 } + static-mapping TEST1-1 { + ip-address 192.168.0.11 + mac-address 00:01:02:03:04:05 + } + static-mapping TEST1-2 { + ip-address 192.168.0.12 + mac-address 00:01:02:03:04:05 + } + static-mapping TEST2-1 { + ip-address 192.168.0.21 + mac-address 00:01:02:03:04:21 + } + static-mapping TEST2-2 { + ip-address 192.168.0.21 + mac-address 00:01:02:03:04:22 + } } } } diff --git a/smoketest/configs/dialup-router-wireguard-ipv6 b/smoketest/configs/dialup-router-wireguard-ipv6 index 33afb9b04..058582148 100644 --- a/smoketest/configs/dialup-router-wireguard-ipv6 +++ b/smoketest/configs/dialup-router-wireguard-ipv6 @@ -4,11 +4,13 @@ firewall { config-trap disable group { address-group DMZ-WEBSERVER { + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." address 172.16.36.10 address 172.16.36.40 address 172.16.36.20 } address-group DMZ-RDP-SERVER { + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." address 172.16.33.40 } address-group DOMAIN-CONTROLLER { @@ -35,11 +37,14 @@ firewall { } ipv6-name ALLOW-ALL-6 { default-action accept + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." } ipv6-name ALLOW-BASIC-6 { default-action drop enable-default-log + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." rule 1 { + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." action accept state { established enable @@ -47,6 +52,7 @@ firewall { } } rule 2 { + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." action drop state { invalid enable @@ -185,7 +191,9 @@ firewall { name DMZ-LAN { default-action drop enable-default-log + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." rule 1 { + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." action accept state { established enable @@ -194,6 +202,7 @@ firewall { } rule 2 { action drop + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." log enable state { invalid enable @@ -201,6 +210,7 @@ firewall { } rule 100 { action accept + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." destination { group { address-group DOMAIN-CONTROLLER @@ -210,6 +220,7 @@ firewall { protocol tcp_udp } rule 300 { + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." action accept destination { group { @@ -226,6 +237,7 @@ firewall { name DMZ-LOCAL { default-action drop enable-default-log + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." rule 1 { action accept state { @@ -241,6 +253,7 @@ firewall { } } rule 50 { + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." action accept destination { address 172.16.254.30 @@ -258,6 +271,7 @@ firewall { } name DMZ-WAN { default-action accept + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." } name GUEST-DMZ { default-action drop @@ -737,9 +751,11 @@ firewall { } name WAN-LAN { default-action drop + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." enable-default-log rule 1 { action accept + description "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." state { established enable related enable diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index a40b762a8..9b705c801 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -318,6 +318,22 @@ class BasicInterfaceTest: self.assertEqual(tmp, str()) self.assertEqual(Interface(intf).get_alias(), str()) + # Test maximum interface description lengt (255 characters) + test_string='abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789___' + for intf in self._interfaces: + + self.cli_set(self._base_path + [intf, 'description', test_string]) + for option in self._options.get(intf, []): + self.cli_set(self._base_path + [intf] + option.split()) + + self.cli_commit() + + # Validate interface description + for intf in self._interfaces: + tmp = read_file(f'/sys/class/net/{intf}/ifalias') + self.assertEqual(tmp, test_string) + self.assertEqual(Interface(intf).get_alias(), test_string) + def test_add_single_ip_address(self): addr = '192.0.2.0/31' for intf in self._interfaces: diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py index 140642806..c49d3e76c 100644 --- a/smoketest/scripts/cli/base_vyostest_shim.py +++ b/smoketest/scripts/cli/base_vyostest_shim.py @@ -102,6 +102,29 @@ class VyOSUnitTestSHIM: ssh_client.close() return output, error + # Verify nftables output + def verify_nftables(self, nftables_search, table, inverse=False, args=''): + nftables_output = cmd(f'sudo nft {args} list table {table}') + + for search in nftables_search: + matched = False + for line in nftables_output.split("\n"): + if all(item in line for item in search): + matched = True + break + self.assertTrue(not matched if inverse else matched, msg=search) + + def verify_nftables_chain(self, nftables_search, table, chain, inverse=False, args=''): + nftables_output = cmd(f'sudo nft {args} list chain {table} {chain}') + + for search in nftables_search: + matched = False + for line in nftables_output.split("\n"): + if all(item in line for item in search): + matched = True + break + self.assertTrue(not matched if inverse else matched, msg=search) + # standard construction; typing suggestion: https://stackoverflow.com/a/70292317 def ignore_warning(warning: Type[Warning]): import warnings diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index bc2848492..9e8473fa4 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -22,7 +22,6 @@ from time import sleep from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSessionError -from vyos.utils.process import cmd from vyos.utils.process import run sysfs_config = { @@ -67,28 +66,6 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.verify_nftables(nftables_search, 'ip vyos_filter', inverse=True) - def verify_nftables(self, nftables_search, table, inverse=False, args=''): - nftables_output = cmd(f'sudo nft {args} list table {table}') - - for search in nftables_search: - matched = False - for line in nftables_output.split("\n"): - if all(item in line for item in search): - matched = True - break - self.assertTrue(not matched if inverse else matched, msg=search) - - def verify_nftables_chain(self, nftables_search, table, chain, inverse=False, args=''): - nftables_output = cmd(f'sudo nft {args} list chain {table} {chain}') - - for search in nftables_search: - matched = False - for line in nftables_output.split("\n"): - if all(item in line for item in search): - matched = True - break - self.assertTrue(not matched if inverse else matched, msg=search) - def wait_for_domain_resolver(self, table, set_name, element, max_wait=10): # Resolver no longer blocks commit, need to wait for daemon to populate set count = 0 @@ -652,8 +629,8 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): nftables_search = [ ['ct state { established, related }', 'accept'], ['ct state invalid', 'reject'], - ['ct state new', 'ct status == dnat', 'accept'], - ['ct state { established, new }', 'ct status == snat', 'accept'], + ['ct state new', 'ct status dnat', 'accept'], + ['ct state { established, new }', 'ct status snat', 'accept'], ['ct state related', 'ct helper { "ftp", "pptp" }', 'accept'], ['drop', f'comment "{name} default-action drop"'], ['jump VYOS_STATE_POLICY'], @@ -808,7 +785,6 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): ['ct state related', 'accept'] ] - nftables_output = cmd('sudo nft list table ip vyos_filter') self.verify_nftables(nftables_search, 'ip vyos_filter') self.verify_nftables(nftables_search_v6, 'ip6 vyos_filter') diff --git a/smoketest/scripts/cli/test_high-availability_vrrp.py b/smoketest/scripts/cli/test_high-availability_vrrp.py index 98259d830..9ba06aef6 100755 --- a/smoketest/scripts/cli/test_high-availability_vrrp.py +++ b/smoketest/scripts/cli/test_high-availability_vrrp.py @@ -237,5 +237,85 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase): self.assertIn(f'track_interface', config) self.assertIn(f' {none_vrrp_interface}', config) + def test_05_set_multiple_peer_address(self): + group = 'VyOS-WAN' + vlan_id = '24' + vip = '100.64.24.1/24' + peer_address_1 = '192.0.2.1' + peer_address_2 = '192.0.2.2' + vrid = '150' + group_base = base_path + ['vrrp', 'group', group] + + self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', '100.64.24.11/24']) + self.cli_set(group_base + ['interface', vrrp_interface]) + self.cli_set(group_base + ['address', vip]) + self.cli_set(group_base + ['peer-address', peer_address_1]) + self.cli_set(group_base + ['peer-address', peer_address_2]) + self.cli_set(group_base + ['vrid', vrid]) + + # commit changes + self.cli_commit() + + config = getConfig(f'vrrp_instance {group}') + + self.assertIn(f'interface {vrrp_interface}', config) + self.assertIn(f'virtual_router_id {vrid}', config) + self.assertIn(f'unicast_peer', config) + self.assertIn(f' {peer_address_1}', config) + self.assertIn(f' {peer_address_2}', config) + + def test_check_health_script(self): + sync_group = 'VyOS' + + for group in groups: + vlan_id = group.lstrip('VLAN') + vip = f'100.64.{vlan_id}.1/24' + group_base = base_path + ['vrrp', 'group', group] + + self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]]) + + self.cli_set(group_base + ['interface', f'{vrrp_interface}.{vlan_id}']) + self.cli_set(group_base + ['address', vip]) + self.cli_set(group_base + ['vrid', vlan_id]) + + self.cli_set(group_base + ['health-check', 'ping', '127.0.0.1']) + + # commit changes + self.cli_commit() + + for group in groups: + config = getConfig(f'vrrp_instance {group}') + self.assertIn(f'track_script', config) + + self.cli_set(base_path + ['vrrp', 'sync-group', sync_group, 'member', groups[0]]) + + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + self.cli_delete(base_path + ['vrrp', 'group', groups[0], 'health-check']) + self.cli_commit() + + for group in groups[1:]: + config = getConfig(f'vrrp_instance {group}') + self.assertIn(f'track_script', config) + + config = getConfig(f'vrrp_instance {groups[0]}') + self.assertNotIn(f'track_script', config) + + config = getConfig(f'vrrp_sync_group {sync_group}') + self.assertNotIn(f'track_script', config) + + self.cli_set(base_path + ['vrrp', 'sync-group', sync_group, 'health-check', 'ping', '127.0.0.1']) + + # commit changes + self.cli_commit() + + config = getConfig(f'vrrp_instance {groups[0]}') + self.assertNotIn(f'track_script', config) + + config = getConfig(f'vrrp_sync_group {sync_group}') + self.assertIn(f'track_script', config) + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py index e414f18cb..8f387b23d 100755 --- a/smoketest/scripts/cli/test_interfaces_ethernet.py +++ b/smoketest/scripts/cli/test_interfaces_ethernet.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2022 VyOS maintainers and contributors +# Copyright (C) 2020-2024 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 @@ -17,7 +17,9 @@ import os import re import unittest + from glob import glob +from json import loads from netifaces import AF_INET from netifaces import AF_INET6 @@ -27,9 +29,9 @@ from base_interfaces_test import BasicInterfaceTest from vyos.configsession import ConfigSessionError from vyos.ifconfig import Section from vyos.pki import CERT_BEGIN -from vyos.template import is_ipv6 from vyos.utils.process import cmd from vyos.utils.process import process_named_running +from vyos.utils.process import popen from vyos.utils.file import read_file from vyos.utils.network import is_ipv6_link_local @@ -301,5 +303,56 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase): self.cli_delete(['pki', 'ca', name]) self.cli_delete(['pki', 'certificate', cert_name]) + def test_ethtool_ring_buffer(self): + for interface in self._interfaces: + # We do not use vyos.ethtool here to not have any chance + # for invalid testcases. Re-gain data by hand + tmp = cmd(f'sudo ethtool --json --show-ring {interface}') + tmp = loads(tmp) + max_rx = str(tmp[0]['rx-max']) + max_tx = str(tmp[0]['tx-max']) + + self.cli_set(self._base_path + [interface, 'ring-buffer', 'rx', max_rx]) + self.cli_set(self._base_path + [interface, 'ring-buffer', 'tx', max_tx]) + + self.cli_commit() + + for interface in self._interfaces: + tmp = cmd(f'sudo ethtool --json --show-ring {interface}') + tmp = loads(tmp) + max_rx = str(tmp[0]['rx-max']) + max_tx = str(tmp[0]['tx-max']) + rx = str(tmp[0]['rx']) + tx = str(tmp[0]['tx']) + + # validate if the above change was carried out properly and the + # ring-buffer size got increased + self.assertEqual(max_rx, rx) + self.assertEqual(max_tx, tx) + + def test_ethtool_flow_control(self): + for interface in self._interfaces: + # Disable flow-control + self.cli_set(self._base_path + [interface, 'disable-flow-control']) + # Check current flow-control state on ethernet interface + out, err = popen(f'sudo ethtool --json --show-pause {interface}') + # Flow-control not supported - test if it bails out with a proper + # this is a dynamic path where err = 1 on VMware, but err = 0 on + # a physical box. + if bool(err): + with self.assertRaises(ConfigSessionError): + self.cli_commit() + else: + out = loads(out) + # Flow control is on + self.assertTrue(out[0]['autonegotiate']) + + # commit change on CLI to disable-flow-control and re-test + self.cli_commit() + + out, err = popen(f'sudo ethtool --json --show-pause {interface}') + out = loads(out) + self.assertFalse(out[0]['autonegotiate']) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py index 1e6435df8..43e374398 100755 --- a/smoketest/scripts/cli/test_nat.py +++ b/smoketest/scripts/cli/test_nat.py @@ -21,8 +21,6 @@ import unittest from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSessionError -from vyos.utils.process import cmd -from vyos.utils.dict import dict_search base_path = ['nat'] src_path = base_path + ['source'] @@ -47,17 +45,6 @@ class TestNAT(VyOSUnitTestSHIM.TestCase): self.assertFalse(os.path.exists(nftables_nat_config)) self.assertFalse(os.path.exists(nftables_static_nat_conf)) - def verify_nftables(self, nftables_search, table, inverse=False, args=''): - nftables_output = cmd(f'sudo nft {args} list table {table}') - - for search in nftables_search: - matched = False - for line in nftables_output.split("\n"): - if all(item in line for item in search): - matched = True - break - self.assertTrue(not matched if inverse else matched, msg=search) - def wait_for_domain_resolver(self, table, set_name, element, max_wait=10): # Resolver no longer blocks commit, need to wait for daemon to populate set count = 0 @@ -100,21 +87,28 @@ class TestNAT(VyOSUnitTestSHIM.TestCase): address_group_member = '192.0.2.1' interface_group = 'smoketest_ifaces' interface_group_member = 'bond.99' - rule = '100' self.cli_set(['firewall', 'group', 'address-group', address_group, 'address', address_group_member]) self.cli_set(['firewall', 'group', 'interface-group', interface_group, 'interface', interface_group_member]) - self.cli_set(src_path + ['rule', rule, 'source', 'group', 'address-group', address_group]) - self.cli_set(src_path + ['rule', rule, 'outbound-interface', 'group', interface_group]) - self.cli_set(src_path + ['rule', rule, 'translation', 'address', 'masquerade']) + self.cli_set(src_path + ['rule', '100', 'source', 'group', 'address-group', address_group]) + self.cli_set(src_path + ['rule', '100', 'outbound-interface', 'group', interface_group]) + self.cli_set(src_path + ['rule', '100', 'translation', 'address', 'masquerade']) + + self.cli_set(src_path + ['rule', '110', 'source', 'group', 'address-group', address_group]) + self.cli_set(src_path + ['rule', '110', 'translation', 'address', '203.0.113.1']) + + self.cli_set(src_path + ['rule', '120', 'source', 'group', 'address-group', address_group]) + self.cli_set(src_path + ['rule', '120', 'translation', 'address', '203.0.113.111/32']) self.cli_commit() nftables_search = [ [f'set A_{address_group}'], [f'elements = {{ {address_group_member} }}'], - [f'ip saddr @A_{address_group}', f'oifname @I_{interface_group}', 'masquerade'] + [f'ip saddr @A_{address_group}', f'oifname @I_{interface_group}', 'masquerade'], + [f'ip saddr @A_{address_group}', 'snat to 203.0.113.1'], + [f'ip saddr @A_{address_group}', 'snat prefix to 203.0.113.111/32'] ] self.verify_nftables(nftables_search, 'ip vyos_nat') diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py index 0607f6616..400a895ff 100755 --- a/smoketest/scripts/cli/test_nat66.py +++ b/smoketest/scripts/cli/test_nat66.py @@ -22,8 +22,6 @@ import unittest from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSessionError -from vyos.utils.process import cmd -from vyos.utils.dict import dict_search base_path = ['nat66'] src_path = base_path + ['source'] @@ -42,17 +40,6 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase): self.cli_delete(base_path) self.cli_commit() - def verify_nftables(self, nftables_search, table, inverse=False, args=''): - nftables_output = cmd(f'sudo nft {args} list table {table}') - - for search in nftables_search: - matched = False - for line in nftables_output.split("\n"): - if all(item in line for item in search): - matched = True - break - self.assertTrue(not matched if inverse else matched, msg=search) - def test_source_nat66(self): source_prefix = 'fc00::/64' translation_prefix = 'fc01::/64' diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py index c21d8af4e..ee4445251 100755 --- a/smoketest/scripts/cli/test_policy.py +++ b/smoketest/scripts/cli/test_policy.py @@ -1065,6 +1065,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): '20' : { 'action' : 'permit', 'set' : { + 'as-path-exclude' : 'all', 'evpn-gateway-ipv4' : '192.0.2.99', 'evpn-gateway-ipv6' : '2001:db8:f00::1', }, diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py index c0b7c1fe7..462fc24d0 100755 --- a/smoketest/scripts/cli/test_policy_route.py +++ b/smoketest/scripts/cli/test_policy_route.py @@ -68,17 +68,6 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): self.verify_rules(ip_rule_search, inverse=True) - def verify_nftables(self, nftables_search, table, inverse=False): - nftables_output = cmd(f'sudo nft list table {table}') - - for search in nftables_search: - matched = False - for line in nftables_output.split("\n"): - if all(item in line for item in search): - matched = True - break - self.assertTrue(not matched if inverse else matched, msg=search) - def verify_rules(self, rules_search, inverse=False): rule_output = cmd('ip rule show') diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index 6bffc7c45..82fb96754 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2023 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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 @@ -240,7 +240,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): def test_ospf_07_redistribute(self): metric = '15' metric_type = '1' - redistribute = ['bgp', 'connected', 'isis', 'kernel', 'rip', 'static'] + redistribute = ['babel', 'bgp', 'connected', 'isis', 'kernel', 'rip', 'static'] for protocol in redistribute: self.cli_set(base_path + ['redistribute', protocol, 'metric', metric]) diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py index 4ae7f05d9..989e1552d 100755 --- a/smoketest/scripts/cli/test_protocols_ospfv3.py +++ b/smoketest/scripts/cli/test_protocols_ospfv3.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2023 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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 @@ -114,14 +114,18 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): def test_ospfv3_03_redistribute(self): + metric = '15' + metric_type = '1' route_map = 'foo-bar' route_map_seq = '10' - redistribute = ['bgp', 'connected', 'kernel', 'ripng', 'static'] + redistribute = ['babel', 'bgp', 'connected', 'isis', 'kernel', 'ripng', 'static'] self.cli_set(['policy', 'route-map', route_map, 'rule', route_map_seq, 'action', 'permit']) for protocol in redistribute: + self.cli_set(base_path + ['redistribute', protocol, 'metric', metric]) self.cli_set(base_path + ['redistribute', protocol, 'route-map', route_map]) + self.cli_set(base_path + ['redistribute', protocol, 'metric-type', metric_type]) # commit changes self.cli_commit() @@ -130,7 +134,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): frrconfig = self.getFRRconfig('router ospf6', daemon=PROCESS_NAME) self.assertIn(f'router ospf6', frrconfig) for protocol in redistribute: - self.assertIn(f' redistribute {protocol} route-map {route_map}', frrconfig) + self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) def test_ospfv3_04_interfaces(self): diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py index 194289567..b582a2108 100755 --- a/smoketest/scripts/cli/test_service_dhcp-server.py +++ b/smoketest/scripts/cli/test_service_dhcp-server.py @@ -100,6 +100,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet] self.cli_set(pool + ['subnet-id', '1']) + self.cli_set(pool + ['ignore-client-id']) # we use the first subnet IP address as default gateway self.cli_set(pool + ['option', 'default-router', router]) self.cli_set(pool + ['option', 'name-server', dns_1]) @@ -124,6 +125,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name) self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet) self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'id', 1) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'match-client-id', False) self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'valid-lifetime', 86400) self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400) diff --git a/smoketest/scripts/cli/test_service_router-advert.py b/smoketest/scripts/cli/test_service_router-advert.py index 5fc2019fd..d1ff25a58 100755 --- a/smoketest/scripts/cli/test_service_router-advert.py +++ b/smoketest/scripts/cli/test_service_router-advert.py @@ -195,6 +195,34 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase): for src in ra_src: self.assertIn(f' {src};', config) + def test_nat64prefix(self): + nat64prefix = '64:ff9b::/96' + nat64prefix_invalid = '64:ff9b::/44' + + self.cli_set(base_path + ['nat64prefix', nat64prefix]) + + # and another invalid prefix + # Invalid NAT64 prefix length for "2001:db8::/34", can only be one of: + # /32, /40, /48, /56, /64, /96 + self.cli_set(base_path + ['nat64prefix', nat64prefix_invalid]) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['nat64prefix', nat64prefix_invalid]) + + # NAT64 valid-lifetime must not be smaller then "interval max" + self.cli_set(base_path + ['nat64prefix', nat64prefix, 'valid-lifetime', '500']) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['nat64prefix', nat64prefix, 'valid-lifetime']) + + # commit changes + self.cli_commit() + + config = read_file(RADVD_CONF) + + tmp = f'nat64prefix {nat64prefix}' + ' {' + self.assertIn(tmp, config) + self.assertIn('AdvValidLifetime 65528;', config) # default if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_snmp.py b/smoketest/scripts/cli/test_service_snmp.py index 52a72ec4f..b3daa90d0 100755 --- a/smoketest/scripts/cli/test_service_snmp.py +++ b/smoketest/scripts/cli/test_service_snmp.py @@ -229,5 +229,22 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase): tmp = call(f'snmpwalk -v 3 -u {snmpv3_user} -a MD5 -A {snmpv3_auth_pw} -x DES -X {snmpv3_priv_pw} -l authPriv 127.0.0.1', stdout=DEVNULL) self.assertEqual(tmp, 0) + def test_snmpv3_view_exclude(self): + snmpv3_view_oid_exclude = ['1.3.6.1.2.1.4.21', '1.3.6.1.2.1.4.24'] + + self.cli_set(base_path + ['v3', 'group', snmpv3_group, 'view', snmpv3_view]) + self.cli_set(base_path + ['v3', 'view', snmpv3_view, 'oid', snmpv3_view_oid]) + + for excluded in snmpv3_view_oid_exclude: + self.cli_set(base_path + ['v3', 'view', snmpv3_view, 'oid', snmpv3_view_oid, 'exclude', excluded]) + + self.cli_commit() + + tmp = read_file(SNMPD_CONF) + # views + self.assertIn(f'view {snmpv3_view} included .{snmpv3_view_oid}', tmp) + for excluded in snmpv3_view_oid_exclude: + self.assertIn(f'view {snmpv3_view} excluded .{excluded}', tmp) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_system_conntrack.py b/smoketest/scripts/cli/test_system_conntrack.py index ce237a6e7..2d76da145 100755 --- a/smoketest/scripts/cli/test_system_conntrack.py +++ b/smoketest/scripts/cli/test_system_conntrack.py @@ -21,7 +21,6 @@ import unittest from base_vyostest_shim import VyOSUnitTestSHIM from vyos.firewall import find_nftables_rule -from vyos.utils.process import cmd from vyos.utils.file import read_file base_path = ['system', 'conntrack'] @@ -43,17 +42,6 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase): self.cli_delete(base_path) self.cli_commit() - def verify_nftables(self, nftables_search, table, inverse=False, args=''): - nftables_output = cmd(f'sudo nft {args} list table {table}') - - for search in nftables_search: - matched = False - for line in nftables_output.split("\n"): - if all(item in line for item in search): - matched = True - break - self.assertTrue(not matched if inverse else matched, msg=search) - def test_conntrack_options(self): conntrack_config = { 'net.netfilter.nf_conntrack_expect_max' : { @@ -186,12 +174,16 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase): 'pptp': { 'driver': ['nf_nat_pptp', 'nf_conntrack_pptp'], 'nftables': ['ct helper set "pptp_tcp"'] - }, + }, + 'rtsp': { + 'driver': ['nf_nat_rtsp', 'nf_conntrack_rtsp'], + 'nftables': ['ct helper set "rtsp_tcp"'] + }, 'sip': { 'driver': ['nf_nat_sip', 'nf_conntrack_sip'], 'nftables': ['ct helper set "sip_tcp"', 'ct helper set "sip_udp"'] - }, + }, 'sqlnet': { 'nftables': ['ct helper set "tns_tcp"'] }, diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py index 438387f2d..c96b8e374 100755 --- a/smoketest/scripts/cli/test_vrf.py +++ b/smoketest/scripts/cli/test_vrf.py @@ -529,5 +529,28 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): self.assertNotIn(f' no ip nht resolve-via-default', frrconfig) self.assertNotIn(f' no ipv6 nht resolve-via-default', frrconfig) + def test_vrf_conntrack(self): + table = '1000' + nftables_rules = { + 'vrf_zones_ct_in': ['ct original zone set iifname map @ct_iface_map'], + 'vrf_zones_ct_out': ['ct original zone set oifname map @ct_iface_map'] + } + + self.cli_set(base_path + ['name', 'blue', 'table', table]) + self.cli_commit() + + # Conntrack rules should not be present + for chain, rule in nftables_rules.items(): + self.verify_nftables_chain(rule, 'inet vrf_zones', chain, inverse=True) + + self.cli_set(['nat']) + self.cli_commit() + + # Conntrack rules should now be present + for chain, rule in nftables_rules.items(): + self.verify_nftables_chain(rule, 'inet vrf_zones', chain, inverse=False) + + self.cli_delete(['nat']) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py index 321d00abf..e967bee71 100755 --- a/src/conf_mode/container.py +++ b/src/conf_mode/container.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2023 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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 @@ -32,7 +32,6 @@ from vyos.utils.file import write_file from vyos.utils.process import call from vyos.utils.process import cmd from vyos.utils.process import run -from vyos.utils.process import rc_cmd from vyos.template import bracketize_ipv6 from vyos.template import inc_ip from vyos.template import is_ipv4 @@ -251,7 +250,7 @@ def verify(container): if 'authentication' not in registry_config: continue if not {'username', 'password'} <= set(registry_config['authentication']): - raise ConfigError('If registry username or or password is defined, so must be the other!') + raise ConfigError('Container registry requires both username and password to be set!') return None @@ -401,24 +400,6 @@ def generate(container): write_file(f'/etc/containers/networks/{network}.json', json_write(tmp, indent=2)) - if 'registry' in container: - cmd = f'podman logout --all' - rc, out = rc_cmd(cmd) - if rc != 0: - raise ConfigError(out) - - for registry, registry_config in container['registry'].items(): - if 'disable' in registry_config: - continue - if 'authentication' in registry_config: - if {'username', 'password'} <= set(registry_config['authentication']): - username = registry_config['authentication']['username'] - password = registry_config['authentication']['password'] - cmd = f'podman login --username {username} --password {password} {registry}' - rc, out = rc_cmd(cmd) - if rc != 0: - raise ConfigError(out) - render(config_containers, 'container/containers.conf.j2', container) render(config_registry, 'container/registries.conf.j2', container) render(config_storage, 'container/storage.conf.j2', container) diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index acb7dfa41..810437dda 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -268,6 +268,18 @@ def verify_rule(firewall, rule_conf, ipv6): if 'port' in side_conf and dict_search_args(side_conf, 'group', 'port_group'): raise ConfigError(f'{side} port-group and port cannot both be defined') + if 'add_address_to_group' in rule_conf: + for type in ['destination_address', 'source_address']: + if type in rule_conf['add_address_to_group']: + if 'address_group' not in rule_conf['add_address_to_group'][type]: + raise ConfigError(f'Dynamic address group must be defined.') + else: + target = rule_conf['add_address_to_group'][type]['address_group'] + fwall_group = 'ipv6_address_group' if ipv6 else 'address_group' + group_obj = dict_search_args(firewall, 'group', 'dynamic_group', fwall_group, target) + if group_obj is None: + raise ConfigError(f'Invalid dynamic address group on firewall rule') + if 'log_options' in rule_conf: if 'log' not in rule_conf: raise ConfigError('log-options defined, but log is not enable') @@ -282,6 +294,15 @@ def verify_rule(firewall, rule_conf, ipv6): if direction in rule_conf: if 'name' in rule_conf[direction] and 'group' in rule_conf[direction]: raise ConfigError(f'Cannot specify both interface group and interface name for {direction}') + if 'group' in rule_conf[direction]: + group_name = rule_conf[direction]['group'] + if group_name[0] == '!': + group_name = group_name[1:] + group_obj = dict_search_args(firewall, 'group', 'interface_group', group_name) + if group_obj is None: + raise ConfigError(f'Invalid interface group "{group_name}" on firewall rule') + if not group_obj: + Warning(f'interface-group "{group_name}" has no members!') def verify_nested_group(group_name, group, groups, seen): if 'include' not in group: diff --git a/src/conf_mode/high-availability.py b/src/conf_mode/high-availability.py index b3b27b14e..c726db8b2 100755 --- a/src/conf_mode/high-availability.py +++ b/src/conf_mode/high-availability.py @@ -86,16 +86,7 @@ def verify(ha): raise ConfigError(f'Authentication requires both type and passwortd to be set in VRRP group "{group}"') if 'health_check' in group_config: - health_check_types = ["script", "ping"] - from vyos.utils.dict import check_mutually_exclusive_options - try: - check_mutually_exclusive_options(group_config["health_check"], health_check_types, required=True) - except ValueError: - Warning(f'Health check configuration for VRRP group "{group}" will remain unused ' \ - f'until it has one of the following options: {health_check_types}') - # XXX: health check has default options so we need to remove it - # to avoid generating useless config statements in keepalived.conf - del group_config["health_check"] + _validate_health_check(group, group_config) # Keepalived doesn't allow mixing IPv4 and IPv6 in one group, so we mirror that restriction # We also need to make sure VRID is not used twice on the same interface with the @@ -125,8 +116,9 @@ def verify(ha): raise ConfigError(f'VRRP group "{group}" uses IPv4 but hello-source-address is IPv6!') if 'peer_address' in group_config: - if is_ipv6(group_config['peer_address']): - raise ConfigError(f'VRRP group "{group}" uses IPv4 but peer-address is IPv6!') + for peer_address in group_config['peer_address']: + if is_ipv6(peer_address): + raise ConfigError(f'VRRP group "{group}" uses IPv4 but peer-address is IPv6!') if vaddrs6: tmp = {'interface': interface, 'vrid': vrid, 'ipver': 'IPv6'} @@ -139,16 +131,28 @@ def verify(ha): raise ConfigError(f'VRRP group "{group}" uses IPv6 but hello-source-address is IPv4!') if 'peer_address' in group_config: - if is_ipv4(group_config['peer_address']): - raise ConfigError(f'VRRP group "{group}" uses IPv6 but peer-address is IPv4!') + for peer_address in group_config['peer_address']: + if is_ipv4(peer_address): + raise ConfigError(f'VRRP group "{group}" uses IPv6 but peer-address is IPv4!') # Check sync groups if 'vrrp' in ha and 'sync_group' in ha['vrrp']: for sync_group, sync_config in ha['vrrp']['sync_group'].items(): + if 'health_check' in sync_config: + _validate_health_check(sync_group, sync_config) + if 'member' in sync_config: for member in sync_config['member']: if member not in ha['vrrp']['group']: raise ConfigError(f'VRRP sync-group "{sync_group}" refers to VRRP group "{member}", '\ 'but it does not exist!') + else: + ha['vrrp']['group'][member]['_is_sync_group_member'] = True + if ha['vrrp']['group'][member].get('health_check') is not None: + raise ConfigError( + f'Health check configuration for VRRP group "{member}" will remain unused ' + f'while it has member of sync group "{sync_group}" ' + f'Only sync group health check will be used' + ) # Virtual-server if 'virtual_server' in ha: @@ -170,6 +174,21 @@ def verify(ha): raise ConfigError(f'Port is required but not set for virtual-server "{vs}" real-server "{rs}"') +def _validate_health_check(group, group_config): + health_check_types = ["script", "ping"] + from vyos.utils.dict import check_mutually_exclusive_options + try: + check_mutually_exclusive_options(group_config["health_check"], + health_check_types, required=True) + except ValueError: + Warning( + f'Health check configuration for VRRP group "{group}" will remain unused ' \ + f'until it has one of the following options: {health_check_types}') + # XXX: health check has default options so we need to remove it + # to avoid generating useless config statements in keepalived.conf + del group_config["health_check"] + + def generate(ha): if not ha or 'disable' in ha: if os.path.isfile(systemd_override): diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index 26822b755..b3f38c04a 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -153,6 +153,15 @@ def verify(nat): elif 'name' in config['outbound_interface']: if config['outbound_interface']['name'] not in 'any' and config['outbound_interface']['name'] not in interfaces(): Warning(f'NAT interface "{config["outbound_interface"]["name"]}" for source NAT rule "{rule}" does not exist!') + else: + group_name = config['outbound_interface']['group'] + if group_name[0] == '!': + group_name = group_name[1:] + group_obj = dict_search_args(nat['firewall_group'], 'interface_group', group_name) + if group_obj is None: + raise ConfigError(f'Invalid interface group "{group_name}" on source nat rule') + if not group_obj: + Warning(f'interface-group "{group_name}" has no members!') if not dict_search('translation.address', config) and not dict_search('translation.port', config): if 'exclude' not in config and 'backend' not in config['load_balance']: @@ -177,6 +186,15 @@ def verify(nat): elif 'name' in config['inbound_interface']: if config['inbound_interface']['name'] not in 'any' and config['inbound_interface']['name'] not in interfaces(): Warning(f'NAT interface "{config["inbound_interface"]["name"]}" for destination NAT rule "{rule}" does not exist!') + else: + group_name = config['inbound_interface']['group'] + if group_name[0] == '!': + group_name = group_name[1:] + group_obj = dict_search_args(nat['firewall_group'], 'interface_group', group_name) + if group_obj is None: + raise ConfigError(f'Invalid interface group "{group_name}" on destination nat rule') + if not group_obj: + Warning(f'interface-group "{group_name}" has no members!') if not dict_search('translation.address', config) and not dict_search('translation.port', config) and 'redirect' not in config['translation']: if 'exclude' not in config and 'backend' not in config['load_balance']: diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 34cf49286..695842795 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2023 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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 @@ -91,6 +91,8 @@ def get_config(config=None): for protocol in ['babel', 'bgp', 'connected', 'isis', 'kernel', 'rip', 'static']: if dict_search(f'redistribute.{protocol}', ospf) is None: del default_values['redistribute'][protocol] + if not bool(default_values['redistribute']): + del default_values['redistribute'] for interface in ospf.get('interface', []): # We need to reload the defaults on every pass b/c of @@ -213,7 +215,7 @@ def verify(ospf): raise ConfigError(f'Segment routing prefix {prefix} cannot have both explicit-null '\ f'and no-php-flag configured at the same time.') - # Check for index ranges being larger than the segment routing global block + # Check for index ranges being larger than the segment routing global block if dict_search('segment_routing.global_block', ospf): g_high_label_value = dict_search('segment_routing.global_block.high_label_value', ospf) g_low_label_value = dict_search('segment_routing.global_block.low_label_value', ospf) diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py index 5b1adce30..afd767dbf 100755 --- a/src/conf_mode/protocols_ospfv3.py +++ b/src/conf_mode/protocols_ospfv3.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2023 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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 @@ -85,6 +85,12 @@ def get_config(config=None): if 'graceful_restart' not in ospfv3: del default_values['graceful_restart'] + for protocol in ['babel', 'bgp', 'connected', 'isis', 'kernel', 'ripng', 'static']: + if dict_search(f'redistribute.{protocol}', ospfv3) is None: + del default_values['redistribute'][protocol] + if not bool(default_values['redistribute']): + del default_values['redistribute'] + default_values.pop('interface', {}) # merge in remaining default values diff --git a/src/conf_mode/service_router-advert.py b/src/conf_mode/service_router-advert.py index dbb47de4e..88d767bb8 100755 --- a/src/conf_mode/service_router-advert.py +++ b/src/conf_mode/service_router-advert.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2022 VyOS maintainers and contributors +# Copyright (C) 2018-2024 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 @@ -17,6 +17,8 @@ import os from sys import exit +from ipaddress import IPv6Network + from vyos.base import Warning from vyos.config import Config from vyos.template import render @@ -47,7 +49,9 @@ def verify(rtradv): return None for interface, interface_config in rtradv['interface'].items(): - if 'prefix' in interface: + interval_max = int(interface_config['interval']['max']) + + if 'prefix' in interface_config: for prefix, prefix_config in interface_config['prefix'].items(): valid_lifetime = prefix_config['valid_lifetime'] if valid_lifetime == 'infinity': @@ -60,6 +64,15 @@ def verify(rtradv): if not (int(valid_lifetime) >= int(preferred_lifetime)): raise ConfigError('Prefix valid-lifetime must be greater then or equal to preferred-lifetime') + if 'nat64prefix' in interface_config: + nat64_supported_lengths = [32, 40, 48, 56, 64, 96] + for prefix, prefix_config in interface_config['nat64prefix'].items(): + if IPv6Network(prefix).prefixlen not in nat64_supported_lengths: + raise ConfigError(f'Invalid NAT64 prefix length for "{prefix}", can only be one of: /' + ', /'.join(nat64_supported_lengths)) + + if int(prefix_config['valid_lifetime']) < interval_max: + raise ConfigError(f'NAT64 valid-lifetime must not be smaller then "interval max" which is "{interval_max}"!') + if 'name_server' in interface_config: if len(interface_config['name_server']) > 3: raise ConfigError('No more then 3 IPv6 name-servers supported!') @@ -72,7 +85,6 @@ def verify(rtradv): # ensure stale RDNSS info gets removed in a timely fashion, this # should not be greater than 2*MaxRtrAdvInterval. lifetime = int(interface_config['name_server_lifetime']) - interval_max = int(interface_config['interval']['max']) if lifetime > 0: if lifetime < int(interval_max): raise ConfigError(f'RDNSS lifetime must be at least "{interval_max}" seconds!') diff --git a/src/conf_mode/system_conntrack.py b/src/conf_mode/system_conntrack.py index 7f6c71440..a1472aaaa 100755 --- a/src/conf_mode/system_conntrack.py +++ b/src/conf_mode/system_conntrack.py @@ -42,33 +42,38 @@ nftables_ct_file = r'/run/nftables-ct.conf' module_map = { 'ftp': { 'ko': ['nf_nat_ftp', 'nf_conntrack_ftp'], - 'nftables': ['ct helper set "ftp_tcp" tcp dport {21} return'] + 'nftables': ['tcp dport {21} ct helper set "ftp_tcp" return'] }, 'h323': { 'ko': ['nf_nat_h323', 'nf_conntrack_h323'], - 'nftables': ['ct helper set "ras_udp" udp dport {1719} return', - 'ct helper set "q931_tcp" tcp dport {1720} return'] + 'nftables': ['udp dport {1719} ct helper set "ras_udp" return', + 'tcp dport {1720} ct helper set "q931_tcp" return'] }, 'nfs': { - 'nftables': ['ct helper set "rpc_tcp" tcp dport {111} return', - 'ct helper set "rpc_udp" udp dport {111} return'] + 'nftables': ['tcp dport {111} ct helper set "rpc_tcp" return', + 'udp dport {111} ct helper set "rpc_udp" return'] }, 'pptp': { 'ko': ['nf_nat_pptp', 'nf_conntrack_pptp'], - 'nftables': ['ct helper set "pptp_tcp" tcp dport {1723} return'], + 'nftables': ['tcp dport {1723} ct helper set "pptp_tcp" return'], 'ipv4': True }, + 'rtsp': { + 'ko': ['nf_nat_rtsp', 'nf_conntrack_rtsp'], + 'nftables': ['tcp dport {554} ct helper set "rtsp_tcp" return'], + 'ipv4': True + }, 'sip': { 'ko': ['nf_nat_sip', 'nf_conntrack_sip'], - 'nftables': ['ct helper set "sip_tcp" tcp dport {5060,5061} return', - 'ct helper set "sip_udp" udp dport {5060,5061} return'] + 'nftables': ['tcp dport {5060,5061} ct helper set "sip_tcp" return', + 'udp dport {5060,5061} ct helper set "sip_udp" return'] }, 'sqlnet': { - 'nftables': ['ct helper set "tns_tcp" tcp dport {1521,1525,1536} return'] + 'nftables': ['tcp dport {1521,1525,1536} ct helper set "tns_tcp" return'] }, 'tftp': { 'ko': ['nf_nat_tftp', 'nf_conntrack_tftp'], - 'nftables': ['ct helper set "tftp_udp" udp dport {69} return'] + 'nftables': ['udp dport {69} ct helper set "tftp_udp" return'] }, } @@ -104,6 +109,10 @@ def get_config(config=None): if conf.exists(['service', 'conntrack-sync']): set_dependents('conntrack_sync', conf) + # If conntrack status changes, VRF zone rules need updating + if conf.exists(['vrf']): + set_dependents('vrf', conf) + return conntrack def verify(conntrack): @@ -191,7 +200,7 @@ def generate(conntrack): def apply(conntrack): # Depending on the enable/disable state of the ALG (Application Layer Gateway) # modules we need to either insmod or rmmod the helpers. - + add_modules = [] rm_modules = [] diff --git a/src/conf_mode/system_login_banner.py b/src/conf_mode/system_login_banner.py index 65fa04417..923e1bf57 100755 --- a/src/conf_mode/system_login_banner.py +++ b/src/conf_mode/system_login_banner.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2021 VyOS maintainers and contributors +# Copyright (C) 2020-2024 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 @@ -18,30 +18,26 @@ from sys import exit from copy import deepcopy from vyos.config import Config +from vyos.template import render from vyos.utils.file import write_file +from vyos.version import get_version_data from vyos import ConfigError from vyos import airbag airbag.enable() -try: - with open('/usr/share/vyos/default_motd') as f: - motd = f.read() -except: - # Use an empty banner if the default banner file cannot be read - motd = "\n" - PRELOGIN_FILE = r'/etc/issue' PRELOGIN_NET_FILE = r'/etc/issue.net' POSTLOGIN_FILE = r'/etc/motd' default_config_data = { 'issue': 'Welcome to VyOS - \\n \\l\n\n', - 'issue_net': '', - 'motd': motd + 'issue_net': '' } def get_config(config=None): banner = deepcopy(default_config_data) + banner['version_data'] = get_version_data() + if config: conf = config else: @@ -92,7 +88,11 @@ def generate(banner): def apply(banner): write_file(PRELOGIN_FILE, banner['issue']) write_file(PRELOGIN_NET_FILE, banner['issue_net']) - write_file(POSTLOGIN_FILE, banner['motd']) + if 'motd' in banner: + write_file(POSTLOGIN_FILE, banner['motd']) + else: + render(POSTLOGIN_FILE, 'login/default_motd.j2', banner, + permission=0o644, user='root', group='root') return None diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py index 3b5b67437..7ed451e16 100755 --- a/src/conf_mode/system_option.py +++ b/src/conf_mode/system_option.py @@ -92,6 +92,8 @@ def generate(options): if 'kernel' in options: if 'disable_mitigations' in options['kernel']: cmdline_options.append('mitigations=off') + if 'disable_power_saving' in options['kernel']: + cmdline_options.append('intel_idle.max_cstate=0 processor.max_cstate=1') grub_util.update_kernel_cmdline_options(' '.join(cmdline_options)) return None diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index d074ed159..388f2a709 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -25,6 +25,8 @@ from time import time from vyos.base import Warning from vyos.config import Config +from vyos.configdep import set_dependents +from vyos.configdep import call_dependents from vyos.configdict import leaf_node_changed from vyos.configverify import verify_interface_exists from vyos.configverify import dynamic_interface_pattern @@ -97,6 +99,9 @@ def get_config(config=None): ipsec['interface_change'] = leaf_node_changed(conf, base + ['interface']) ipsec['nhrp_exists'] = conf.exists(['protocols', 'nhrp', 'tunnel']) + if ipsec['nhrp_exists']: + set_dependents('nhrp', conf) + tmp = conf.get_config_dict(l2tp_base, key_mangling=('-', '_'), no_tag_node_value_mangle=True, get_first_key=True) @@ -575,13 +580,6 @@ def generate(ipsec): render(interface_conf, 'ipsec/interfaces_use.conf.j2', ipsec) render(swanctl_conf, 'ipsec/swanctl.conf.j2', ipsec) -def resync_nhrp(ipsec): - if ipsec and not ipsec['nhrp_exists']: - return - - tmp = run('/usr/libexec/vyos/conf_mode/protocols_nhrp.py') - if tmp > 0: - print('ERROR: failed to reapply NHRP settings!') def apply(ipsec): systemd_service = 'strongswan.service' @@ -590,7 +588,14 @@ def apply(ipsec): else: call(f'systemctl reload-or-restart {systemd_service}') - resync_nhrp(ipsec) + if ipsec.get('nhrp_exists', False): + try: + call_dependents() + except ConfigError: + # Ignore config errors on dependent due to being called too early. Example: + # ConfigError("ConfigError('Interface ethN requires an IP address!')") + pass + if __name__ == '__main__': try: diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index a2f4956be..16908100f 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -23,6 +23,7 @@ from vyos.config import Config from vyos.configdict import dict_merge from vyos.configdict import node_changed from vyos.configverify import verify_route_map +from vyos.firewall import conntrack_required from vyos.ifconfig import Interface from vyos.template import render from vyos.template import render_to_string @@ -41,6 +42,12 @@ airbag.enable() config_file = '/etc/iproute2/rt_tables.d/vyos-vrf.conf' k_mod = ['vrf'] +nftables_table = 'inet vrf_zones' +nftables_rules = { + 'vrf_zones_ct_in': 'counter ct original zone set iifname map @ct_iface_map', + 'vrf_zones_ct_out': 'counter ct original zone set oifname map @ct_iface_map' +} + def has_rule(af : str, priority : int, table : str=None): """ Check if a given ip rule exists @@ -114,6 +121,9 @@ def get_config(config=None): routes = vrf_routing(conf, name) if routes: vrf['vrf_remove'][name]['route'] = routes + if 'name' in vrf: + vrf['conntrack'] = conntrack_required(conf) + # We also need the route-map information from the config # # XXX: one MUST always call this without the key_mangling() option! See @@ -294,6 +304,14 @@ def apply(vrf): nft_add_element = f'add element inet vrf_zones ct_iface_map {{ "{name}" : {table} }}' cmd(f'nft {nft_add_element}') + if vrf['conntrack']: + for chain, rule in nftables_rules.items(): + cmd(f'nft add rule inet vrf_zones {chain} {rule}') + + if 'name' not in vrf or not vrf['conntrack']: + for chain, rule in nftables_rules.items(): + cmd(f'nft flush chain inet vrf_zones {chain}') + # Apply FRR filters zebra_daemon = 'zebra' # Save original configuration prior to starting any commit actions diff --git a/src/helpers/vyos-config-encrypt.py b/src/helpers/vyos-config-encrypt.py new file mode 100755 index 000000000..8f7359767 --- /dev/null +++ b/src/helpers/vyos-config-encrypt.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import re +import shutil +import sys + +from argparse import ArgumentParser +from cryptography.fernet import Fernet +from tempfile import NamedTemporaryFile +from tempfile import TemporaryDirectory + +from vyos.tpm import clear_tpm_key +from vyos.tpm import init_tpm +from vyos.tpm import read_tpm_key +from vyos.tpm import write_tpm_key +from vyos.util import ask_input +from vyos.util import ask_yes_no +from vyos.util import cmd + +persistpath_cmd = '/opt/vyatta/sbin/vyos-persistpath' +mount_paths = ['/config', '/opt/vyatta/etc/config'] +dm_device = '/dev/mapper/vyos_config' + +def is_opened(): + return os.path.exists(dm_device) + +def get_current_image(): + with open('/proc/cmdline', 'r') as f: + args = f.read().split(" ") + for arg in args: + if 'vyos-union' in arg: + k, v = arg.split("=") + path_split = v.split("/") + return path_split[-1] + return None + +def load_config(key): + if not key: + return + + persist_path = cmd(persistpath_cmd).strip() + image_name = get_current_image() + image_path = os.path.join(persist_path, 'luks', image_name) + + if not os.path.exists(image_path): + raise Exception("Encrypted config volume doesn't exist") + + if is_opened(): + print('Encrypted config volume is already mounted') + return + + with NamedTemporaryFile(dir='/dev/shm', delete=False) as f: + f.write(key) + key_file = f.name + + cmd(f'cryptsetup -q open {image_path} vyos_config --key-file={key_file}') + + for path in mount_paths: + cmd(f'mount /dev/mapper/vyos_config {path}') + cmd(f'chgrp -R vyattacfg {path}') + + os.unlink(key_file) + + return True + +def encrypt_config(key, recovery_key): + if is_opened(): + raise Exception('An encrypted config volume is already mapped') + + # Clear and write key to TPM + try: + clear_tpm_key() + except: + pass + write_tpm_key(key) + + persist_path = cmd(persistpath_cmd).strip() + size = ask_input('Enter size of encrypted config partition (MB): ', numeric_only=True, default=512) + + luks_folder = os.path.join(persist_path, 'luks') + + if not os.path.isdir(luks_folder): + os.mkdir(luks_folder) + + image_name = get_current_image() + image_path = os.path.join(luks_folder, image_name) + + # Create file for encrypted config + cmd(f'fallocate -l {size}M {image_path}') + + # Write TPM key for slot #1 + with NamedTemporaryFile(dir='/dev/shm', delete=False) as f: + f.write(key) + key_file = f.name + + # Format and add main key to volume + cmd(f'cryptsetup -q luksFormat {image_path} {key_file}') + + if recovery_key: + # Write recovery key for slot 2 + with NamedTemporaryFile(dir='/dev/shm', delete=False) as f: + f.write(recovery_key) + recovery_key_file = f.name + + cmd(f'cryptsetup -q luksAddKey {image_path} {recovery_key_file} --key-file={key_file}') + + # Open encrypted volume and format with ext4 + cmd(f'cryptsetup -q open {image_path} vyos_config --key-file={key_file}') + cmd('mkfs.ext4 /dev/mapper/vyos_config') + + with TemporaryDirectory() as d: + cmd(f'mount /dev/mapper/vyos_config {d}') + + # Move /config to encrypted volume + shutil.copytree('/config', d, copy_function=shutil.move, dirs_exist_ok=True) + + cmd(f'umount {d}') + + os.unlink(key_file) + + if recovery_key: + os.unlink(recovery_key_file) + + for path in mount_paths: + cmd(f'mount /dev/mapper/vyos_config {path}') + cmd(f'chgrp vyattacfg {path}') + + return True + +def decrypt_config(key): + if not key: + return + + persist_path = cmd(persistpath_cmd).strip() + image_name = get_current_image() + image_path = os.path.join(persist_path, 'luks', image_name) + + if not os.path.exists(image_path): + raise Exception("Encrypted config volume doesn't exist") + + key_file = None + + if not is_opened(): + with NamedTemporaryFile(dir='/dev/shm', delete=False) as f: + f.write(key) + key_file = f.name + + cmd(f'cryptsetup -q open {image_path} vyos_config --key-file={key_file}') + + # unmount encrypted volume mount points + for path in mount_paths: + if os.path.ismount(path): + cmd(f'umount {path}') + + # If /config is populated, move to /config.old + if len(os.listdir('/config')) > 0: + print('Moving existing /config folder to /config.old') + shutil.move('/config', '/config.old') + + # Temporarily mount encrypted volume and migrate files to /config on rootfs + with TemporaryDirectory() as d: + cmd(f'mount /dev/mapper/vyos_config {d}') + + # Move encrypted volume to /config + shutil.copytree(d, '/config', copy_function=shutil.move, dirs_exist_ok=True) + cmd(f'chgrp -R vyattacfg /config') + + cmd(f'umount {d}') + + # Close encrypted volume + cmd('cryptsetup -q close vyos_config') + + # Remove encrypted volume image file and key + if key_file: + os.unlink(key_file) + os.unlink(image_path) + + try: + clear_tpm_key() + except: + pass + + return True + +if __name__ == '__main__': + if len(sys.argv) < 2: + print("Must specify action.") + sys.exit(1) + + parser = ArgumentParser(description='Config encryption') + parser.add_argument('--disable', help='Disable encryption', action="store_true") + parser.add_argument('--enable', help='Enable encryption', action="store_true") + parser.add_argument('--load', help='Load encrypted config volume', action="store_true") + args = parser.parse_args() + + tpm_exists = os.path.exists('/sys/class/tpm/tpm0') + + key = None + recovery_key = None + need_recovery = False + + question_key_str = 'recovery key' if tpm_exists else 'key' + + if tpm_exists: + if args.enable: + key = Fernet.generate_key() + elif args.disable or args.load: + try: + key = read_tpm_key() + need_recovery = False + except: + print('Failed to read key from TPM, recovery key required') + need_recovery = True + else: + need_recovery = True + + if args.enable and not tpm_exists: + print('WARNING: VyOS will boot into a default config when encrypted without a TPM') + print('You will need to manually login with default credentials and use "encryption load"') + print('to mount the encrypted volume and use "load /config/config.boot"') + + if not ask_yes_no('Are you sure you want to proceed?'): + sys.exit(0) + + if need_recovery or (args.enable and not ask_yes_no(f'Automatically generate a {question_key_str}?', default=True)): + while True: + recovery_key = ask_input(f'Enter {question_key_str}:', default=None).encode() + + if len(recovery_key) >= 32: + break + + print('Invalid key - must be at least 32 characters, try again.') + else: + recovery_key = Fernet.generate_key() + + try: + if args.disable: + decrypt_config(key or recovery_key) + + print('Encrypted config volume has been disabled') + print('Contents have been migrated to /config on rootfs') + elif args.load: + load_config(key or recovery_key) + + print('Encrypted config volume has been mounted') + print('Use "load /config/config.boot" to load configuration') + elif args.enable and tpm_exists: + encrypt_config(key, recovery_key) + + print('Encrypted config volume has been enabled with TPM') + print('Backup the recovery key in a safe place!') + print('Recovery key: ' + recovery_key.decode()) + elif args.enable: + encrypt_config(recovery_key) + + print('Encrypted config volume has been enabled without TPM') + print('Backup the key in a safe place!') + print('Key: ' + recovery_key.decode()) + except Exception as e: + word = 'decrypt' if args.disable or args.load else 'encrypt' + print(f'Failed to {word} config: {e}') diff --git a/src/helpers/vyos_config_sync.py b/src/helpers/vyos_config_sync.py index 7cfa8fe88..572fea61f 100755 --- a/src/helpers/vyos_config_sync.py +++ b/src/helpers/vyos_config_sync.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2023 VyOS maintainers and contributors +# Copyright (C) 2023-2024 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 @@ -60,6 +60,7 @@ def post_request(url: str, return response + def retrieve_config(section: str = None) -> Optional[Dict[str, Any]]: """Retrieves the configuration from the local server. @@ -71,8 +72,6 @@ def retrieve_config(section: str = None) -> Optional[Dict[str, Any]]: """ if section is None: section = [] - else: - section = section.split() conf = Config() config = conf.get_config_dict(section, get_first_key=True) @@ -101,8 +100,6 @@ def set_remote_config( if path is None: path = [] - else: - path = path.split() headers = {'Content-Type': 'application/json'} # Disable the InsecureRequestWarning @@ -127,17 +124,16 @@ def set_remote_config( def is_section_revised(section: str) -> bool: from vyos.config_mgmt import is_node_revised - return is_node_revised([section]) + return is_node_revised(section) def config_sync(secondary_address: str, secondary_key: str, - sections: List[str], + sections: List[list], mode: str): """Retrieve a config section from primary router in JSON format and send it to secondary router """ - # Config sync only if sections changed if not any(map(is_section_revised, sections)): return @@ -188,5 +184,17 @@ if __name__ == '__main__': "Missing required configuration data for config synchronization.") exit(0) + # Generate list_sections of sections/subsections + # [ + # ['interfaces', 'pseudo-ethernet'], ['interfaces', 'virtual-ethernet'], ['nat'], ['nat66'] + # ] + list_sections = [] + for section, subsections in sections.items(): + if subsections: + for subsection in subsections: + list_sections.append([section, subsection]) + else: + list_sections.append([section]) + config_sync(secondary_address, secondary_key, - sections, mode) + list_sections, mode) diff --git a/src/init/vyos-router b/src/init/vyos-router index eac3e7e47..adf892371 100755 --- a/src/init/vyos-router +++ b/src/init/vyos-router @@ -64,6 +64,69 @@ disabled () { grep -q -w no-vyos-$1 /proc/cmdline } +# Load encrypted config volume +mount_encrypted_config() { + persist_path=$(/opt/vyatta/sbin/vyos-persistpath) + if [ $? == 0 ]; then + if [ -e $persist_path/boot ]; then + image_name=$(cat /proc/cmdline | sed -e s+^.*vyos-union=/boot/++ | sed -e 's/ .*$//') + + if [ -z "$image_name" ]; then + return + fi + + if [ ! -f $persist_path/luks/$image_name ]; then + return + fi + + vyos_tpm_key=$(python3 -c 'from vyos.tpm import read_tpm_key; print(read_tpm_key().decode())' 2>/dev/null) + + if [ $? -ne 0 ]; then + echo "ERROR: Failed to fetch encryption key from TPM. Encrypted config volume has not been mounted" + echo "Use 'encryption load' to load volume with recovery key" + echo "or 'encryption disable' to decrypt volume with recovery key" + return + fi + + echo $vyos_tpm_key | tr -d '\r\n' | cryptsetup open $persist_path/luks/$image_name vyos_config --key-file=- + + if [ $? -ne 0 ]; then + echo "ERROR: Failed to decrypt config volume. Encrypted config volume has not been mounted" + echo "Use 'encryption load' to load volume with recovery key" + echo "or 'encryption disable' to decrypt volume with recovery key" + return + fi + + mount /dev/mapper/vyos_config /config + mount /dev/mapper/vyos_config $vyatta_sysconfdir/config + + echo "Mounted encrypted config volume" + fi + fi +} + +unmount_encrypted_config() { + persist_path=$(/opt/vyatta/sbin/vyos-persistpath) + if [ $? == 0 ]; then + if [ -e $persist_path/boot ]; then + image_name=$(cat /proc/cmdline | sed -e s+^.*vyos-union=/boot/++ | sed -e 's/ .*$//') + + if [ -z "$image_name" ]; then + return + fi + + if [ ! -f $persist_path/luks/$image_name ]; then + return + fi + + umount /config + umount $vyatta_sysconfdir/config + + cryptsetup close vyos_config + fi + fi +} + # if necessary, provide initial config init_bootfile () { if [ ! -r $BOOTFILE ] ; then @@ -218,8 +281,8 @@ cleanup_post_commit_hooks () { # note that this approach only supports hooks that are "configured", # i.e., it does not support hooks that need to always be present. cpostdir=$(cli-shell-api getPostCommitHookDir) - # exclude commits hooks from vyatta-cfg - excluded="10vyatta-log-commit.pl 99vyos-user-postcommit-hooks" + # exclude commit hooks that need to always be present + excluded="00vyos-sync 10vyatta-log-commit.pl 99vyos-user-postcommit-hooks" if [ -d "$cpostdir" ]; then for f in $cpostdir/*; do if [[ ! $excluded =~ $(basename $f) ]]; then @@ -402,6 +465,8 @@ start () && chgrp ${GROUP} ${vyatta_configdir} log_action_end_msg $? + mount_encrypted_config + # T5239: early read of system hostname as this value is read-only once during # FRR initialisation tmp=$(${vyos_libexec_dir}/read-saved-value.py --path "system host-name") @@ -470,6 +535,8 @@ stop() log_action_end_msg $? systemctl stop frr.service + + unmount_encrypted_config } case "$action" in diff --git a/src/migration-scripts/dhcp-server/6-to-7 b/src/migration-scripts/dhcp-server/6-to-7 index ccf385a30..e6c298a60 100755 --- a/src/migration-scripts/dhcp-server/6-to-7 +++ b/src/migration-scripts/dhcp-server/6-to-7 @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2023 VyOS maintainers and contributors +# Copyright (C) 2024 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,19 +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/>. -# T3316: Migrate to Kea -# - global-parameters will not function -# - shared-network-parameters will not function -# - subnet-parameters will not function -# - static-mapping-parameters will not function -# - host-decl-name is on by default, option removed -# - ping-check no longer supported -# - failover is default enabled on all subnets that exist on failover servers +# T6079: Disable duplicate static mappings import sys from vyos.configtree import ConfigTree -if (len(sys.argv) < 2): +if len(sys.argv) < 2: print("Must specify file name!") sys.exit(1) @@ -38,46 +31,42 @@ with open(file_name, 'r') as f: base = ['service', 'dhcp-server'] config = ConfigTree(config_file) -if not config.exists(base): +if not config.exists(base + ['shared-network-name']): # Nothing to do - sys.exit(0) + exit(0) -if config.exists(base + ['host-decl-name']): - config.delete(base + ['host-decl-name']) +# Run this for every instance if 'shared-network-name' +for network in config.list_nodes(base + ['shared-network-name']): + base_network = base + ['shared-network-name', network] -if config.exists(base + ['global-parameters']): - config.delete(base + ['global-parameters']) + if not config.exists(base_network + ['subnet']): + continue -if config.exists(base + ['shared-network-name']): - for network in config.list_nodes(base + ['shared-network-name']): - base_network = base + ['shared-network-name', network] + for subnet in config.list_nodes(base_network + ['subnet']): + base_subnet = base_network + ['subnet', subnet] - if config.exists(base_network + ['ping-check']): - config.delete(base_network + ['ping-check']) + if config.exists(base_subnet + ['static-mapping']): + used_mac = [] + used_ip = [] - if config.exists(base_network + ['shared-network-parameters']): - config.delete(base_network +['shared-network-parameters']) + for mapping in config.list_nodes(base_subnet + ['static-mapping']): + base_mapping = base_subnet + ['static-mapping', mapping] - if not config.exists(base_network + ['subnet']): - continue + if config.exists(base_mapping + ['mac-address']): + mac = config.return_value(base_mapping + ['mac-address']) - # Run this for every specified 'subnet' - for subnet in config.list_nodes(base_network + ['subnet']): - base_subnet = base_network + ['subnet', subnet] + if mac in used_mac: + config.set(base_mapping + ['disable']) + else: + used_mac.append(mac) - if config.exists(base_subnet + ['enable-failover']): - config.delete(base_subnet + ['enable-failover']) + if config.exists(base_mapping + ['ip-address']): + ip = config.return_value(base_mapping + ['ip-address']) - if config.exists(base_subnet + ['ping-check']): - config.delete(base_subnet + ['ping-check']) - - if config.exists(base_subnet + ['subnet-parameters']): - config.delete(base_subnet + ['subnet-parameters']) - - if config.exists(base_subnet + ['static-mapping']): - for mapping in config.list_nodes(base_subnet + ['static-mapping']): - if config.exists(base_subnet + ['static-mapping', mapping, 'static-mapping-parameters']): - config.delete(base_subnet + ['static-mapping', mapping, 'static-mapping-parameters']) + if ip in used_ip: + config.set(base_subnet + ['static-mapping', mapping, 'disable']) + else: + used_ip.append(ip) try: with open(file_name, 'w') as f: diff --git a/src/migration-scripts/dhcp-server/7-to-8 b/src/migration-scripts/dhcp-server/7-to-8 index 151aa6d7b..ccf385a30 100755 --- a/src/migration-scripts/dhcp-server/7-to-8 +++ b/src/migration-scripts/dhcp-server/7-to-8 @@ -14,16 +14,19 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# T3316: -# - Adjust hostname to have valid FQDN characters only (underscores aren't allowed anymore) -# - Rename "service dhcp-server shared-network-name ... static-mapping <hostname> mac-address ..." -# to "service dhcp-server shared-network-name ... static-mapping <hostname> mac ..." +# T3316: Migrate to Kea +# - global-parameters will not function +# - shared-network-parameters will not function +# - subnet-parameters will not function +# - static-mapping-parameters will not function +# - host-decl-name is on by default, option removed +# - ping-check no longer supported +# - failover is default enabled on all subnets that exist on failover servers import sys -import re from vyos.configtree import ConfigTree -if len(sys.argv) < 2: +if (len(sys.argv) < 2): print("Must specify file name!") sys.exit(1) @@ -32,30 +35,49 @@ file_name = sys.argv[1] with open(file_name, 'r') as f: config_file = f.read() -base = ['service', 'dhcp-server', 'shared-network-name'] +base = ['service', 'dhcp-server'] config = ConfigTree(config_file) if not config.exists(base): # Nothing to do sys.exit(0) -for network in config.list_nodes(base): - # Run this for every specified 'subnet' - if config.exists(base + [network, 'subnet']): - for subnet in config.list_nodes(base + [network, 'subnet']): - base_subnet = base + [network, 'subnet', subnet] - if config.exists(base_subnet + ['static-mapping']): - for hostname in config.list_nodes(base_subnet + ['static-mapping']): - base_mapping = base_subnet + ['static-mapping', hostname] +if config.exists(base + ['host-decl-name']): + config.delete(base + ['host-decl-name']) + +if config.exists(base + ['global-parameters']): + config.delete(base + ['global-parameters']) + +if config.exists(base + ['shared-network-name']): + for network in config.list_nodes(base + ['shared-network-name']): + base_network = base + ['shared-network-name', network] + + if config.exists(base_network + ['ping-check']): + config.delete(base_network + ['ping-check']) + + if config.exists(base_network + ['shared-network-parameters']): + config.delete(base_network +['shared-network-parameters']) - # Rename the 'mac-address' node to 'mac' - if config.exists(base_mapping + ['mac-address']): - config.rename(base_mapping + ['mac-address'], 'mac') + if not config.exists(base_network + ['subnet']): + continue - # Adjust hostname to have valid FQDN characters only - new_hostname = re.sub(r'[^a-zA-Z0-9-.]', '-', hostname) - if new_hostname != hostname: - config.rename(base_mapping, new_hostname) + # Run this for every specified 'subnet' + for subnet in config.list_nodes(base_network + ['subnet']): + base_subnet = base_network + ['subnet', subnet] + + if config.exists(base_subnet + ['enable-failover']): + config.delete(base_subnet + ['enable-failover']) + + if config.exists(base_subnet + ['ping-check']): + config.delete(base_subnet + ['ping-check']) + + if config.exists(base_subnet + ['subnet-parameters']): + config.delete(base_subnet + ['subnet-parameters']) + + if config.exists(base_subnet + ['static-mapping']): + for mapping in config.list_nodes(base_subnet + ['static-mapping']): + if config.exists(base_subnet + ['static-mapping', mapping, 'static-mapping-parameters']): + config.delete(base_subnet + ['static-mapping', mapping, 'static-mapping-parameters']) try: with open(file_name, 'w') as f: diff --git a/src/migration-scripts/dhcp-server/8-to-9 b/src/migration-scripts/dhcp-server/8-to-9 index 810e403a6..151aa6d7b 100755 --- a/src/migration-scripts/dhcp-server/8-to-9 +++ b/src/migration-scripts/dhcp-server/8-to-9 @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2024 VyOS maintainers and contributors +# Copyright (C) 2023 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 @@ -15,8 +15,9 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # T3316: -# - Migrate dhcp options under new option node -# - Add subnet IDs to existing subnets +# - Adjust hostname to have valid FQDN characters only (underscores aren't allowed anymore) +# - Rename "service dhcp-server shared-network-name ... static-mapping <hostname> mac-address ..." +# to "service dhcp-server shared-network-name ... static-mapping <hostname> mac ..." import sys import re @@ -38,34 +39,23 @@ if not config.exists(base): # Nothing to do sys.exit(0) -option_nodes = ['bootfile-name', 'bootfile-server', 'bootfile-size', 'captive-portal', - 'client-prefix-length', 'default-router', 'domain-name', 'domain-search', - 'name-server', 'ip-forwarding', 'ipv6-only-preferred', 'ntp-server', - 'pop-server', 'server-identifier', 'smtp-server', 'static-route', - 'tftp-server-name', 'time-offset', 'time-server', 'time-zone', - 'vendor-option', 'wins-server', 'wpad-url'] - -subnet_id = 1 - for network in config.list_nodes(base): - for option in option_nodes: - if config.exists(base + [network, option]): - config.set(base + [network, 'option']) - config.copy(base + [network, option], base + [network, 'option', option]) - config.delete(base + [network, option]) - + # Run this for every specified 'subnet' if config.exists(base + [network, 'subnet']): for subnet in config.list_nodes(base + [network, 'subnet']): base_subnet = base + [network, 'subnet', subnet] - - for option in option_nodes: - if config.exists(base_subnet + [option]): - config.set(base_subnet + ['option']) - config.copy(base_subnet + [option], base_subnet + ['option', option]) - config.delete(base_subnet + [option]) + if config.exists(base_subnet + ['static-mapping']): + for hostname in config.list_nodes(base_subnet + ['static-mapping']): + base_mapping = base_subnet + ['static-mapping', hostname] + + # Rename the 'mac-address' node to 'mac' + if config.exists(base_mapping + ['mac-address']): + config.rename(base_mapping + ['mac-address'], 'mac') - config.set(base_subnet + ['subnet-id'], value=subnet_id) - subnet_id += 1 + # Adjust hostname to have valid FQDN characters only + new_hostname = re.sub(r'[^a-zA-Z0-9-.]', '-', hostname) + if new_hostname != hostname: + config.rename(base_mapping, new_hostname) try: with open(file_name, 'w') as f: diff --git a/src/migration-scripts/dhcp-server/9-to-10 b/src/migration-scripts/dhcp-server/9-to-10 new file mode 100755 index 000000000..810e403a6 --- /dev/null +++ b/src/migration-scripts/dhcp-server/9-to-10 @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 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/>. + +# T3316: +# - Migrate dhcp options under new option node +# - Add subnet IDs to existing subnets + +import sys +import re +from vyos.configtree import ConfigTree + +if len(sys.argv) < 2: + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['service', 'dhcp-server', 'shared-network-name'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + sys.exit(0) + +option_nodes = ['bootfile-name', 'bootfile-server', 'bootfile-size', 'captive-portal', + 'client-prefix-length', 'default-router', 'domain-name', 'domain-search', + 'name-server', 'ip-forwarding', 'ipv6-only-preferred', 'ntp-server', + 'pop-server', 'server-identifier', 'smtp-server', 'static-route', + 'tftp-server-name', 'time-offset', 'time-server', 'time-zone', + 'vendor-option', 'wins-server', 'wpad-url'] + +subnet_id = 1 + +for network in config.list_nodes(base): + for option in option_nodes: + if config.exists(base + [network, option]): + config.set(base + [network, 'option']) + config.copy(base + [network, option], base + [network, 'option', option]) + config.delete(base + [network, option]) + + if config.exists(base + [network, 'subnet']): + for subnet in config.list_nodes(base + [network, 'subnet']): + base_subnet = base + [network, 'subnet', subnet] + + for option in option_nodes: + if config.exists(base_subnet + [option]): + config.set(base_subnet + ['option']) + config.copy(base_subnet + [option], base_subnet + ['option', option]) + config.delete(base_subnet + [option]) + + config.set(base_subnet + ['subnet-id'], value=subnet_id) + subnet_id += 1 + +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) diff --git a/src/migration-scripts/dhcpv6-server/4-to-5 b/src/migration-scripts/dhcpv6-server/4-to-5 index ae506b9c5..55fda91b3 100755 --- a/src/migration-scripts/dhcpv6-server/4-to-5 +++ b/src/migration-scripts/dhcpv6-server/4-to-5 @@ -42,8 +42,11 @@ def find_subnet_interface(subnet): def check_addr(if_path): if config.exists(if_path + ['address']): for addr in config.return_values(if_path + ['address']): - if ip_network(addr, strict=False) == subnet_net: - return True + try: + if ip_network(addr, strict=False) == subnet_net: + return True + except: + pass # interface address was probably "dhcp" or other magic string return None for iftype in config.list_nodes(['interfaces']): diff --git a/src/migration-scripts/firewall/6-to-7 b/src/migration-scripts/firewall/6-to-7 index b918833e9..72f07880b 100755 --- a/src/migration-scripts/firewall/6-to-7 +++ b/src/migration-scripts/firewall/6-to-7 @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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 @@ -19,6 +19,7 @@ # utc: nftables userspace uses localtime and calculates the UTC offset automatically # icmp/v6: migrate previously available `type-name` to valid type/code # T4178: Update tcp flags to use multi value node +# T6071: CLI description limit of 256 characters import re @@ -26,7 +27,6 @@ from sys import argv from sys import exit from vyos.configtree import ConfigTree -from vyos.ifconfig import Section if len(argv) < 2: print("Must specify file name!") @@ -37,6 +37,8 @@ file_name = argv[1] with open(file_name, 'r') as f: config_file = f.read() +max_len_description = 255 + base = ['firewall'] config = ConfigTree(config_file) @@ -105,12 +107,30 @@ icmpv6_translations = { 'unknown-option': [4, 2] } +if config.exists(base + ['group']): + for group_type in config.list_nodes(base + ['group']): + for group_name in config.list_nodes(base + ['group', group_type]): + name_description = base + ['group', group_type, group_name, 'description'] + if config.exists(name_description): + tmp = config.return_value(name_description) + config.set(name_description, value=tmp[:max_len_description]) + if config.exists(base + ['name']): for name in config.list_nodes(base + ['name']): + name_description = base + ['name', name, 'description'] + if config.exists(name_description): + tmp = config.return_value(name_description) + config.set(name_description, value=tmp[:max_len_description]) + if not config.exists(base + ['name', name, 'rule']): continue for rule in config.list_nodes(base + ['name', name, 'rule']): + rule_description = base + ['name', name, 'rule', rule, 'description'] + if config.exists(rule_description): + tmp = config.return_value(rule_description) + config.set(rule_description, value=tmp[:max_len_description]) + rule_recent = base + ['name', name, 'rule', rule, 'recent'] rule_time = base + ['name', name, 'rule', rule, 'time'] rule_tcp_flags = base + ['name', name, 'rule', rule, 'tcp', 'flags'] @@ -161,10 +181,20 @@ if config.exists(base + ['name']): if config.exists(base + ['ipv6-name']): for name in config.list_nodes(base + ['ipv6-name']): + name_description = base + ['ipv6-name', name, 'description'] + if config.exists(name_description): + tmp = config.return_value(name_description) + config.set(name_description, value=tmp[:max_len_description]) + if not config.exists(base + ['ipv6-name', name, 'rule']): continue for rule in config.list_nodes(base + ['ipv6-name', name, 'rule']): + rule_description = base + ['ipv6-name', name, 'rule', rule, 'description'] + if config.exists(rule_description): + tmp = config.return_value(rule_description) + config.set(rule_description, value=tmp[:max_len_description]) + rule_recent = base + ['ipv6-name', name, 'rule', rule, 'recent'] rule_time = base + ['ipv6-name', name, 'rule', rule, 'time'] rule_tcp_flags = base + ['ipv6-name', name, 'rule', rule, 'tcp', 'flags'] diff --git a/src/migration-scripts/policy/1-to-2 b/src/migration-scripts/policy/1-to-2 index c70490ce9..c7a983bba 100755 --- a/src/migration-scripts/policy/1-to-2 +++ b/src/migration-scripts/policy/1-to-2 @@ -32,23 +32,23 @@ file_name = argv[1] with open(file_name, 'r') as f: config_file = f.read() -base = ['policy', 'ipv6-route'] +base = ['policy'] config = ConfigTree(config_file) if not config.exists(base): # Nothing to do exit(0) -config.rename(base, 'route6') -config.set_tag(['policy', 'route6']) +if config.exists(base + ['ipv6-route']): + config.rename(base + ['ipv6-route'],'route6') + config.set_tag(['policy', 'route6']) for route in ['route', 'route6']: - route_path = ['policy', route] - if config.exists(route_path): - for name in config.list_nodes(route_path): - if config.exists(route_path + [name, 'rule']): - for rule in config.list_nodes(route_path + [name, 'rule']): - rule_tcp_flags = route_path + [name, 'rule', rule, 'tcp', 'flags'] + if config.exists(base + [route]): + for name in config.list_nodes(base + [route]): + if config.exists(base + [route, name, 'rule']): + for rule in config.list_nodes(base + [route, name, 'rule']): + rule_tcp_flags = base + [route, name, 'rule', rule, 'tcp', 'flags'] if config.exists(rule_tcp_flags): tmp = config.return_value(rule_tcp_flags) diff --git a/src/op_mode/clear_dhcp_lease.py b/src/op_mode/clear_dhcp_lease.py deleted file mode 100755 index 7d4b47104..000000000 --- a/src/op_mode/clear_dhcp_lease.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see <http://www.gnu.org/licenses/>. - -import argparse -import re - -from vyos.configquery import ConfigTreeQuery -from vyos.kea import kea_parse_leases -from vyos.utils.io import ask_yes_no -from vyos.utils.process import call -from vyos.utils.commit import commit_in_progress - -# TODO: Update to use Kea control socket command "lease4-del" - -config = ConfigTreeQuery() -base = ['service', 'dhcp-server'] -lease_file = '/config/dhcp/dhcp4-leases.csv' - - -def del_lease_ip(address): - """ - Read lease_file and write data to this file - without specific section "lease ip" - Delete section "lease x.x.x.x { x;x;x; }" - """ - with open(lease_file, encoding='utf-8') as f: - data = f.read().rstrip() - pattern = rf"^{address},[^\n]+\n" - # Delete lease for ip block - data = re.sub(pattern, '', data) - - # Write new data to original lease_file - with open(lease_file, 'w', encoding='utf-8') as f: - f.write(data) - -def is_ip_in_leases(address): - """ - Return True if address found in the lease file - """ - leases = kea_parse_leases(lease_file) - for lease in leases: - if address == lease['address']: - return True - print(f'Address "{address}" not found in "{lease_file}"') - return False - -if not config.exists(base): - print('DHCP-server not configured!') - exit(0) - -if config.exists(base + ['failover']): - print('Lease cannot be reset in failover mode!') - exit(0) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('--ip', help='IPv4 address', action='store', required=True) - - args = parser.parse_args() - address = args.ip - - if not is_ip_in_leases(address): - exit(1) - - if commit_in_progress(): - print('Cannot clear DHCP lease while a commit is in progress') - exit(1) - - if not ask_yes_no(f'This will restart DHCP server.\nContinue?'): - exit(1) - else: - del_lease_ip(address) - call('systemctl restart kea-dhcp4-server.service') diff --git a/src/op_mode/conntrack.py b/src/op_mode/conntrack.py index cf8adf795..6ea213bec 100755 --- a/src/op_mode/conntrack.py +++ b/src/op_mode/conntrack.py @@ -112,7 +112,8 @@ def get_formatted_output(dict_data): proto = meta['layer4']['protoname'] if direction == 'independent': conn_id = meta['id'] - timeout = meta['timeout'] + # T6138 flowtable offload conntrack entries without 'timeout' + timeout = meta.get('timeout', 'n/a') orig_src = f'{orig_src}:{orig_sport}' if orig_sport else orig_src orig_dst = f'{orig_dst}:{orig_dport}' if orig_dport else orig_dst reply_src = f'{reply_src}:{reply_sport}' if reply_sport else reply_src diff --git a/src/op_mode/container.py b/src/op_mode/container.py index 5a022d0c0..d29af8821 100755 --- a/src/op_mode/container.py +++ b/src/op_mode/container.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-2024 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 @@ -20,6 +20,8 @@ import sys from sys import exit from vyos.utils.process import cmd +from vyos.utils.process import call +from vyos.utils.process import rc_cmd import vyos.opmode @@ -29,23 +31,51 @@ def _get_json_data(command: str) -> list: """ return cmd(f'{command} --format json') - def _get_raw_data(command: str) -> list: json_data = _get_json_data(command) data = json.loads(json_data) return data def add_image(name: str): - from vyos.utils.process import rc_cmd + """ Pull image from container registry. If registry authentication + is defined within VyOS CLI, credentials are used to login befroe pull """ + from vyos.configquery import ConfigTreeQuery + + conf = ConfigTreeQuery() + container = conf.get_config_dict(['container', 'registry']) + + do_logout = False + if 'registry' in container: + for registry, registry_config in container['registry'].items(): + if 'disable' in registry_config: + continue + if 'authentication' in registry_config: + do_logout = True + if {'username', 'password'} <= set(registry_config['authentication']): + username = registry_config['authentication']['username'] + password = registry_config['authentication']['password'] + cmd = f'podman login --username {username} --password {password} {registry}' + rc, out = rc_cmd(cmd) + if rc != 0: raise vyos.opmode.InternalError(out) rc, output = rc_cmd(f'podman image pull {name}') if rc != 0: raise vyos.opmode.InternalError(output) + if do_logout: + rc_cmd('podman logout --all') + def delete_image(name: str): from vyos.utils.process import rc_cmd - rc, output = rc_cmd(f'podman image rm --force {name}') + if name == 'all': + # gather list of all images and pass them to the removal list + name = cmd('sudo podman image ls --quiet') + # If there are no container images left, we can not delete them all + if not name: return + # replace newline with whitespace + name = name.replace('\n', ' ') + rc, output = rc_cmd(f'podman image rm {name}') if rc != 0: raise vyos.opmode.InternalError(output) @@ -57,7 +87,6 @@ def show_container(raw: bool): else: return cmd(command) - def show_image(raw: bool): command = 'podman image ls' container_data = _get_raw_data('podman image ls') @@ -66,7 +95,6 @@ def show_image(raw: bool): else: return cmd(command) - def show_network(raw: bool): command = 'podman network ls' container_data = _get_raw_data(command) @@ -75,7 +103,6 @@ def show_network(raw: bool): else: return cmd(command) - def restart(name: str): from vyos.utils.process import rc_cmd @@ -86,7 +113,6 @@ def restart(name: str): print(f'Container "{name}" restarted!') return output - if __name__ == '__main__': try: res = vyos.opmode.run(sys.modules[__name__]) diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py index 1d9ad0e76..d27e1baf7 100755 --- a/src/op_mode/dhcp.py +++ b/src/op_mode/dhcp.py @@ -31,6 +31,7 @@ from vyos.configquery import ConfigTreeQuery from vyos.kea import kea_get_active_config from vyos.kea import kea_get_leases from vyos.kea import kea_get_pool_from_subnet_id +from vyos.kea import kea_delete_lease from vyos.utils.process import is_systemd_service_running time_string = "%a %b %d %H:%M:%S %Z %Y" @@ -360,6 +361,28 @@ def show_server_static_mappings(raw: bool, family: ArgFamily, pool: typing.Optio else: return _get_formatted_server_static_mappings(static_mappings, family=family) +def _lease_valid(inet, address): + leases = kea_get_leases(inet) + for lease in leases: + if address == lease['ip-address']: + return True + return False + +@_verify +def clear_dhcp_server_lease(family: ArgFamily, address: str): + v = 'v6' if family == 'inet6' else '' + inet = '6' if family == 'inet6' else '4' + + if not _lease_valid(inet, address): + print(f'Lease not found on DHCP{v} server') + return None + + if not kea_delete_lease(inet, address): + print(f'Failed to clear lease for "{address}"') + return None + + print(f'Lease "{address}" has been cleared') + def _get_raw_client_leases(family='inet', interface=None): from time import mktime from datetime import datetime diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py index d677c2cf8..b0567305a 100755 --- a/src/op_mode/image_installer.py +++ b/src/op_mode/image_installer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> # # This file is part of VyOS. # @@ -65,6 +65,8 @@ MSG_INPUT_ROOT_SIZE_SET: str = 'Please specify the size (in GB) of the root part MSG_INPUT_CONSOLE_TYPE: str = 'What console should be used by default? (K: KVM, S: Serial, U: USB-Serial)?' MSG_INPUT_COPY_DATA: str = 'Would you like to copy data to the new image?' MSG_INPUT_CHOOSE_COPY_DATA: str = 'From which image would you like to save config information?' +MSG_INPUT_COPY_ENC_DATA: str = 'Would you like to copy the encrypted config to the new image?' +MSG_INPUT_CHOOSE_COPY_ENC_DATA: str = 'From which image would you like to copy the encrypted config?' MSG_WARN_ISO_SIGN_INVALID: str = 'Signature is not valid. Do you want to continue with installation?' MSG_WARN_ISO_SIGN_UNAVAL: str = 'Signature is not available. Do you want to continue with installation?' MSG_WARN_ROOT_SIZE_TOOBIG: str = 'The size is too big. Try again.' @@ -212,14 +214,17 @@ def search_previous_installation(disks: list[str]) -> None: disks (list[str]): a list of available disks """ mnt_config = '/mnt/config' + mnt_encrypted_config = '/mnt/encrypted_config' mnt_ssh = '/mnt/ssh' mnt_tmp = '/mnt/tmp' rmtree(Path(mnt_config), ignore_errors=True) rmtree(Path(mnt_ssh), ignore_errors=True) Path(mnt_tmp).mkdir(exist_ok=True) + Path(mnt_encrypted_config).unlink(missing_ok=True) print('Searching for data from previous installations') image_data = [] + encrypted_configs = [] for disk_name in disks: for partition in disk.partition_list(disk_name): if disk.partition_mount(partition, mnt_tmp): @@ -227,32 +232,61 @@ def search_previous_installation(disks: list[str]) -> None: for path in Path(mnt_tmp + '/boot').iterdir(): if path.joinpath('rw/config/.vyatta_config').exists(): image_data.append((path.name, partition)) + if Path(mnt_tmp + '/luks').exists(): + for path in Path(mnt_tmp + '/luks').iterdir(): + encrypted_configs.append((path.name, partition)) disk.partition_umount(partition) - if len(image_data) == 1: - image_name, image_drive = image_data[0] - print('Found data from previous installation:') - print(f'\t{image_name} on {image_drive}') - if not ask_yes_no(MSG_INPUT_COPY_DATA, default=True): - return - - elif len(image_data) > 1: - print('Found data from previous installations') - if not ask_yes_no(MSG_INPUT_COPY_DATA, default=True): - return - - image_name, image_drive = select_entry(image_data, - 'Available versions:', - MSG_INPUT_CHOOSE_COPY_DATA, - search_format_selection) + image_name = None + image_drive = None + encrypted = False + + if len(image_data) > 0: + if len(image_data) == 1: + print('Found data from previous installation:') + print(f'\t{" on ".join(image_data[0])}') + if ask_yes_no(MSG_INPUT_COPY_DATA, default=True): + image_name, image_drive = image_data[0] + + elif len(image_data) > 1: + print('Found data from previous installations') + if ask_yes_no(MSG_INPUT_COPY_DATA, default=True): + image_name, image_drive = select_entry(image_data, + 'Available versions:', + MSG_INPUT_CHOOSE_COPY_DATA, + search_format_selection) + elif len(encrypted_configs) > 0: + if len(encrypted_configs) == 1: + print('Found encrypted config from previous installation:') + print(f'\t{" on ".join(encrypted_configs[0])}') + if ask_yes_no(MSG_INPUT_COPY_ENC_DATA, default=True): + image_name, image_drive = encrypted_configs[0] + encrypted = True + + elif len(encrypted_configs) > 1: + print('Found encrypted configs from previous installations') + if ask_yes_no(MSG_INPUT_COPY_ENC_DATA, default=True): + image_name, image_drive = select_entry(encrypted_configs, + 'Available versions:', + MSG_INPUT_CHOOSE_COPY_ENC_DATA, + search_format_selection) + encrypted = True + else: print('No previous installation found') return + if not image_name: + return + disk.partition_mount(image_drive, mnt_tmp) - copytree(f'{mnt_tmp}/boot/{image_name}/rw/config', mnt_config) + if not encrypted: + copytree(f'{mnt_tmp}/boot/{image_name}/rw/config', mnt_config) + else: + copy(f'{mnt_tmp}/luks/{image_name}', mnt_encrypted_config) + Path(mnt_ssh).mkdir() host_keys: list[str] = glob(f'{mnt_tmp}/boot/{image_name}/rw/etc/ssh/ssh_host*') for host_key in host_keys: @@ -279,6 +313,12 @@ def copy_previous_installation_data(target_dir: str) -> None: dirs_exist_ok=True) +def copy_previous_encrypted_config(target_dir: str, image_name: str) -> None: + if Path('/mnt/encrypted_config').exists(): + Path(target_dir).mkdir(exist_ok=True) + copy('/mnt/encrypted_config', Path(target_dir).joinpath(image_name)) + + def ask_single_disk(disks_available: dict[str, int]) -> str: """Ask user to select a disk for installation @@ -712,6 +752,9 @@ def install_image() -> None: # owner restored on copy of config data by chmod_2775, above copy_previous_installation_data(f'{DIR_DST_ROOT}/boot/{image_name}/rw') + # copy saved encrypted config volume + copy_previous_encrypted_config(f'{DIR_DST_ROOT}/luks', image_name) + if is_raid_install(install_target): write_dir: str = f'{DIR_DST_ROOT}/boot/{image_name}/rw' raid.update_default(write_dir) @@ -743,6 +786,10 @@ def install_image() -> None: grub.install(install_target.name, f'{DIR_DST_ROOT}/boot/', f'{DIR_DST_ROOT}/boot/efi') + # sort inodes (to make GRUB read config files in alphabetical order) + grub.sort_inodes(f'{DIR_DST_ROOT}/{grub.GRUB_DIR_VYOS}') + grub.sort_inodes(f'{DIR_DST_ROOT}/{grub.GRUB_DIR_VYOS_VERS}') + # umount filesystems and remove temporary files if is_raid_install(install_target): cleanup([install_target.name], diff --git a/src/op_mode/image_manager.py b/src/op_mode/image_manager.py index e64a85b95..1510a667c 100755 --- a/src/op_mode/image_manager.py +++ b/src/op_mode/image_manager.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> # # This file is part of VyOS. # @@ -95,6 +95,15 @@ def delete_image(image_name: Optional[str] = None, except Exception as err: exit(f'Unable to remove the image "{image_name}": {err}') + # remove LUKS volume if it exists + luks_path: Path = Path(f'{persistence_storage}/luks/{image_name}') + if luks_path.is_file(): + try: + luks_path.unlink() + print(f'The encrypted config for "{image_name}" was successfully deleted') + except Exception as err: + exit(f'Unable to remove the encrypted config for "{image_name}": {err}') + @compat.grub_cfg_update def set_image(image_name: Optional[str] = None, @@ -174,6 +183,16 @@ def rename_image(name_old: str, name_new: str) -> None: except Exception as err: exit(f'Unable to rename image "{name_old}" to "{name_new}": {err}') + # rename LUKS volume if it exists + old_luks_path: Path = Path(f'{persistence_storage}/luks/{name_old}') + if old_luks_path.is_file(): + try: + new_luks_path: Path = Path(f'{persistence_storage}/luks/{name_new}') + old_luks_path.rename(new_luks_path) + print(f'The encrypted config for "{name_old}" was successfully renamed to "{name_new}"') + except Exception as err: + exit(f'Unable to rename the encrypted config for "{name_old}" to "{name_new}": {err}') + def list_images() -> None: """Print list of available images for CLI hints""" diff --git a/src/services/vyos-configd b/src/services/vyos-configd index 355182b26..648a017d5 100755 --- a/src/services/vyos-configd +++ b/src/services/vyos-configd @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2023 VyOS maintainers and contributors +# Copyright (C) 2020-2024 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 @@ -19,6 +19,7 @@ import sys import grp import re import json +import typing import logging import signal import importlib.util @@ -29,6 +30,7 @@ from vyos.defaults import directories from vyos.utils.boot import boot_configuration_complete from vyos.configsource import ConfigSourceString from vyos.configsource import ConfigSourceError +from vyos.configdep import call_dependents from vyos.config import Config from vyos import ConfigError @@ -198,10 +200,12 @@ def initialization(socket): return None config = Config(config_source=configsource) + dependent_func: dict[str, list[typing.Callable]] = {} + setattr(config, 'dependent_func', dependent_func) return config -def process_node_data(config, data) -> int: +def process_node_data(config, data, last: bool = False) -> int: if not config: logger.critical(f"Empty config") return R_ERROR_DAEMON @@ -223,11 +227,18 @@ def process_node_data(config, data) -> int: args.insert(0, f'{script_name}.py') if script_name not in include_set: + # call dependents now if last element of prio queue is run + # independent of configd + if last: + call_dependents(dependent_func=config.dependent_func) return R_PASS with stdout_redirected(session_out, session_mode): result = run_script(conf_mode_scripts[script_name], config, args) + if last: + call_dependents(dependent_func=config.dependent_func) + return result def remove_if_file(f: str): @@ -281,7 +292,9 @@ if __name__ == '__main__': socket.send(resp.encode()) config = initialization(socket) elif message["type"] == "node": - res = process_node_data(config, message["data"]) + if message["last"]: + logger.debug(f'final element of priority queue') + res = process_node_data(config, message["data"], message["last"]) response = res.to_bytes(1, byteorder=sys.byteorder) logger.debug(f"Sending response {res}") socket.send(response) diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server index 40d442e30..a7b14a1a3 100755 --- a/src/services/vyos-http-api-server +++ b/src/services/vyos-http-api-server @@ -459,7 +459,6 @@ def _configure_op(data: Union[ConfigureModel, ConfigureListModel, request: Request, background_tasks: BackgroundTasks): session = app.state.vyos_session env = session.get_session_env() - config = Config(session_env=env) endpoint = request.url.path @@ -474,6 +473,8 @@ def _configure_op(data: Union[ConfigureModel, ConfigureListModel, # so the lock is really global lock.acquire() + config = Config(session_env=env) + status = 200 msg = None error_msg = None diff --git a/src/shim/vyshim.c b/src/shim/vyshim.c index cae8b6152..41723e7a4 100644 --- a/src/shim/vyshim.c +++ b/src/shim/vyshim.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 VyOS maintainers and contributors + * Copyright (C) 2020-2024 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 @@ -49,6 +49,7 @@ #define GET_SESSION "cli-shell-api --show-working-only --show-show-defaults --show-ignore-edit showConfig" #define COMMIT_MARKER "/var/tmp/initial_in_commit" +#define QUEUE_MARKER "/var/tmp/last_in_queue" enum { SUCCESS = 1 << 0, @@ -77,6 +78,7 @@ int main(int argc, char* argv[]) int ex_index; int init_timeout = 0; + int last = 0; debug_print("Connecting to vyos-configd ...\n"); zmq_connect(requester, SOCKET_PATH); @@ -101,10 +103,16 @@ int main(int argc, char* argv[]) return ret; } + if (access(QUEUE_MARKER, F_OK) != -1) { + last = 1; + remove(QUEUE_MARKER); + } + char error_code[1]; debug_print("Sending node data ...\n"); - char *string_node_data_msg = mkjson(MKJSON_OBJ, 2, + char *string_node_data_msg = mkjson(MKJSON_OBJ, 3, MKJSON_STRING, "type", "node", + MKJSON_BOOL, "last", last, MKJSON_STRING, "data", &string_node_data[0]); zmq_send(requester, string_node_data_msg, strlen(string_node_data_msg), 0); diff --git a/src/system/grub_update.py b/src/system/grub_update.py index 5a7d8eb72..5a0534195 100644 --- a/src/system/grub_update.py +++ b/src/system/grub_update.py @@ -105,4 +105,8 @@ if __name__ == '__main__': else: render(grub_cfg_main, grub.TMPL_GRUB_MAIN, {}) + # sort inodes (to make GRUB read config files in alphabetical order) + grub.sort_inodes(f'{root_dir}/{grub.GRUB_DIR_VYOS}') + grub.sort_inodes(f'{root_dir}/{grub.GRUB_DIR_VYOS_VERS}') + exit(0) diff --git a/src/system/vyos-event-handler.py b/src/system/vyos-event-handler.py index 74112ec91..dd2793046 100755 --- a/src/system/vyos-event-handler.py +++ b/src/system/vyos-event-handler.py @@ -153,7 +153,12 @@ if __name__ == '__main__': continue for entry in data: message = entry['MESSAGE'] - pid = entry['_PID'] + pid = -1 + try: + pid = entry['_PID'] + except Exception as ex: + journal.send(f'Unable to extract PID from message entry: {entry}', SYSLOG_IDENTIFIER=my_name) + continue # Skip empty messages and messages from this process if message and pid != my_pid: try: |