Add forward_guard to host_firewall for a forward chain that drops traffic to guarded guests outside the allowed interfaces, ports and ICMP types
This commit is contained in:
@@ -31,6 +31,11 @@ firewall:
|
|||||||
dnat: {}
|
dnat: {}
|
||||||
# IPv4 source NAT: name -> nft match, e.g. 'oifname "vmbr0"'.
|
# IPv4 source NAT: name -> nft match, e.g. 'oifname "vmbr0"'.
|
||||||
masquerade: {}
|
masquerade: {}
|
||||||
|
# Forwarded traffic to guarded guests: name -> {ifaces, v4, v6}, where v4 and
|
||||||
|
# v6 are {daddr, tcp: ports, icmp: icmp or icmpv6 types}. Replies and traffic
|
||||||
|
# in on ifaces pass, the listed ports and types pass, the rest to daddr drops;
|
||||||
|
# traffic to other addresses is not filtered.
|
||||||
|
forward_guard: {}
|
||||||
ssh_rate_limit: "10/minute burst 5 packets"
|
ssh_rate_limit: "10/minute burst 5 packets"
|
||||||
# EU-27 plus CH, NO, GB, IS, LI (ipdeny country codes). Empty leaves the geo
|
# EU-27 plus CH, NO, GB, IS, LI (ipdeny country codes). Empty leaves the geo
|
||||||
# ports to the admin set; a load peaks at about 60 MB of kernel memory, too much for a 1 GB host.
|
# ports to the admin set; a load peaks at about 60 MB of kernel memory, too much for a 1 GB host.
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ set {{ name }} {
|
|||||||
{% set guest_match = 'iifname { "' ~ (guest_ifaces | join('", "')) ~ '" }' %}
|
{% set guest_match = 'iifname { "' ~ (guest_ifaces | join('", "')) ~ '" }' %}
|
||||||
{% set dnat = firewall.dnat | default({}, true) | dict2items | rejectattr('value', 'none') | map(attribute='value') | list %}
|
{% set dnat = firewall.dnat | default({}, true) | dict2items | rejectattr('value', 'none') | map(attribute='value') | list %}
|
||||||
{% set masquerade = firewall.masquerade | default({}, true) | dict2items | rejectattr('value', 'none') | map(attribute='value') | unique | list %}
|
{% set masquerade = firewall.masquerade | default({}, true) | dict2items | rejectattr('value', 'none') | map(attribute='value') | unique | list %}
|
||||||
|
{% set forward_guard = firewall.forward_guard | default({}, true) | dict2items | rejectattr('value', 'none') | list %}
|
||||||
|
|
||||||
# Declare-then-delete makes every load one atomic transaction that replaces
|
# Declare-then-delete makes every load one atomic transaction that replaces
|
||||||
# only these tables; Cilium, Docker, NetBird and CrowdSec tables stay untouched.
|
# only these tables; Cilium, Docker, NetBird and CrowdSec tables stay untouched.
|
||||||
@@ -119,6 +120,32 @@ table inet host {
|
|||||||
ip6 saddr @geo_v6 accept
|
ip6 saddr @geo_v6 accept
|
||||||
{% endif %}
|
{% endif %}
|
||||||
}
|
}
|
||||||
|
{% if forward_guard %}
|
||||||
|
|
||||||
|
chain forward {
|
||||||
|
type filter hook forward priority filter; policy accept;
|
||||||
|
{% for guard in forward_guard %}
|
||||||
|
|
||||||
|
# {{ guard.key }}
|
||||||
|
{% set ifaces = guard.value.ifaces | default({}, true) | dict2items | rejectattr('value', 'none') | map(attribute='value') | unique | list %}
|
||||||
|
{% for fam, l3, icmp in [('v4', 'ip', 'icmp'), ('v6', 'ip6', 'icmpv6')] if list((guard.value[fam] | default({}, true)).daddr) %}
|
||||||
|
{% set zone = guard.value[fam] %}
|
||||||
|
{% set match = l3 ~ ' daddr { ' ~ list(zone.daddr) ~ ' }' %}
|
||||||
|
{{ match }} ct state established,related accept
|
||||||
|
{% if ifaces %}
|
||||||
|
{{ match }} iifname { "{{ ifaces | join('", "') }}" } accept
|
||||||
|
{% endif %}
|
||||||
|
{% if list(zone.tcp) %}
|
||||||
|
{{ match }} tcp dport { {{ list(zone.tcp) }} } accept
|
||||||
|
{% endif %}
|
||||||
|
{% if list(zone.icmp) %}
|
||||||
|
{{ match }} {{ icmp }} type { {{ list(zone.icmp) }} } accept
|
||||||
|
{% endif %}
|
||||||
|
{{ match }} drop
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
}
|
}
|
||||||
{% if dnat or masquerade %}
|
{% if dnat or masquerade %}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user