From 37cf3713db64ccd72806e2c0a5d92d997b498283 Mon Sep 17 00:00:00 2001 From: SNeef Date: Mon, 23 Jun 2025 13:23:27 +0200 Subject: [PATCH 1/2] WIP: Separate iptables rules into different chains --- ansible/roles/router_iptables/tasks/main.yml | 192 +++++++++++++------ 1 file changed, 138 insertions(+), 54 deletions(-) diff --git a/ansible/roles/router_iptables/tasks/main.yml b/ansible/roles/router_iptables/tasks/main.yml index 41aacfc..4047cf5 100644 --- a/ansible/roles/router_iptables/tasks/main.yml +++ b/ansible/roles/router_iptables/tasks/main.yml @@ -1,4 +1,10 @@ --- +- name: "Enable ipv4 forward" + sysctl: + name: net.ipv4.ip_forward + value: "1" + sysctl_set: true + - name: Allow wireguard input traffic (game) iptables: chain: INPUT @@ -15,75 +21,127 @@ protocol: udp jump: ACCEPT -- name: "Enable ipv4 forward" - sysctl: - name: net.ipv4.ip_forward - value: "1" - sysctl_set: true +- name: Create FW_RATELIMIT chain + iptables: + chain: FW_RATELIMIT + chain_management: true + state: present -- name: Allow related/established forward traffic +- name: Create FW_CONNTRACK chain + iptables: + chain: FW_CONNTRACK + chain_management: true + state: present + +- name: Create FW_INTERNAL chain + iptables: + chain: FW_INTERNAL + chain_management: true + state: present + +- name: Create FW_ROUTER_NETS chain + iptables: + chain: FW_ROUTER_NETS + chain_management: true + state: present + +- name: Create FW_TEAM_NETS chain + iptables: + chain: FW_TEAM_NETS + chain_management: true + state: present + +- name: Create FW_ROUTER chain + iptables: + chain: FW_ROUTER + chain_management: true + state: present + +- name: Create FW_TEAM_NO_MASQ chain + iptables: + table: nat + chain: FW_TEAM_NO_MASQ + chain_management: true + state: present + +- name: Insert FW_RATELIMIT into FORWARD iptables: chain: FORWARD - ctstate: [RELATED, ESTABLISHED] - jump: ACCEPT + in_interface: router + jump: FW_RATELIMIT + +- name: Insert FW_CONNTRACK into FORWARD + iptables: + chain: FORWARD + jump: FW_CONNTRACK + +- name: Insert FW_INTERNAL into FORWARD + iptables: + chain: FORWARD + out_interface: internal + jump: FW_INTERNAL + +- name: Insert FW_ROUTER_NETS into FORWARD + iptables: + chain: FORWARD + out_interface: router + jump: FW_ROUTER_NETS + +- name: Insert FW_TEAM_NETS into FORWARD + iptables: + chain: FORWARD + out_interface: team+ + jump: FW_TEAM_NETS + +- name: Insert FW_ROUTER into FORWARD + iptables: + chain: FORWARD + out_interface: router + jump: FW_ROUTER + +- name: Insert FW_TEAM_NO_MASQ into POSTROUTING + iptables: + chain: POSTROUTING + out_interface: router + jump: FW_TEAM_NO_MASQ - name: "Capture iptables-save output" shell: "iptables-save" register: iptablessave - name: "Insert limit bw internal" - shell: "iptables -I FORWARD -i router -o internal -m hashlimit --hashlimit-above 10mb/s --hashlimit-burst 20mb --hashlimit-mode srcip --hashlimit-name bwi --hashlimit-srcmask 24 -j DROP" - when: '"-A FORWARD -i router -o internal -m hashlimit --hashlimit-above 10mb/s --hashlimit-burst 20mb --hashlimit-mode srcip --hashlimit-name bwi --hashlimit-srcmask 24 -j DROP" not in iptablessave.stdout' + shell: "iptables -I FW_RATELIMIT -i router -o internal -m hashlimit --hashlimit-above 10mb/s --hashlimit-burst 20mb --hashlimit-mode srcip --hashlimit-name bwi --hashlimit-srcmask 24 -j DROP" + when: '"-A FW_RATELIMIT -i router -o internal -m hashlimit --hashlimit-above 10mb/s --hashlimit-burst 20mb --hashlimit-mode srcip --hashlimit-name bwi --hashlimit-srcmask 24 -j DROP" not in iptablessave.stdout' - name: "Insert limit bw other" - shell: "iptables -I FORWARD -i router ! -o internal -m hashlimit --hashlimit-above 5mb/s --hashlimit-burst 10mb --hashlimit-mode srcip --hashlimit-name bw --hashlimit-srcmask 24 -j DROP" - when: '"-A FORWARD -i router ! -o internal -m hashlimit --hashlimit-above 5mb/s --hashlimit-burst 10mb --hashlimit-mode srcip --hashlimit-name bw --hashlimit-srcmask 24 -j DROP" not in iptablessave.stdout' + shell: "iptables -I FW_RATELIMIT -i router ! -o internal -m hashlimit --hashlimit-above 5mb/s --hashlimit-burst 10mb --hashlimit-mode srcip --hashlimit-name bw --hashlimit-srcmask 24 -j DROP" + when: '"-A FW_RATELIMIT -i router ! -o internal -m hashlimit --hashlimit-above 5mb/s --hashlimit-burst 10mb --hashlimit-mode srcip --hashlimit-name bw --hashlimit-srcmask 24 -j DROP" not in iptablessave.stdout' - name: "Insert limit conn internal" - shell: "iptables -I FORWARD -i router -o internal -m conntrack --ctstate NEW -m hashlimit --hashlimit-above 2/sec --hashlimit-burst 10 --hashlimit-mode srcip,dstip --hashlimit-name subnewconns -j DROP" - when: '"-A FORWARD -i router -o internal -m conntrack --ctstate NEW -m hashlimit --hashlimit-above 2/sec --hashlimit-burst 10 --hashlimit-mode srcip,dstip --hashlimit-name subnewconns -j DROP" not in iptablessave.stdout' + shell: "iptables -I FW_RATELIMIT -i router -o internal -m conntrack --ctstate NEW -m hashlimit --hashlimit-above 2/sec --hashlimit-burst 10 --hashlimit-mode srcip,dstip --hashlimit-name subnewconns -j DROP" + when: '"-A FW_RATELIMIT -i router -o internal -m conntrack --ctstate NEW -m hashlimit --hashlimit-above 2/sec --hashlimit-burst 10 --hashlimit-mode srcip,dstip --hashlimit-name subnewconns -j DROP" not in iptablessave.stdout' - name: "Insert limit conn other" - shell: "iptables -I FORWARD -i router ! -o internal -m conntrack --ctstate NEW -m hashlimit --hashlimit-above 50/sec --hashlimit-burst 100 --hashlimit-mode srcip,dstip --hashlimit-name newconns -j DROP" - when: '"-A FORWARD -i router ! -o internal -m conntrack --ctstate NEW -m hashlimit --hashlimit-above 50/sec --hashlimit-burst 100 --hashlimit-mode srcip,dstip --hashlimit-name newconns -j DROP" not in iptablessave.stdout' + shell: "iptables -I FW_RATELIMIT -i router ! -o internal -m conntrack --ctstate NEW -m hashlimit --hashlimit-above 50/sec --hashlimit-burst 100 --hashlimit-mode srcip,dstip --hashlimit-name newconns -j DROP" + when: '"-A FW_RATELIMIT -i router ! -o internal -m conntrack --ctstate NEW -m hashlimit --hashlimit-above 50/sec --hashlimit-burst 100 --hashlimit-mode srcip,dstip --hashlimit-name newconns -j DROP" not in iptablessave.stdout' - name: Allow related/established forward traffic iptables: - chain: FORWARD + chain: FW_CONNTRACK ctstate: [RELATED, ESTABLISHED] jump: ACCEPT - name: Allow all internal traffic iptables: - chain: FORWARD + chain: FW_INTERNAL in_interface: internal out_interface: internal jump: ACCEPT -# this is a pretty hacky workaround, but the execution of the iptables-module in ansible is just painfully slow -- name: Prevent masquerading for intra-team traffic part - shell: "iptables -t nat -A POSTROUTING -s 10.1.{{ item }}.0/24 -d 10.1.{{ item }}.0/24 -o router -j RETURN" - when: '"-A POSTROUTING -s 10.1."+item+".0/24 -d 10.1."+item+".0/24 -o router -j RETURN" not in iptablessave.stdout' - with_sequence: start=1 end=255 - -- name: Masquerade outgoing traffic on the router interface - iptables: - table: nat - chain: POSTROUTING - out_interface: router - jump: MASQUERADE - -- name: DNAT the flag submission IP to the engine - iptables: - table: nat - chain: PREROUTING - destination: 10.0.13.37 - jump: DNAT - to_destination: 192.168.1.0 - name: Allow pinging the flag submission endpoint from vulnboxes iptables: - chain: FORWARD + chain: FW_INTERNAL in_interface: router out_interface: internal destination: 192.168.1.0 @@ -93,7 +151,7 @@ - name: Allow pinging the flag submission endpoint from vpn connections iptables: - chain: FORWARD + chain: FW_INTERNAL in_interface: team+ out_interface: internal destination: 192.168.1.0 @@ -103,7 +161,7 @@ - name: Allow flag submission traffic from vulnboxes iptables: - chain: FORWARD + chain: FW_INTERNAL in_interface: router out_interface: internal destination: 192.168.1.0 @@ -113,7 +171,7 @@ - name: Allow flag submission traffic from vpn connections iptables: - chain: FORWARD + chain: FW_INTERNAL in_interface: team+ out_interface: internal destination: 192.168.1.0 @@ -123,7 +181,7 @@ - name: Allow enoctfportal traffic from vulnboxes iptables: - chain: FORWARD + chain: FW_INTERNAL in_interface: router out_interface: internal destination: 192.168.1.0 @@ -133,7 +191,7 @@ - name: Allow enoctfportal traffic from vpn connections iptables: - chain: FORWARD + chain: FW_INTERNAL in_interface: team+ out_interface: internal destination: 192.168.1.0 @@ -141,26 +199,52 @@ destination_port: "5001" jump: ACCEPT + +# this is a pretty hacky workaround, but the execution of the iptables-module in ansible is just painfully slow +- name: Allow intra-team traffic part 1 (time-based) + shell: "iptables -A FW_ROUTER_NETS -s 10.1.{{ item }}.0/24 -d 10.1.{{ item }}.0/24 -o router -m time --datestart {{ vulnbox_access_time }} -j ACCEPT" + # when: '"-A FW_ROUTER_NETS -s 10.1."+item+".0/24 -d 10.1."+item+".0/24 -o router -j ACCEPT" not in iptablessave.stdout' # TODO: this is broken, needs to be revisited + with_sequence: start=1 end=255 + +- name: Allow intra-team traffic part 2 (time-based) + shell: "iptables -A FW_TEAM_NETS -s 10.1.{{ item }}.0/24 -d 10.1.{{ item }}.0/24 -o team+ -m time --datestart {{ vulnbox_access_time }} -j ACCEPT" + # when: '"-A FW_TEAM_NETS -s 10.1."+item+".0/24 -d 10.1."+item+".0/24 -o router -j ACCEPT" not in iptablessave.stdout' # TODO: this is broken, needs to be revisited + with_sequence: start=1 end=255 + + - name: Allow traffic from the internal network to teams iptables: - chain: FORWARD + chain: FW_ROUTER in_interface: internal out_interface: router jump: ACCEPT +- name: Open game network (time-based) + shell: "iptables -A FW_ROUTER -o router -m time --datestart {{ network_open_time }} --datestop {{ network_close_time }} -j ACCEPT" + when: '"-A FW_ROUTER -o router -m time --datestart {{ network_open_time }} --datestop {{ network_close_time }} -j ACCEPT" not in iptablessave.stdout' + + +- name: DNAT the flag submission IP to the engine + iptables: + table: nat + chain: PREROUTING + destination: 10.0.13.37 + jump: DNAT + to_destination: 192.168.1.0 + # this is a pretty hacky workaround, but the execution of the iptables-module in ansible is just painfully slow -- name: Allow intra-team traffic part 1 (time-based) - shell: "iptables -A FORWARD -s 10.1.{{ item }}.0/24 -d 10.1.{{ item }}.0/24 -o router -m time --datestart {{ vulnbox_access_time }} -j ACCEPT" - # when: '"-A FORWARD -s 10.1."+item+".0/24 -d 10.1."+item+".0/24 -o router -j ACCEPT" not in iptablessave.stdout' # TODO: this is broken, needs to be revisited +- name: Prevent masquerading for intra-team traffic part + shell: "iptables -t nat -A FW_TEAM_NO_MASQ -s 10.1.{{ item }}.0/24 -d 10.1.{{ item }}.0/24 -o router -j RETURN" + when: '"-A FW_TEAM_NO_MASQ -s 10.1."+item+".0/24 -d 10.1."+item+".0/24 -o router -j RETURN" not in iptablessave.stdout' with_sequence: start=1 end=255 -- name: Allow intra-team traffic part 2 (time-based) - shell: "iptables -A FORWARD -s 10.1.{{ item }}.0/24 -d 10.1.{{ item }}.0/24 -o team+ -m time --datestart {{ vulnbox_access_time }} -j ACCEPT" - # when: '"-A FORWARD -s 10.1."+item+".0/24 -d 10.1."+item+".0/24 -o router -j ACCEPT" not in iptablessave.stdout' # TODO: this is broken, needs to be revisited - with_sequence: start=1 end=255 +- name: Masquerade outgoing traffic on the router interface + iptables: + table: nat + chain: POSTROUTING + out_interface: router + jump: MASQUERADE -- name: Open game network (time-based) - shell: "iptables -A FORWARD -o router -m time --datestart {{ network_open_time }} --datestop {{ network_close_time }} -j ACCEPT" - name: Persist iptables config include_role: From 16a17e5443c34395b7c3c6cb6db8dea620a7bf0b Mon Sep 17 00:00:00 2001 From: SNeef Date: Mon, 23 Jun 2025 13:42:35 +0200 Subject: [PATCH 2/2] Fix bug in iptables rule --- ansible/roles/router_iptables/tasks/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/ansible/roles/router_iptables/tasks/main.yml b/ansible/roles/router_iptables/tasks/main.yml index 4047cf5..4725817 100644 --- a/ansible/roles/router_iptables/tasks/main.yml +++ b/ansible/roles/router_iptables/tasks/main.yml @@ -101,6 +101,7 @@ - name: Insert FW_TEAM_NO_MASQ into POSTROUTING iptables: + table: nat chain: POSTROUTING out_interface: router jump: FW_TEAM_NO_MASQ