Skip to content

Commit ac94b7d

Browse files
authored
firewall: add new role (ethpandaops#179)
* firewall: add new role * firewall: fix shebang
1 parent e9107e0 commit ac94b7d

File tree

7 files changed

+259
-0
lines changed

7 files changed

+259
-0
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ A collection of reusable ansible components used by the EthPandaOps team.
2727
### Ethereum client pair
2828
- [ethereum_node](roles/ethereum_node)
2929
- [ethereum_node_fact_discovery](roles/ethereum_node_fact_discovery)
30+
3031
### Ethereum execution clients
3132
- [besu](roles/besu)
3233
- [erigon](roles/erigon)
3334
- [ethereumjs](roles/ethereumjs)
3435
- [geth](roles/geth)
3536
- [nethermind](roles/nethermind)
37+
3638
### Ethereum consensus clients
3739
- [lighthouse](roles/lighthouse)
3840
- [lodestar](roles/lodestar)
@@ -48,6 +50,7 @@ A collection of reusable ansible components used by the EthPandaOps team.
4850
- [docker_cleanup](roles/docker_cleanup)
4951
- [docker_network](roles/docker_network)
5052
- [docker_nginx_proxy](roles/docker_nginx_proxy)
53+
- [firewall](roles/firewall)
5154
- [json_rpc_snooper](roles/json_rpc_snooper)
5255
- [k3s](roles/k3s)
5356
- [litestream](roles/litestream)

roles/firewall/README.md

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# ethpandaops.general.firewall
2+
3+
This ansible role is forked and heavily based from https://github.com/geerlingguy/ansible-role-firewall which was initially created in 2014 by [Jeff Geerling](http://www.jeffgeerling.com/), author of [Ansible for DevOps](https://www.ansiblefordevops.com/).
4+
5+
---
6+
7+
Installs a simple iptables-based firewall for RHEL/CentOS or Debian/Ubuntu systems. Supports both IPv4 (`iptables`) and IPv6 (`ip6tables`).
8+
9+
This firewall aims for simplicity over complexity, and only opens a few specific ports for incoming traffic (configurable through Ansible variables). If you have a rudimentary knowledge of `iptables` and/or firewalls in general, this role should be a good starting point for a secure system firewall.
10+
11+
After the role is run, a `firewall` init service will be available on the server. You can use `service firewall [start|stop|restart|status]` to control the firewall.
12+
13+
> **Note regarding docker:** On systems where docker is installed, the firewall script will restart the docker daemon so that it can setup all the iptables rules required by docker.
14+
15+
## Requirements
16+
17+
None.
18+
19+
## Role Variables
20+
21+
Available variables are listed below, along with default values (see `defaults/main.yml`):
22+
23+
firewall_allowed_tcp_ports:
24+
- "22"
25+
- "80"
26+
...
27+
firewall_allowed_udp_ports: []
28+
29+
A list of TCP or UDP ports (respectively) to open to incoming traffic.
30+
31+
firewall_forwarded_tcp_ports:
32+
- { src: "22", dest: "2222" }
33+
- { src: "80", dest: "8080" }
34+
firewall_forwarded_udp_ports: []
35+
36+
Forward `src` port to `dest` port, either TCP or UDP (respectively).
37+
38+
firewall_additional_rules: []
39+
firewall_ip6_additional_rules: []
40+
41+
Any additional (custom) rules to be added to the firewall (in the same format you would add them via command line, e.g. `iptables [rule]`/`ip6tables [rule]`). A few examples of how this could be used:
42+
43+
# Allow only the IP 167.89.89.18 to access port 4949 (Munin).
44+
firewall_additional_rules:
45+
- "iptables -A INPUT -p tcp --dport 4949 -s 167.89.89.18 -j ACCEPT"
46+
47+
# Allow only the IP 214.192.48.21 to access port 3306 (MySQL).
48+
firewall_additional_rules:
49+
- "iptables -A INPUT -p tcp --dport 3306 -s 214.192.48.21 -j ACCEPT"
50+
51+
See [Iptables Essentials: Common Firewall Rules and Commands](https://www.digitalocean.com/community/tutorials/iptables-essentials-common-firewall-rules-and-commands) for more examples.
52+
53+
firewall_log_dropped_packets: true
54+
55+
Whether to log dropped packets to syslog (messages will be prefixed with "Dropped by firewall: ").
56+
57+
## Dependencies
58+
59+
None.
60+
61+
## Example Playbook
62+
63+
Your playbook could look like this:
64+
65+
```yaml
66+
- hosts: localhost
67+
become: true
68+
roles:
69+
- role: ethpandaops.general.firewall
70+
```

roles/firewall/defaults/main.yml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
firewall_allowed_tcp_ports:
3+
- "22"
4+
- "25"
5+
- "80"
6+
- "443"
7+
firewall_allowed_udp_ports: []
8+
firewall_forwarded_tcp_ports: []
9+
firewall_forwarded_udp_ports: []
10+
firewall_additional_rules: []
11+
firewall_ip6_additional_rules: []
12+
firewall_log_dropped_packets: true

roles/firewall/handlers/main.yml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
- name: Restart firewall
3+
ansible.builtin.service:
4+
name: firewall
5+
state: restarted

roles/firewall/tasks/main.yml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
- name: Ensure iptables is installed.
3+
ansible.builtin.package:
4+
name: iptables
5+
state: present
6+
7+
- name: Copy firewall script into place.
8+
ansible.builtin.template:
9+
src: firewall.bash.j2
10+
dest: /etc/firewall.bash
11+
owner: root
12+
group: root
13+
mode: "0744"
14+
notify: Restart firewall
15+
16+
- name: Copy firewall systemd unit file into place (for systemd systems).
17+
ansible.builtin.template:
18+
src: firewall.unit.j2
19+
dest: /etc/systemd/system/firewall.service
20+
owner: root
21+
group: root
22+
mode: "0755"
23+
when: >
24+
(ansible_distribution == 'Ubuntu' and ansible_distribution_version.split(".")[0]|int >= 16) or
25+
(ansible_distribution == 'Debian' and ansible_distribution_version.split(".")[0]|int >= 8) or
26+
(ansible_distribution == 'CentOS' and ansible_distribution_version.split(".")[0]|int >= 7) or
27+
(ansible_distribution == 'Fedora')
28+
29+
- name: Ensure the firewall is enabled and will start on boot.
30+
ansible.builtin.service:
31+
name: firewall
32+
state: started
33+
enabled: true
+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/bin/bash -eux
2+
# iptables firewall ( https://github.com/ethpandaops/ansible-collection-general/tree/master/roles/firewall )
3+
#
4+
# {{ ansible_managed }}
5+
6+
# No spoofing.
7+
if [ -e /proc/sys/net/ipv4/conf/all/rp_filter ]
8+
then
9+
for filter in /proc/sys/net/ipv4/conf/*/rp_filter
10+
do
11+
echo 1 > $filter
12+
done
13+
fi
14+
15+
# Completely reset the firewall by removing all rules and chains.
16+
iptables -P INPUT ACCEPT
17+
iptables -P FORWARD ACCEPT
18+
iptables -P OUTPUT ACCEPT
19+
iptables -t nat -F
20+
iptables -t mangle -F
21+
iptables -F
22+
iptables -X
23+
24+
# Accept traffic from loopback interface (localhost).
25+
iptables -A INPUT -i lo -j ACCEPT
26+
27+
# Forwarded ports.
28+
{# Add a rule for each forwarded port #}
29+
{% for forwarded_port in firewall_forwarded_tcp_ports %}
30+
iptables -t nat -I PREROUTING -p tcp --dport {{ forwarded_port.src }} -j REDIRECT --to-port {{ forwarded_port.dest }}
31+
iptables -t nat -I OUTPUT -p tcp -o lo --dport {{ forwarded_port.src }} -j REDIRECT --to-port {{ forwarded_port.dest }}
32+
{% endfor %}
33+
{% for forwarded_port in firewall_forwarded_udp_ports %}
34+
iptables -t nat -I PREROUTING -p udp --dport {{ forwarded_port.src }} -j REDIRECT --to-port {{ forwarded_port.dest }}
35+
iptables -t nat -I OUTPUT -p udp -o lo --dport {{ forwarded_port.src }} -j REDIRECT --to-port {{ forwarded_port.dest }}
36+
{% endfor %}
37+
38+
# Open ports.
39+
{# Add a rule for each open port #}
40+
{% for port in firewall_allowed_tcp_ports %}
41+
iptables -A INPUT -p tcp -m tcp --dport {{ port }} -j ACCEPT
42+
{% endfor %}
43+
{% for port in firewall_allowed_udp_ports %}
44+
iptables -A INPUT -p udp -m udp --dport {{ port }} -j ACCEPT
45+
{% endfor %}
46+
47+
# Accept icmp ping requests.
48+
iptables -A INPUT -p icmp -j ACCEPT
49+
50+
# Allow NTP traffic for time synchronization.
51+
iptables -A OUTPUT -p udp --dport 123 -j ACCEPT
52+
iptables -A INPUT -p udp --sport 123 -j ACCEPT
53+
54+
# Additional custom rules.
55+
{% for rule in firewall_additional_rules %}
56+
{{ rule }}
57+
{% endfor %}
58+
59+
# Allow established connections:
60+
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
61+
62+
# Log EVERYTHING (ONLY for Debug).
63+
# iptables -A INPUT -j LOG
64+
65+
{% if firewall_log_dropped_packets %}
66+
# Log other incoming requests (all of which are dropped) at 15/minute max.
67+
iptables -A INPUT -m limit --limit 15/minute -j LOG --log-level 7 --log-prefix "Dropped by firewall: "
68+
{% endif %}
69+
70+
# Drop all other traffic.
71+
iptables -A INPUT -j DROP
72+
73+
74+
# Configure IPv6 if ip6tables is present.
75+
if [ -x "$(which ip6tables 2>/dev/null)" ]; then
76+
77+
# Remove all rules and chains.
78+
ip6tables -F
79+
ip6tables -X
80+
81+
# Accept traffic from loopback interface (localhost).
82+
ip6tables -A INPUT -i lo -j ACCEPT
83+
84+
# Open ports.
85+
{# Add a rule for each open port #}
86+
{% for port in firewall_allowed_tcp_ports %}
87+
ip6tables -A INPUT -p tcp -m tcp --dport {{ port }} -j ACCEPT
88+
{% endfor %}
89+
{% for port in firewall_allowed_udp_ports %}
90+
ip6tables -A INPUT -p udp -m udp --dport {{ port }} -j ACCEPT
91+
{% endfor %}
92+
93+
# Accept icmp ping requests.
94+
ip6tables -A INPUT -p icmp -j ACCEPT
95+
96+
# Allow NTP traffic for time synchronization.
97+
ip6tables -A OUTPUT -p udp --dport 123 -j ACCEPT
98+
ip6tables -A INPUT -p udp --sport 123 -j ACCEPT
99+
100+
# Additional custom rules.
101+
{% for rule in firewall_ip6_additional_rules %}
102+
{{ rule }}
103+
{% endfor %}
104+
105+
# Allow established connections:
106+
ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
107+
108+
# Log EVERYTHING (ONLY for Debug).
109+
# ip6tables -A INPUT -j LOG
110+
111+
{% if firewall_log_dropped_packets %}
112+
# Log other incoming requests (all of which are dropped) at 15/minute max.
113+
ip6tables -A INPUT -m limit --limit 15/minute -j LOG --log-level 7 --log-prefix "Dropped by firewall: "
114+
{% endif %}
115+
116+
# Drop all other traffic.
117+
ip6tables -A INPUT -j DROP
118+
119+
fi
120+
121+
# Restart docker daemon if it's available
122+
if systemctl is-active --quiet docker > /dev/null 2>&1; then
123+
systemctl restart docker
124+
fi
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[Unit]
2+
Description=Firewall
3+
After=syslog.target network.target
4+
5+
[Service]
6+
Type=oneshot
7+
ExecStart=/etc/firewall.bash
8+
ExecStop=/sbin/iptables -F
9+
RemainAfterExit=yes
10+
11+
[Install]
12+
WantedBy=multi-user.target

0 commit comments

Comments
 (0)