diff --git a/collections/ansible_collections/nullified/infrastructure/roles/security/defaults/main.yml b/collections/ansible_collections/nullified/infrastructure/roles/security/defaults/main.yml index b79ba88..0fbc085 100644 --- a/collections/ansible_collections/nullified/infrastructure/roles/security/defaults/main.yml +++ b/collections/ansible_collections/nullified/infrastructure/roles/security/defaults/main.yml @@ -5,6 +5,29 @@ security: https_ignore_list: [] clamav: version: 1.2.1 + firewall: + enable: true + nat: + policy: + prerouting: accept + input: accept + postrouting: accept + output: accept + additional_rules: "" + mangle: + drop_privatenets: true + policy: + prerouting: accept + postrouting: accept + output: accept + forward: drop + additional_rules: "" + filter: + policy: + input: drop + output: drop + forward: drop + additional_rules: "" custom_security: {} recursive_combine: true diff --git a/collections/ansible_collections/nullified/infrastructure/roles/security/handlers/main.yml b/collections/ansible_collections/nullified/infrastructure/roles/security/handlers/main.yml index 3dd7388..09d63bd 100644 --- a/collections/ansible_collections/nullified/infrastructure/roles/security/handlers/main.yml +++ b/collections/ansible_collections/nullified/infrastructure/roles/security/handlers/main.yml @@ -1,4 +1,11 @@ --- +- name: '[firewall] restart service' + become: true + systemd_service: + name: nftables.service + enabled: true + state: restarted + - name: '[ssh] restart service' become: true ansible.builtin.systemd_service: diff --git a/collections/ansible_collections/nullified/infrastructure/roles/security/tasks/firewall.yml b/collections/ansible_collections/nullified/infrastructure/roles/security/tasks/firewall.yml new file mode 100644 index 0000000..efb0871 --- /dev/null +++ b/collections/ansible_collections/nullified/infrastructure/roles/security/tasks/firewall.yml @@ -0,0 +1,50 @@ +--- +- name: gather facts if required + ansible.builtin.setup: + gather_subset: + - distribution + - virtualization_type + +- name: install and configure nftables + when: security.firewall.enable is truthy + become: true + notify: + - 'security : [firewall] restart service' + block: + - name: install nftables + ansible.builtin.apt: + pkg: + - nftables + + - name: enable nftables + ansible.builtin.systemd: + name: nftables + enabled: true + masked: false + + - name: create config dir + ansible.builtin.file: + path: /etc/nftables.d + mode: '0700' + state: directory + + - name: base config file + ansible.builtin.template: + src: "../templates/system/{{ ansible_facts['distribution'] | lower }}/nftables.conf.j2" + dest: /etc/nftables.conf + mode: '0700' + vars: + controller_ip: "{{ lookup('pipe', '/bin/dig +short A $(/usr/bin/hostname -f)') }}" + controller_ip6: "{{ lookup('pipe', '/bin/dig +short AAAA $(/usr/bin/hostname -f)') }}" + + - name: common firewall rules + ansible.builtin.template: + src: "../templates/system/nftables/{{ item }}.table.j2" + dest: "/etc/nftables.d/{{ item }}.table" + mode: '0600' + vars: + firewall: "{{ security.firewall }}" + loop: + - 01-nat + - 02-mangle + - 03-filter diff --git a/collections/ansible_collections/nullified/infrastructure/roles/security/tasks/main.yml b/collections/ansible_collections/nullified/infrastructure/roles/security/tasks/main.yml index fb1b4f1..746fe67 100644 --- a/collections/ansible_collections/nullified/infrastructure/roles/security/tasks/main.yml +++ b/collections/ansible_collections/nullified/infrastructure/roles/security/tasks/main.yml @@ -9,6 +9,8 @@ security: "{{ security | combine(custom_security, recursive=recursive_combine) }}" changed_when: false +- include_tasks: firewall.yml + - name: '[apt] force HTTPS sources' become: true when: security.apt.force_https is truthy diff --git a/collections/ansible_collections/nullified/infrastructure/roles/security/templates/system/debian/nftables.conf.j2 b/collections/ansible_collections/nullified/infrastructure/roles/security/templates/system/debian/nftables.conf.j2 new file mode 100644 index 0000000..3062e50 --- /dev/null +++ b/collections/ansible_collections/nullified/infrastructure/roles/security/templates/system/debian/nftables.conf.j2 @@ -0,0 +1,23 @@ +#!/usr/sbin/nft -f + +flush ruleset + +define ansible_controller_ip = {{ controller_ip | default('127.0.0.1', true) }} +define ansible_controller_ip6 = {{ controller_ip6 | default('fe80::', true) }} +define dns_server = {{ dns_server | default('9.9.9.9', true) }} +define dns_server6 = {{ dns_server | default('2620:fe::fe', true) }} +define private_nets = { + 10.0.0.0/8, 100.64.0.0/10, 172.16.0.0/12, + 192.0.0.0/24, 192.168.0.0/16, 198.18.0.0/15 +} +define reserved_nets = { + 0.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24, + 192.88.99.0/24, 198.51.100.0/24, 203.0.113.0/24, + 224.0.0.0/4, 233.252.0.0/24, 240.0.0.0/4 +} + +include "/etc/nftables.d/01-nat.table" +include "/etc/nftables.d/02-mangle.table" +include "/etc/nftables.d/03-filter.table" + +include "/etc/nftables.d/*.nft" diff --git a/collections/ansible_collections/nullified/infrastructure/roles/security/templates/system/nftables/01-nat.table.j2 b/collections/ansible_collections/nullified/infrastructure/roles/security/templates/system/nftables/01-nat.table.j2 new file mode 100644 index 0000000..a037fb9 --- /dev/null +++ b/collections/ansible_collections/nullified/infrastructure/roles/security/templates/system/nftables/01-nat.table.j2 @@ -0,0 +1,18 @@ +# Setup +table inet nat { + chain prerouting { + type nat hook prerouting priority -100; policy {{ firewall.nat.policy.prerouting }}; + } + + chain input { + type nat hook input priority 100; policy {{ firewall.nat.policy.input }}; + } + + chain postrouting { + type nat hook postrouting priority 100; policy {{ firewall.nat.policy.postrouting }}; + } + + chain output { + type nat hook output priority -100; policy {{ firewall.nat.policy.output }}; + } +} diff --git a/collections/ansible_collections/nullified/infrastructure/roles/security/templates/system/nftables/02-mangle.table.j2 b/collections/ansible_collections/nullified/infrastructure/roles/security/templates/system/nftables/02-mangle.table.j2 new file mode 100644 index 0000000..dac87a8 --- /dev/null +++ b/collections/ansible_collections/nullified/infrastructure/roles/security/templates/system/nftables/02-mangle.table.j2 @@ -0,0 +1,51 @@ +# Setup +table inet mangle { + chain prerouting { + type filter hook prerouting priority -150; policy {{ firewall.mangle.policy.prerouting }}; + ip saddr $ansible_controller_ip tcp dport 22 accept + ip6 saddr $ansible_controller_ip6 tcp dport 22 accept + ip daddr $ansible_controller_ip tcp sport 22 accept + ip6 daddr $ansible_controller_ip6 tcp sport 22 accept + + ip protocol icmp accept + ip frag-off & 0x1fff != 0 counter drop + ct state invalid counter drop + tcp flags & (fin|syn|rst|ack) != syn ct state new counter drop + tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 counter drop + tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|rst|psh|ack|urg counter drop + tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 counter drop + tcp flags & (fin|syn) == fin|syn counter drop + tcp flags & (syn|rst) == syn|rst counter drop + tcp flags & (fin|rst) == fin|rst counter drop + tcp flags & (fin|ack) == fin counter drop + tcp flags & (ack|urg) == urg counter drop + tcp flags & (fin|ack) == fin counter drop + tcp flags & (psh|ack) == psh counter drop + tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|rst|psh|ack|urg counter drop + tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 counter drop + tcp flags & (fin|syn|rst|psh|ack|urg) == fin|psh|urg counter drop + tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|psh|urg counter drop + tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|rst|ack|urg counter drop + {% if firewall.mangle.drop_privatenets %} + ip saddr $private_nets counter drop + {% endif %} + ip saddr $reserved_nets counter drop + iifname != "lo" ip saddr 127.0.0.0/8 counter drop + } + + chain postrouting { + type filter hook postrouting priority -150; policy {{ firewall.mangle.policy.postrouting }}; + ip saddr $ansible_controller_ip tcp dport 22 accept + ip6 saddr $ansible_controller_ip6 tcp dport 22 accept + ip daddr $ansible_controller_ip tcp sport 22 accept + ip6 daddr $ansible_controller_ip6 tcp sport 22 accept + } + + chain output { + type route hook output priority -150; policy {{ firewall.mangle.policy.output }}; + } + + chain forward { + type filter hook forward priority -150; policy {{ firewall.mangle.policy.forward }}; + } +} diff --git a/collections/ansible_collections/nullified/infrastructure/roles/security/templates/system/nftables/03-filter.table.j2 b/collections/ansible_collections/nullified/infrastructure/roles/security/templates/system/nftables/03-filter.table.j2 new file mode 100644 index 0000000..7196b73 --- /dev/null +++ b/collections/ansible_collections/nullified/infrastructure/roles/security/templates/system/nftables/03-filter.table.j2 @@ -0,0 +1,26 @@ +# Setup +table inet filter { + chain input { + type filter hook input priority 0; policy {{ firewall.filter.policy.input }}; + ip saddr $ansible_controller_ip tcp dport 22 accept + ip6 saddr $ansible_controller_ip6 tcp dport 22 accept + + iifname "lo" counter accept + ct state related,established counter accept + tcp dport 22 limit rate 10/hour burst 5 packets counter accept + } + + chain output { + type filter hook output priority 0; policy {{ firewall.filter.policy.output }}; + ip daddr $ansible_controller_ip tcp sport 22 accept + ip6 daddr $ansible_controller_ip6 tcp sport 22 accept + + oifname "lo" counter accept + ct state related,established counter accept + tcp sport 22 counter accept + } + + chain forward { + type filter hook forward priority 0; policy {{ firewall.filter.policy.forward }}; + } +} diff --git a/inventory/host_vars/unobtainium/vars.yml b/inventory/host_vars/unobtainium/vars.yml index b6b6595..6d3920b 100644 --- a/inventory/host_vars/unobtainium/vars.yml +++ b/inventory/host_vars/unobtainium/vars.yml @@ -21,3 +21,14 @@ custom_common: force_sign: true signing_key: "{{ vault_common_gitconfig_signingkey }}" install_fonts: true + +custom_security: + firewall: + filter: + policy: + output: accept + forward: accept + mangle: + drop_privatenets: false + policy: + forward: accept