diff --git a/host_firewall/defaults/main.yml b/host_firewall/defaults/main.yml index a901192..a287ba6 100644 --- a/host_firewall/defaults/main.yml +++ b/host_firewall/defaults/main.yml @@ -93,7 +93,7 @@ firewall: # Lets the kernel log the nft log statements of container network # namespaces (LXC guests) into the host kernel log. all_netns: false - # Shared by the Spamhaus (fw-spamhaus-in: , fw-spamhaus-out: ), FireHOL (fw-firehol-in: , fw-firehol-out: ), bruteforceblocker (fw-bruteforceblocker-in: ) and scanner (fw-scanners-in: ) chains, + # Shared by the Spamhaus (fw-spamhaus-in: , fw-spamhaus-out: ), FireHOL (fw-firehol-in: , fw-firehol-out: ) and scanner (fw-scanners-in: ) chains, # one limit each. spamhaus_rate: "6/minute burst 10 packets" # Spamhaus DROP, DROPv6 and ASN-DROP: traffic from the listed networks drops @@ -101,11 +101,9 @@ firewall: # are refreshed daily by nft-host-spamhaus. spamhaus: true # FireHOL lists (IPv4 only, FireHOL has no IPv6 lists), from github.com/firehol/blocklist-ipsets: - # - level1 and level2 (level2 includes GreenSnow) and the malware set (vxvault and cybercrime): treated like the - # Spamhaus lists, with the prefixes fw-firehol-in: and fw-firehol-out: and the same log limit. - # - bruteforceblocker: inbound new connections from the listed hosts drop, outbound traffic is not filtered. Log - # prefix fw-bruteforceblocker-in: with its own limit, which CrowdSec whitelists. - # Entries in private or reserved space and ones holding an address of the host are left out. All sets are refreshed + # level1 and level2 (level2 includes GreenSnow): treated like the Spamhaus lists, with the prefixes fw-firehol-in: + # and fw-firehol-out: and the same log limit. + # Entries in private or reserved space and ones holding an address of the host are left out. Both sets are refreshed # daily by nft-host-firehol. firehol: true # Internet-wide scanners (Censys, Shodan, ONYPHE, Shadowserver, ...), IPv4 diff --git a/host_firewall/files/nft-host-firehol b/host_firewall/files/nft-host-firehol index 1d892b6..f95b18a 100644 --- a/host_firewall/files/nft-host-firehol +++ b/host_firewall/files/nft-host-firehol @@ -23,13 +23,11 @@ BASE_URL = "https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/" USER_AGENT = "ruekov-host-firewall (nft-host-firehol)" # set name -> (lists merged into it, lower bound for their entries together that -# catches an empty or truncated answer; the lists hold about 4700 (level1), -# 18700 (level2), 500 (bruteforceblocker) and 70 + 500 (vxvault, cybercrime)). +# catches an empty or truncated answer; the lists hold about 4700 (level1) and +# 18700 (level2)). SETS = { "firehol_v4": (["firehol_level1.netset"], 1000), "firehol_level2_v4": (["firehol_level2.netset"], 5000), - "bruteforceblocker_v4": (["bruteforceblocker.ipset"], 100), - "malware_v4": (["vxvault.ipset", "cybercrime.ipset"], 200), } # A prefix this wide is a parse error, not a bad network. MIN_PREFIXLEN = 8 diff --git a/host_firewall/tasks/main.yml b/host_firewall/tasks/main.yml index a66f4ee..5489a3e 100644 --- a/host_firewall/tasks/main.yml +++ b/host_firewall/tasks/main.yml @@ -183,17 +183,26 @@ dest: /usr/local/sbin/nft-host-firehol mode: "0755" - # The rule set includes this file and uses all of its sets, so a missing file or set would fail the load at boot. + # The rule set includes this file and uses both sets, so a missing file or set would fail the load at boot. - name: Look for the FireHOL sets - ansible.builtin.command: grep -cE 'set (firehol_v4|firehol_level2_v4|bruteforceblocker_v4|malware_v4) ' /etc/nftables.d/host-firehol.nft + ansible.builtin.command: grep -cE 'set (firehol_v4|firehol_level2_v4) ' /etc/nftables.d/host-firehol.nft register: firewall_firehol_sets changed_when: false failed_when: false check_mode: false + # Sets of removed lists are unused by the new rule set, which nft accepts, but the deployed one still uses them: + # a rebuild before the new rule set is confirmed would fail its reload. The rebuild at the end of the role drops them. + - name: Look for sets of removed FireHOL lists + ansible.builtin.command: grep -cE 'set (bruteforceblocker_v4|malware_v4) ' /etc/nftables.d/host-firehol.nft + register: firewall_firehol_stale + changed_when: false + failed_when: false + check_mode: false + - name: Build the FireHOL sets when missing ansible.builtin.command: /usr/local/sbin/nft-host-firehol - when: firewall_firehol_sets.stdout != '4' + when: firewall_firehol_sets.stdout != '2' - name: Read the FireHOL sets file ansible.builtin.stat: @@ -400,3 +409,12 @@ ansible.builtin.systemd: daemon_reload: true when: firewall_greensnow_files is changed + +# The sets file keeps sets of removed FireHOL lists until the rule set without them is confirmed (see above). +- name: Drop sets of removed FireHOL lists + ansible.builtin.command: /usr/local/sbin/nft-host-firehol + when: + - firewall.firehol + - firewall_firehol_sets.stdout == '2' + - (firewall_firehol_stale.stdout | default('0') | int) > 0 + - not ansible_check_mode diff --git a/host_firewall/templates/host.nft.j2 b/host_firewall/templates/host.nft.j2 index 125a0ae..6e3cf98 100644 --- a/host_firewall/templates/host.nft.j2 +++ b/host_firewall/templates/host.nft.j2 @@ -171,7 +171,6 @@ table inet host { {% if firewall.firehol %} ip saddr @firehol_v4 jump firehol_in ip saddr @firehol_level2_v4 jump firehol_in - ip saddr @malware_v4 jump firehol_in {% endif %} {% endif %} @@ -191,12 +190,6 @@ table inet host { ip saddr @scanners_v4 jump scanners_in ip6 saddr @scanners_v6 jump scanners_in -{% endif %} -{% if firewall.firehol %} - - # After the established rule: only inbound connections of bruteforceblocker hosts drop. - ip saddr @bruteforceblocker_v4 jump bruteforceblocker_in - {% endif %} ct state invalid jump drop_log iif lo accept @@ -360,16 +353,6 @@ table inet host { drop } {% endif %} -{% if firewall.firehol %} - - # Own limit and prefix, so bruteforceblocker hits use up neither the fw-in-drop budget - # nor the one of the blocklists. CrowdSec whitelists the prefix: the listed - # hosts are known and need no decision. - chain bruteforceblocker_in { - limit rate {{ firewall.log.spamhaus_rate }} log prefix "fw-bruteforceblocker-in: " - drop - } -{% endif %} {% if firewall.block_smtp_out or blocklists %} # Outbound mail goes through the mail host only. Loopback stays open for a @@ -385,7 +368,6 @@ table inet host { ip daddr @firehol_v4 jump firehol_out ip daddr @firehol_level2_v4 jump firehol_out - ip daddr @malware_v4 jump firehol_out {% endif %} {% if firewall.block_smtp_out %} @@ -474,18 +456,12 @@ table inet host { ip daddr @firehol_v4 jump firehol_out ip saddr @firehol_level2_v4 jump firehol_in ip daddr @firehol_level2_v4 jump firehol_out - ip saddr @malware_v4 jump firehol_in - ip daddr @malware_v4 jump firehol_out {% endif %} {% if firewall.scanners %} ct state new ip saddr @scanners_v4 jump scanners_in ct state new ip6 saddr @scanners_v6 jump scanners_in {% endif %} -{% if firewall.firehol %} - - ct state new ip saddr @bruteforceblocker_v4 jump bruteforceblocker_in -{% endif %} {% for guard in forward_guard %} # {{ guard.key }}