From bc2d42aab02b5ef9e27ee59a03543b41d99adf04 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Tue, 8 Aug 2023 20:14:32 +0200 Subject: add firewall role --- roles/firewall/defaults/main.yml | 10 +++++++ roles/firewall/handlers/main.yml | 5 ++++ roles/firewall/tasks/main.yml | 22 +++++++++++++++ roles/firewall/templates/rules.v4 | 48 ++++++++++++++++++++++++++++++++ roles/firewall/templates/rules.v6 | 58 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 143 insertions(+) create mode 100644 roles/firewall/defaults/main.yml create mode 100644 roles/firewall/handlers/main.yml create mode 100644 roles/firewall/tasks/main.yml create mode 100644 roles/firewall/templates/rules.v4 create mode 100644 roles/firewall/templates/rules.v6 (limited to 'roles') diff --git a/roles/firewall/defaults/main.yml b/roles/firewall/defaults/main.yml new file mode 100644 index 0000000..c2ac034 --- /dev/null +++ b/roles/firewall/defaults/main.yml @@ -0,0 +1,10 @@ +firewall_ports_tcp: [] +firewall_ports4_tcp: [] +firewall_ports6_tcp: [] + +firewall_ports_udp: [] +firewall_ports4_udp: [] +firewall_ports6_udp: [] + +firewall_rules4: [] +firewall_rules6: [] diff --git a/roles/firewall/handlers/main.yml b/roles/firewall/handlers/main.yml new file mode 100644 index 0000000..70387f3 --- /dev/null +++ b/roles/firewall/handlers/main.yml @@ -0,0 +1,5 @@ +- name: Reload iptables + become: true + ansible.builtin.systemd_service: + name: netfilter-persistent + state: restarted diff --git a/roles/firewall/tasks/main.yml b/roles/firewall/tasks/main.yml new file mode 100644 index 0000000..9fcf543 --- /dev/null +++ b/roles/firewall/tasks/main.yml @@ -0,0 +1,22 @@ +- name: Install iptables-persistent + become: true + ansible.builtin.apt: + install_recommends: false + name: iptables-persistent + state: present + +- name: 'Configure rules' + become: true + ansible.builtin.template: + src: '{{ item.src }}' + dest: '{{ item.dest }}' + owner: root + group: root + mode: '640' + loop: + - {src: rules.v4, dest: /etc/iptables/rules.v4} + - {src: rules.v6, dest: /etc/iptables/rules.v6} + notify: Reload iptables + +- name: Flush handlers + ansible.builtin.meta: flush_handlers diff --git a/roles/firewall/templates/rules.v4 b/roles/firewall/templates/rules.v4 new file mode 100644 index 0000000..7ea1162 --- /dev/null +++ b/roles/firewall/templates/rules.v4 @@ -0,0 +1,48 @@ +*filter + +# By default, drop incoming packets: +:INPUT DROP [0:0] +:FORWARD DROP [0:0] +# By default, accept outgoing packets: +:OUTPUT ACCEPT [0:0] + +# Accept packets for localhost: +-A INPUT -i lo -j ACCEPT + +# Accept any packet for an open connection: +-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT + +# The SSH port is always open: +{% set ssh_port = hostvars[inventory_hostname].ansible_port %} + +# Open TCP ports: +{% set tcp_ports = firewall_ports_tcp + firewall_ports4_tcp + [ssh_port] %} +{% set tcp_ports = tcp_ports | unique %} + +{% for port in tcp_ports %} +{% set num = port.port if port.port is defined else port %} +{% set src = '-s ' + port.source if port.source is defined else '' %} +-A INPUT -p tcp {{ src }} --dport {{ num }} -m conntrack --ctstate NEW -j ACCEPT +{% endfor %} + +# Open UDP ports: +{% set udp_ports = firewall_ports_udp + firewall_ports4_udp %} +{% set udp_ports = udp_ports | unique %} + +{% for port in udp_ports %} +{% set num = port.port if port.port is defined else port %} +{% set src = '-s ' + port.source if port.source is defined else '' %} +-A INPUT -p udp {{ src }} --dport {{ num }} -j ACCEPT +{% endfor %} + +# Any additional IPv4 rules: +{{ firewall_rules4 | join('\n') }} + +# ICMP; allow only pings and rate-limit them: +-A INPUT -p icmp --icmp-type echo-request -m hashlimit --hashlimit-upto 5/s --hashlimit-mode srcip --hashlimit-srcmask 32 --hashlimit-name icmp-echo-drop -j ACCEPT + +# Log denies (this must be at the bottom of the file): +-A INPUT -m limit --limit 3/min -j LOG --log-prefix "iptables denied: " --log-level 4 + +COMMIT diff --git a/roles/firewall/templates/rules.v6 b/roles/firewall/templates/rules.v6 new file mode 100644 index 0000000..27bf58b --- /dev/null +++ b/roles/firewall/templates/rules.v6 @@ -0,0 +1,58 @@ +*filter + +# By default, drop incoming packets: +:INPUT DROP [0:0] +:FORWARD DROP [0:0] +# By default, accept outgoing packets: +:OUTPUT ACCEPT [0:0] + +# Accept packets for localhost: +-A INPUT -i lo -j ACCEPT + +# Accept any packet for an open connection: +-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT + +# The SSH port is always open: +{% set ssh_port = hostvars[inventory_hostname].ansible_port %} + +# Open TCP ports: +{% set tcp_ports = firewall_ports_tcp + firewall_ports6_tcp + [ssh_port] %} +{% set tcp_ports = tcp_ports | unique %} + +{% for port in tcp_ports %} +{% set num = port.port if port.port is defined else port %} +{% set src = '-s ' + port.source if port.source is defined else '' %} +-A INPUT -p tcp {{ src }} --dport {{ num }} -m conntrack --ctstate NEW -j ACCEPT +{% endfor %} + +# Open UDP ports: +{% set udp_ports = firewall_ports_udp + firewall_ports6_udp %} +{% set udp_ports = udp_ports | unique %} + +{% for port in udp_ports %} +{% set num = port.port if port.port is defined else port %} +{% set src = '-s ' + port.source if port.source is defined else '' %} +-A INPUT -p udp {{ src }} --dport {{ num }} -j ACCEPT +{% endfor %} + +# Any additional IPv6 rules: +{{ firewall_rules6 | join('\n') }} + +# ICMP; allow only pings and rate-limit them: +-A INPUT -p icmpv6 --icmpv6-type echo-request -m hashlimit --hashlimit-upto 5/s --hashlimit-mode srcip --hashlimit-srcmask 32 --hashlimit-name icmp-echo-drop -j ACCEPT + +# ICMP; IPv6 stuff. To be honest, I don't really understand it; this was copied +# from trailofbits/algo's rules.v6 template at +# +# https://github.com/trailofbits/algo/blob/master/roles/common/templates/rules.v6.j2 +# +-A INPUT -p icmpv6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT +-A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT +-A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT +-A INPUT -p icmpv6 --icmpv6-type redirect -m hl --hl-eq 255 -j ACCEPT + +# Log denies (this must be at the bottom of the file): +-A INPUT -m limit --limit 3/min -j LOG --log-prefix "iptables denied: " --log-level 4 + +COMMIT -- cgit v1.2.3