summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/firewall/nftables-bridge.j235
-rw-r--r--data/templates/firewall/nftables-defines.j214
-rw-r--r--data/templates/firewall/nftables-nat.j22
-rw-r--r--data/templates/firewall/nftables-policy.j24
-rw-r--r--data/templates/firewall/nftables.j215
-rw-r--r--debian/control1
-rw-r--r--interface-definitions/firewall.xml.in9
-rw-r--r--interface-definitions/include/firewall/action-l2.xml.i37
-rw-r--r--interface-definitions/include/firewall/action.xml.i8
-rw-r--r--interface-definitions/include/firewall/bridge-custom-name.xml.i39
-rw-r--r--interface-definitions/include/firewall/bridge-hook-forward.xml.i34
-rw-r--r--interface-definitions/include/firewall/common-rule-bridge.xml.i34
-rw-r--r--interface-definitions/include/firewall/common-rule-inet.xml.i7
-rw-r--r--interface-definitions/include/firewall/default-action-bridge.xml.i34
-rw-r--r--interface-definitions/include/firewall/default-action.xml.i10
-rw-r--r--interface-definitions/include/firewall/match-vlan.xml.i41
-rw-r--r--interface-definitions/system-config-mgmt.xml.in29
-rw-r--r--op-mode-definitions/firewall.xml.in84
-rw-r--r--op-mode-definitions/show-interfaces.xml.in4
-rw-r--r--python/vyos/config_mgmt.py20
-rw-r--r--python/vyos/firewall.py2
-rw-r--r--python/vyos/remote.py131
-rwxr-xr-xscripts/build-command-templates2
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py35
-rwxr-xr-xsrc/op_mode/interfaces.py7
25 files changed, 583 insertions, 55 deletions
diff --git a/data/templates/firewall/nftables-bridge.j2 b/data/templates/firewall/nftables-bridge.j2
new file mode 100644
index 000000000..1a4ad2ed9
--- /dev/null
+++ b/data/templates/firewall/nftables-bridge.j2
@@ -0,0 +1,35 @@
+{% macro bridge(bridge) %}
+{% set ns = namespace(sets=[]) %}
+{% if bridge.forward is vyos_defined %}
+{% for prior, conf in bridge.forward.items() %}
+{% set def_action = conf.default_action %}
+ chain VYOS_FORWARD_{{ prior }} {
+ type filter hook forward priority {{ prior }}; policy {{ def_action }};
+{% if conf.rule is vyos_defined %}
+{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
+ {{ rule_conf | nft_rule('FWD', prior, rule_id, 'bri') }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + ['FWD_' + prior + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
+ }
+{% endfor %}
+{% endif %}
+
+{% if bridge.name is vyos_defined %}
+{% for name_text, conf in bridge.name.items() %}
+ chain NAME_{{ name_text }} {
+{% if conf.rule is vyos_defined %}
+{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
+ {{ rule_conf | nft_rule('NAM', name_text, rule_id, 'bri') }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + ['NAM_' + name_text + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
+ {{ conf | nft_default_rule(name_text) }}
+ }
+{% endfor %}
+{% endif %}
+{% endmacro %} \ No newline at end of file
diff --git a/data/templates/firewall/nftables-defines.j2 b/data/templates/firewall/nftables-defines.j2
index 0a7e79edd..a20c399ae 100644
--- a/data/templates/firewall/nftables-defines.j2
+++ b/data/templates/firewall/nftables-defines.j2
@@ -1,7 +1,7 @@
-{% macro groups(group, is_ipv6) %}
+{% macro groups(group, is_ipv6, is_l3) %}
{% if group is vyos_defined %}
{% set ip_type = 'ipv6_addr' if is_ipv6 else 'ipv4_addr' %}
-{% if group.address_group is vyos_defined and not is_ipv6 %}
+{% if group.address_group is vyos_defined and not is_ipv6 and is_l3 %}
{% for group_name, group_conf in group.address_group.items() %}
{% set includes = group_conf.include if group_conf.include is vyos_defined else [] %}
set A_{{ group_name }} {
@@ -14,7 +14,7 @@
}
{% endfor %}
{% endif %}
-{% if group.ipv6_address_group is vyos_defined and is_ipv6 %}
+{% if group.ipv6_address_group is vyos_defined and is_ipv6 and is_l3 %}
{% for group_name, group_conf in group.ipv6_address_group.items() %}
{% set includes = group_conf.include if group_conf.include is vyos_defined else [] %}
set A6_{{ group_name }} {
@@ -27,7 +27,7 @@
}
{% endfor %}
{% endif %}
-{% if group.domain_group is vyos_defined %}
+{% if group.domain_group is vyos_defined and is_l3 %}
{% for name, name_config in group.domain_group.items() %}
set D_{{ name }} {
type {{ ip_type }}
@@ -46,7 +46,7 @@
}
{% endfor %}
{% endif %}
-{% if group.network_group is vyos_defined and not is_ipv6 %}
+{% if group.network_group is vyos_defined and not is_ipv6 and is_l3 %}
{% for group_name, group_conf in group.network_group.items() %}
{% set includes = group_conf.include if group_conf.include is vyos_defined else [] %}
set N_{{ group_name }} {
@@ -59,7 +59,7 @@
}
{% endfor %}
{% endif %}
-{% if group.ipv6_network_group is vyos_defined and is_ipv6 %}
+{% if group.ipv6_network_group is vyos_defined and is_ipv6 and is_l3 %}
{% for group_name, group_conf in group.ipv6_network_group.items() %}
{% set includes = group_conf.include if group_conf.include is vyos_defined else [] %}
set N6_{{ group_name }} {
@@ -72,7 +72,7 @@
}
{% endfor %}
{% endif %}
-{% if group.port_group is vyos_defined %}
+{% if group.port_group is vyos_defined and is_l3 %}
{% for group_name, group_conf in group.port_group.items() %}
{% set includes = group_conf.include if group_conf.include is vyos_defined else [] %}
set P_{{ group_name }} {
diff --git a/data/templates/firewall/nftables-nat.j2 b/data/templates/firewall/nftables-nat.j2
index f0be3cf5d..dcf28da88 100644
--- a/data/templates/firewall/nftables-nat.j2
+++ b/data/templates/firewall/nftables-nat.j2
@@ -62,6 +62,6 @@ table ip vyos_nat {
return
}
-{{ group_tmpl.groups(firewall_group, False) }}
+{{ group_tmpl.groups(firewall_group, False, True) }}
}
{% endif %}
diff --git a/data/templates/firewall/nftables-policy.j2 b/data/templates/firewall/nftables-policy.j2
index 699349e2b..d77e3f6e9 100644
--- a/data/templates/firewall/nftables-policy.j2
+++ b/data/templates/firewall/nftables-policy.j2
@@ -32,7 +32,7 @@ table ip vyos_mangle {
{% endfor %}
{% endif %}
-{{ group_tmpl.groups(firewall_group, False) }}
+{{ group_tmpl.groups(firewall_group, False, True) }}
}
table ip6 vyos_mangle {
@@ -61,5 +61,5 @@ table ip6 vyos_mangle {
{% endfor %}
{% endif %}
-{{ group_tmpl.groups(firewall_group, True) }}
+{{ group_tmpl.groups(firewall_group, True, True) }}
}
diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2
index 6257b576a..9fcacf677 100644
--- a/data/templates/firewall/nftables.j2
+++ b/data/templates/firewall/nftables.j2
@@ -1,6 +1,7 @@
#!/usr/sbin/nft -f
{% import 'firewall/nftables-defines.j2' as group_tmpl %}
+{% import 'firewall/nftables-bridge.j2' as bridge_tmpl %}
{% import 'firewall/nftables-zone.j2' as zone_tmpl %}
flush chain raw FW_CONNTRACK
@@ -147,7 +148,7 @@ table ip vyos_filter {
{% endfor %}
{% endif %}
{% endif %}
-{{ group_tmpl.groups(group, False) }}
+{{ group_tmpl.groups(group, False, True) }}
{% if zone is vyos_defined %}
{{ zone_tmpl.zone_chains(zone, False) }}
@@ -254,10 +255,20 @@ table ip6 vyos_filter {
{% endfor %}
{% endif %}
{% endif %}
-{{ group_tmpl.groups(group, True) }}
+{{ group_tmpl.groups(group, True, True) }}
{% if zone is vyos_defined %}
{{ zone_tmpl.zone_chains(zone, True) }}
{% endif %}
+}
+
+## Bridge Firewall
+{% if first_install is not vyos_defined %}
+delete table bridge vyos_filter
+{% endif %}
+table bridge vyos_filter {
+{{ bridge_tmpl.bridge(bridge) }}
+{{ group_tmpl.groups(group, False, False) }}
+
} \ No newline at end of file
diff --git a/debian/control b/debian/control
index f31c5a510..42c0b580b 100644
--- a/debian/control
+++ b/debian/control
@@ -62,6 +62,7 @@ Depends:
frr-snmp,
fuse-overlayfs,
libpam-google-authenticator,
+ git,
grc,
haproxy,
hostapd,
diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in
index b0e6358d8..4704b200e 100644
--- a/interface-definitions/firewall.xml.in
+++ b/interface-definitions/firewall.xml.in
@@ -284,6 +284,15 @@
</tagNode>
</children>
</node>
+ <node name="bridge">
+ <properties>
+ <help>Bridge firewall</help>
+ </properties>
+ <children>
+ #include <include/firewall/bridge-hook-forward.xml.i>
+ #include <include/firewall/bridge-custom-name.xml.i>
+ </children>
+ </node>
<node name="ipv4">
<properties>
<help>IPv4 firewall</help>
diff --git a/interface-definitions/include/firewall/action-l2.xml.i b/interface-definitions/include/firewall/action-l2.xml.i
new file mode 100644
index 000000000..43fd211b4
--- /dev/null
+++ b/interface-definitions/include/firewall/action-l2.xml.i
@@ -0,0 +1,37 @@
+<!-- include start from firewall/action-l2.xml.i -->
+<leafNode name="action">
+ <properties>
+ <help>Rule action</help>
+ <completionHelp>
+ <list>accept continue jump return drop queue</list>
+ </completionHelp>
+ <valueHelp>
+ <format>accept</format>
+ <description>Accept matching entries</description>
+ </valueHelp>
+ <valueHelp>
+ <format>continue</format>
+ <description>Continue parsing next rule</description>
+ </valueHelp>
+ <valueHelp>
+ <format>jump</format>
+ <description>Jump to another chain</description>
+ </valueHelp>
+ <valueHelp>
+ <format>return</format>
+ <description>Return from the current chain and continue at the next rule of the last chain</description>
+ </valueHelp>
+ <valueHelp>
+ <format>drop</format>
+ <description>Drop matching entries</description>
+ </valueHelp>
+ <valueHelp>
+ <format>queue</format>
+ <description>Enqueue packet to userspace</description>
+ </valueHelp>
+ <constraint>
+ <regex>(accept|continue|jump|return|drop|queue)</regex>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/action.xml.i b/interface-definitions/include/firewall/action.xml.i
index 7c6e33839..9391a7bee 100644
--- a/interface-definitions/include/firewall/action.xml.i
+++ b/interface-definitions/include/firewall/action.xml.i
@@ -3,13 +3,17 @@
<properties>
<help>Rule action</help>
<completionHelp>
- <list>accept jump reject return drop queue</list>
+ <list>accept continue jump reject return drop queue</list>
</completionHelp>
<valueHelp>
<format>accept</format>
<description>Accept matching entries</description>
</valueHelp>
<valueHelp>
+ <format>continue</format>
+ <description>Continue parsing next rule</description>
+ </valueHelp>
+ <valueHelp>
<format>jump</format>
<description>Jump to another chain</description>
</valueHelp>
@@ -30,7 +34,7 @@
<description>Enqueue packet to userspace</description>
</valueHelp>
<constraint>
- <regex>(accept|jump|reject|return|drop|queue)</regex>
+ <regex>(accept|continue|jump|reject|return|drop|queue)</regex>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/include/firewall/bridge-custom-name.xml.i b/interface-definitions/include/firewall/bridge-custom-name.xml.i
new file mode 100644
index 000000000..a85fd5a19
--- /dev/null
+++ b/interface-definitions/include/firewall/bridge-custom-name.xml.i
@@ -0,0 +1,39 @@
+<!-- include start from firewall/bridge-custom-name.xml.i -->
+<tagNode name="name">
+ <properties>
+ <help>Bridge custom firewall</help>
+ <constraint>
+ <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/firewall/default-action.xml.i>
+ #include <include/firewall/enable-default-log.xml.i>
+ #include <include/generic-description.xml.i>
+ <leafNode name="default-jump-target">
+ <properties>
+ <help>Set jump target. Action jump must be defined in default-action to use this setting</help>
+ <completionHelp>
+ <path>firewall bridge name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <tagNode name="rule">
+ <properties>
+ <help>Bridge Firewall forward filter rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-bridge.xml.i>
+ </children>
+ </tagNode>
+ </children>
+</tagNode>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/bridge-hook-forward.xml.i b/interface-definitions/include/firewall/bridge-hook-forward.xml.i
new file mode 100644
index 000000000..23d757070
--- /dev/null
+++ b/interface-definitions/include/firewall/bridge-hook-forward.xml.i
@@ -0,0 +1,34 @@
+<!-- include start from firewall/bridge-hook-forward.xml.i -->
+<node name="forward">
+ <properties>
+ <help>Bridge forward firewall</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Bridge firewall forward filter</help>
+ </properties>
+ <children>
+ #include <include/firewall/default-action-base-chains.xml.i>
+ #include <include/generic-description.xml.i>
+ <tagNode name="rule">
+ <properties>
+ <help>Bridge Firewall forward filter rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-bridge.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/common-rule-bridge.xml.i b/interface-definitions/include/firewall/common-rule-bridge.xml.i
new file mode 100644
index 000000000..ebf95a111
--- /dev/null
+++ b/interface-definitions/include/firewall/common-rule-bridge.xml.i
@@ -0,0 +1,34 @@
+<!-- include start from firewall/common-rule-bridge.xml.i -->
+#include <include/firewall/action-l2.xml.i>
+#include <include/firewall/nft-queue.xml.i>
+<node name="destination">
+ <properties>
+ <help>Destination parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/mac-address.xml.i>
+ </children>
+</node>
+#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>
+ <completionHelp>
+ <path>firewall bridge name</path>
+ </completionHelp>
+ </properties>
+</leafNode>
+#include <include/firewall/log.xml.i>
+#include <include/firewall/rule-log-options.xml.i>
+<node name="source">
+ <properties>
+ <help>Source parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/mac-address.xml.i>
+ </children>
+</node>
+#include <include/firewall/inbound-interface.xml.i>
+#include <include/firewall/outbound-interface.xml.i>
+#include <include/firewall/match-vlan.xml.i>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/common-rule-inet.xml.i b/interface-definitions/include/firewall/common-rule-inet.xml.i
index 52721ecc4..030adfe7c 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/default-action-bridge.xml.i b/interface-definitions/include/firewall/default-action-bridge.xml.i
new file mode 100644
index 000000000..577165976
--- /dev/null
+++ b/interface-definitions/include/firewall/default-action-bridge.xml.i
@@ -0,0 +1,34 @@
+<!-- include start from firewall/default-action-bridge.xml.i -->
+<leafNode name="default-action">
+ <properties>
+ <help>Default action for rule-set</help>
+ <completionHelp>
+ <list>drop jump return accept continue</list>
+ </completionHelp>
+ <valueHelp>
+ <format>drop</format>
+ <description>Drop if no prior rules are hit</description>
+ </valueHelp>
+ <valueHelp>
+ <format>jump</format>
+ <description>Jump to another chain if no prior rules are hit</description>
+ </valueHelp>
+ <valueHelp>
+ <format>return</format>
+ <description>Return from the current chain and continue at the next rule of the last chain</description>
+ </valueHelp>
+ <valueHelp>
+ <format>accept</format>
+ <description>Accept if no prior rules are hit</description>
+ </valueHelp>
+ <valueHelp>
+ <format>continue</format>
+ <description>Continue parsing next rule</description>
+ </valueHelp>
+ <constraint>
+ <regex>(drop|jump|return|accept|continue)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>drop</defaultValue>
+</leafNode>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/default-action.xml.i b/interface-definitions/include/firewall/default-action.xml.i
index 80efaf335..6a49d800e 100644
--- a/interface-definitions/include/firewall/default-action.xml.i
+++ b/interface-definitions/include/firewall/default-action.xml.i
@@ -1,9 +1,9 @@
<!-- include start from firewall/default-action.xml.i -->
<leafNode name="default-action">
<properties>
- <help>Default-action for rule-set</help>
+ <help>Default action for rule-set</help>
<completionHelp>
- <list>drop jump reject return accept</list>
+ <list>drop jump reject return accept continue</list>
</completionHelp>
<valueHelp>
<format>drop</format>
@@ -25,8 +25,12 @@
<format>accept</format>
<description>Accept if no prior rules are hit</description>
</valueHelp>
+ <valueHelp>
+ <format>continue</format>
+ <description>Continue parsing next rule</description>
+ </valueHelp>
<constraint>
- <regex>(drop|jump|reject|return|accept)</regex>
+ <regex>(drop|jump|reject|return|accept|continue)</regex>
</constraint>
</properties>
<defaultValue>drop</defaultValue>
diff --git a/interface-definitions/include/firewall/match-vlan.xml.i b/interface-definitions/include/firewall/match-vlan.xml.i
new file mode 100644
index 000000000..d0820f7d8
--- /dev/null
+++ b/interface-definitions/include/firewall/match-vlan.xml.i
@@ -0,0 +1,41 @@
+<!-- include start from firewall/match-vlan.xml.i -->
+<node name="vlan">
+ <properties>
+ <help>VLAN parameters</help>
+ </properties>
+ <children>
+ <leafNode name="id">
+ <properties>
+ <help>VLAN id</help>
+ <valueHelp>
+ <format>u32:0-4096</format>
+ <description>VLAN id</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;start-end&gt;</format>
+ <description>VLAN id range to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 0-4095"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="priority">
+ <properties>
+ <help>VLAN priority(pcp)</help>
+ <valueHelp>
+ <format>u32:0-7</format>
+ <description>VLAN priority</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;start-end&gt;</format>
+ <description>VLAN priority range to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 0-7"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/system-config-mgmt.xml.in b/interface-definitions/system-config-mgmt.xml.in
index de5a8cc16..61089ce34 100644
--- a/interface-definitions/system-config-mgmt.xml.in
+++ b/interface-definitions/system-config-mgmt.xml.in
@@ -17,11 +17,36 @@
<properties>
<help>Commit archive location</help>
<valueHelp>
- <format>uri</format>
- <description>Uniform Resource Identifier</description>
+ <format>http://&lt;user&gt;:&lt;passwd&gt;@&lt;host&gt;/&lt;path&gt;</format>
+ <description/>
+ </valueHelp>
+ <valueHelp>
+ <format>https://&lt;user&gt;:&lt;passwd&gt;@&lt;host&gt;/&lt;path&gt;</format>
+ <description/>
+ </valueHelp>
+ <valueHelp>
+ <format>ftp://&lt;user&gt;:&lt;passwd&gt;@&lt;host&gt;/&lt;path&gt;</format>
+ <description/>
+ </valueHelp>
+ <valueHelp>
+ <format>sftp://&lt;user&gt;:&lt;passwd&gt;@&lt;host&gt;/&lt;path&gt;</format>
+ <description/>
+ </valueHelp>
+ <valueHelp>
+ <format>scp://&lt;user&gt;:&lt;passwd&gt;@&lt;host&gt;/&lt;path&gt;</format>
+ <description/>
+ </valueHelp>
+ <valueHelp>
+ <format>tftp://&lt;host&gt;/&lt;path&gt;</format>
+ <description/>
+ </valueHelp>
+ <valueHelp>
+ <format>git+https://&lt;user&gt;:&lt;passwd&gt;@&lt;host&gt;/&lt;path&gt;</format>
+ <description/>
</valueHelp>
<constraint>
<validator name="url --file-transport"/>
+ <regex>(ssh|git|git\+(\w+)):\/\/.*</regex>
</constraint>
<multi/>
</properties>
diff --git a/op-mode-definitions/firewall.xml.in b/op-mode-definitions/firewall.xml.in
index 0f296c272..4a7ffbb66 100644
--- a/op-mode-definitions/firewall.xml.in
+++ b/op-mode-definitions/firewall.xml.in
@@ -132,6 +132,58 @@
</properties>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_group</command>
</leafNode>
+ <node name="bridge">
+ <properties>
+ <help>Show bridge firewall</help>
+ </properties>
+ <children>
+ <node name="forward">
+ <properties>
+ <help>Show bridge forward firewall ruleset</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show bridge forward filter firewall ruleset</help>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of bridge forward filter firewall rules</help>
+ <completionHelp>
+ <path>firewall bridge forward filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
+ </node>
+ </children>
+ </node>
+ <tagNode name="name">
+ <properties>
+ <help>Show bridge custom firewall chains</help>
+ <completionHelp>
+ <path>firewall bridge name</path>
+ </completionHelp>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of bridge custom firewall ruleset</help>
+ <completionHelp>
+ <path>firewall bridge name ${COMP_WORDS[6]} rule</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_family --family $3</command>
+ </node>
<node name="ipv6">
<properties>
<help>Show IPv6 firewall</help>
@@ -154,10 +206,10 @@
<path>firewall ipv6 forward filter rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</node>
</children>
</node>
@@ -178,10 +230,10 @@
<path>firewall ipv6 input filter rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</node>
</children>
</node>
@@ -202,10 +254,10 @@
<path>firewall ipv6 output filter rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</node>
</children>
</node>
@@ -224,10 +276,10 @@
<path>firewall ipv6 ipv6-name ${COMP_WORDS[6]} rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</tagNode>
</children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_family --family $3</command>
@@ -254,10 +306,10 @@
<path>firewall ipv4 forward filter rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</node>
</children>
</node>
@@ -278,10 +330,10 @@
<path>firewall ipv4 input filter rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</node>
</children>
</node>
@@ -302,10 +354,10 @@
<path>firewall ipv4 output filter rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</node>
</children>
</node>
@@ -324,10 +376,10 @@
<path>firewall ipv4 name ${COMP_WORDS[6]} rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</tagNode>
</children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_family --family $3</command>
diff --git a/op-mode-definitions/show-interfaces.xml.in b/op-mode-definitions/show-interfaces.xml.in
index b58e0efea..09466647d 100644
--- a/op-mode-definitions/show-interfaces.xml.in
+++ b/op-mode-definitions/show-interfaces.xml.in
@@ -6,7 +6,7 @@
<properties>
<help>Show network interface information</help>
</properties>
- <command>${vyos_op_scripts_dir}/interfaces.py show_summary</command>
+ <command>${vyos_op_scripts_dir}/interfaces.py show_summary_extended</command>
<children>
<leafNode name="counters">
<properties>
@@ -24,7 +24,7 @@
<properties>
<help>Show summary information of all interfaces</help>
</properties>
- <command>${vyos_op_scripts_dir}/interfaces.py show_summary_extended</command>
+ <command>${vyos_op_scripts_dir}/interfaces.py show_summary</command>
</leafNode>
</children>
</node>
diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py
index 654a8d698..df7240c88 100644
--- a/python/vyos/config_mgmt.py
+++ b/python/vyos/config_mgmt.py
@@ -22,10 +22,11 @@ import logging
from typing import Optional, Tuple, Union
from filecmp import cmp
from datetime import datetime
-from textwrap import dedent
+from textwrap import dedent, indent
from pathlib import Path
from tabulate import tabulate
from shutil import copy, chown
+from urllib.parse import urlsplit, urlunsplit
from vyos.config import Config
from vyos.configtree import ConfigTree, ConfigTreeError, show_diff
@@ -377,9 +378,22 @@ Proceed ?'''
remote_file = f'config.boot-{hostname}.{timestamp}'
source_address = self.source_address
+ if self.effective_locations:
+ print("Archiving config...")
for location in self.effective_locations:
- upload(archive_config_file, f'{location}/{remote_file}',
- source_host=source_address)
+ url = urlsplit(location)
+ _, _, netloc = url.netloc.rpartition("@")
+ redacted_location = urlunsplit(url._replace(netloc=netloc))
+ print(f" {redacted_location}", end=" ", flush=True)
+ try:
+ upload(archive_config_file, f'{location}/{remote_file}',
+ source_host=source_address, raise_error=True)
+ print("OK")
+ except Exception as e:
+ print("FAILED!")
+ print()
+ print(indent(str(e), " > "))
+ print()
# op-mode functions
#
diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py
index 06b58d4ed..8ae269fed 100644
--- a/python/vyos/firewall.py
+++ b/python/vyos/firewall.py
@@ -438,7 +438,7 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name):
else:
output.append('return')
- output.append(f'comment "{hook}-{fw_name}-{rule_id}"')
+ output.append(f'comment "{family}-{hook}-{fw_name}-{rule_id}"')
return " ".join(output)
def parse_tcp_flags(flags):
diff --git a/python/vyos/remote.py b/python/vyos/remote.py
index 4be477d24..8b90e4530 100644
--- a/python/vyos/remote.py
+++ b/python/vyos/remote.py
@@ -14,6 +14,7 @@
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import os
+import pwd
import shutil
import socket
import ssl
@@ -22,6 +23,9 @@ import sys
import tempfile
import urllib.parse
+from contextlib import contextmanager
+from pathlib import Path
+
from ftplib import FTP
from ftplib import FTP_TLS
@@ -37,11 +41,22 @@ from vyos.utils.io import ask_yes_no
from vyos.utils.io import is_interactive
from vyos.utils.io import print_error
from vyos.utils.misc import begin
-from vyos.utils.process import cmd
+from vyos.utils.process import cmd, rc_cmd
from vyos.version import get_version
CHUNK_SIZE = 8192
+@contextmanager
+def umask(mask: int):
+ """
+ Context manager that temporarily sets the process umask.
+ """
+ oldmask = os.umask(mask)
+ try:
+ yield
+ finally:
+ os.umask(oldmask)
+
class InteractivePolicy(MissingHostKeyPolicy):
"""
Paramiko policy for interactively querying the user on whether to proceed
@@ -310,35 +325,137 @@ class TftpC:
with open(location, 'rb') as f:
cmd(f'{self.command} -T - "{self.urlstring}"', input=f.read())
+class GitC:
+ def __init__(self,
+ url,
+ progressbar=False,
+ check_space=False,
+ source_host=None,
+ source_port=0,
+ timeout=10,
+ ):
+ self.command = 'git'
+ self.url = url
+ self.urlstring = urllib.parse.urlunsplit(url)
+ if self.urlstring.startswith("git+"):
+ self.urlstring = self.urlstring.replace("git+", "", 1)
+
+ def download(self, location: str):
+ raise NotImplementedError("not supported")
+
+ @umask(0o077)
+ def upload(self, location: str):
+ scheme = self.url.scheme
+ _, _, scheme = scheme.partition("+")
+ netloc = self.url.netloc
+ url = Path(self.url.path).parent
+ with tempfile.TemporaryDirectory(prefix="git-commit-archive-") as directory:
+ # Determine username, fullname, email for Git commit
+ pwd_entry = pwd.getpwuid(os.getuid())
+ user = pwd_entry.pw_name
+ name = pwd_entry.pw_gecos.split(",")[0] or user
+ fqdn = socket.getfqdn()
+ email = f"{user}@{fqdn}"
+
+ # environment vars for our git commands
+ env = {
+ "GIT_TERMINAL_PROMPT": "0",
+ "GIT_AUTHOR_NAME": name,
+ "GIT_AUTHOR_EMAIL": email,
+ "GIT_COMMITTER_NAME": name,
+ "GIT_COMMITTER_EMAIL": email,
+ }
+
+ # build ssh command for git
+ ssh_command = ["ssh"]
+
+ # if we are not interactive, we use StrictHostKeyChecking=yes to avoid any prompts
+ if not sys.stdout.isatty():
+ ssh_command += ["-o", "StrictHostKeyChecking=yes"]
+
+ env["GIT_SSH_COMMAND"] = " ".join(ssh_command)
+
+ # git clone
+ path_repository = Path(directory) / "repository"
+ scheme = f"{scheme}://" if scheme else ""
+ rc, out = rc_cmd(
+ [self.command, "clone", f"{scheme}{netloc}{url}", str(path_repository), "--depth=1"],
+ env=env,
+ shell=False,
+ )
+ if rc:
+ raise Exception(out)
+
+ # git add
+ filename = Path(Path(self.url.path).name).stem
+ dst = path_repository / filename
+ shutil.copy2(location, dst)
+ rc, out = rc_cmd(
+ [self.command, "-C", str(path_repository), "add", filename],
+ env=env,
+ shell=False,
+ )
+
+ # git commit -m
+ commit_message = os.environ.get("COMMIT_COMMENT", "commit")
+ rc, out = rc_cmd(
+ [self.command, "-C", str(path_repository), "commit", "-m", commit_message],
+ env=env,
+ shell=False,
+ )
+
+ # git push
+ rc, out = rc_cmd(
+ [self.command, "-C", str(path_repository), "push"],
+ env=env,
+ shell=False,
+ )
+ if rc:
+ raise Exception(out)
+
def urlc(urlstring, *args, **kwargs):
"""
Dynamically dispatch the appropriate protocol class.
"""
- url_classes = {'http': HttpC, 'https': HttpC, 'ftp': FtpC, 'ftps': FtpC, \
- 'sftp': SshC, 'ssh': SshC, 'scp': SshC, 'tftp': TftpC}
+ url_classes = {
+ "http": HttpC,
+ "https": HttpC,
+ "ftp": FtpC,
+ "ftps": FtpC,
+ "sftp": SshC,
+ "ssh": SshC,
+ "scp": SshC,
+ "tftp": TftpC,
+ "git": GitC,
+ }
url = urllib.parse.urlsplit(urlstring)
+ scheme, _, _ = url.scheme.partition("+")
try:
- return url_classes[url.scheme](url, *args, **kwargs)
+ return url_classes[scheme](url, *args, **kwargs)
except KeyError:
- raise ValueError(f'Unsupported URL scheme: "{url.scheme}"')
+ raise ValueError(f'Unsupported URL scheme: "{scheme}"')
-def download(local_path, urlstring, progressbar=False, check_space=False,
+def download(local_path, urlstring, progressbar=False, raise_error=False, check_space=False,
source_host='', source_port=0, timeout=10.0):
try:
progressbar = progressbar and is_interactive()
urlc(urlstring, progressbar, check_space, source_host, source_port, timeout).download(local_path)
except Exception as err:
+ if raise_error:
+ raise
print_error(f'Unable to download "{urlstring}": {err}')
except KeyboardInterrupt:
print_error('\nDownload aborted by user.')
-def upload(local_path, urlstring, progressbar=False,
+def upload(local_path, urlstring, progressbar=False, raise_error=False,
source_host='', source_port=0, timeout=10.0):
try:
progressbar = progressbar and is_interactive()
urlc(urlstring, progressbar, source_host, source_port, timeout).upload(local_path)
except Exception as err:
+ if raise_error:
+ raise
print_error(f'Unable to upload "{urlstring}": {err}')
except KeyboardInterrupt:
print_error('\nUpload aborted by user.')
diff --git a/scripts/build-command-templates b/scripts/build-command-templates
index c8ae83d9d..2e7f8b994 100755
--- a/scripts/build-command-templates
+++ b/scripts/build-command-templates
@@ -145,6 +145,8 @@ def get_properties(p, default=None):
description = v.find("description").text
if default != None and default.text == format:
description += f' (default)'
+ # Is no description was specified, keep it empty
+ if not description: description = ''
vh.append( (format, description) )
props["val_help"] = vh
except:
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index c51592ec2..e6c928ad7 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -543,6 +543,41 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.verify_nftables_chain([['accept']], 'raw', 'FW_CONNTRACK')
self.verify_nftables_chain([['return']], 'ip6 raw', 'FW_CONNTRACK')
+ def test_bridge_basic_rules(self):
+ name = 'smoketest'
+ interface_in = 'eth0'
+ mac_address = '00:53:00:00:00:01'
+ vlan_id = '12'
+ vlan_prior = '3'
+
+ self.cli_set(['firewall', 'bridge', 'name', name, 'default-action', 'accept'])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'enable-default-log'])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'source', 'mac-address', mac_address])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'inbound-interface', 'name', interface_in])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'log'])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'log-options', 'level', 'crit'])
+
+ self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '1', 'vlan', 'id', vlan_id])
+ self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '2', 'action', 'jump'])
+ self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '2', 'jump-target', name])
+ self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '2', 'vlan', 'priority', vlan_prior])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['chain VYOS_FORWARD_filter'],
+ ['type filter hook forward priority filter; policy drop;'],
+ [f'vlan id {vlan_id}', 'accept'],
+ [f'vlan pcp {vlan_prior}', f'jump NAME_{name}'],
+ [f'chain NAME_{name}'],
+ [f'ether saddr {mac_address}', f'iifname "{interface_in}"', f'log prefix "[bri-NAM-{name}-1-A]" log level crit', 'accept']
+ ]
+
+ self.verify_nftables(nftables_search, 'bridge vyos_filter')
+
def test_source_validation(self):
# Strict
self.cli_set(['firewall', 'global-options', 'source-validation', 'strict'])
diff --git a/src/op_mode/interfaces.py b/src/op_mode/interfaces.py
index c626535b5..14ffdca9f 100755
--- a/src/op_mode/interfaces.py
+++ b/src/op_mode/interfaces.py
@@ -235,6 +235,11 @@ def _get_summary_data(ifname: typing.Optional[str],
if iftype is None:
iftype = ''
ret = []
+
+ def is_interface_has_mac(interface_name):
+ interface_no_mac = ('tun', 'wg')
+ return not any(interface_name.startswith(prefix) for prefix in interface_no_mac)
+
for interface in filtered_interfaces(ifname, iftype, vif, vrrp):
res_intf = {}
@@ -244,7 +249,7 @@ def _get_summary_data(ifname: typing.Optional[str],
res_intf['addr'] = [_ for _ in interface.get_addr() if not _.startswith('fe80::')]
res_intf['description'] = interface.get_alias()
res_intf['mtu'] = interface.get_mtu()
- res_intf['mac'] = interface.get_mac()
+ res_intf['mac'] = interface.get_mac() if is_interface_has_mac(interface.ifname) else 'n/a'
res_intf['vrf'] = interface.get_vrf()
ret.append(res_intf)