Issue
The last production environment I deployed was running at DigitalOcean. Their cloud firewall does not support VRRP (yet!) which was an issue as this was required to achieve high availability and a proper handling of failed nodes.
Solution? A local firewall!
ipset
is a real nice tool for well-structured iptables and firewall configurations. It looks like ipset
is not that common - so I decided to share it here.
At the end you will know how to setup a firewall with the required iptables rules and ipsets to have a working icinga2 cluster with SSH, HTTP and HTTPS access from your office.
It will be ease to add new environments or drop old ones.
Keep in mind that this article just uses a very small subset of the capabilities of iptables and ipset. See the respective man pages for further information.
Environment
This was tested on
- Ubuntu 16.04
- Ubuntu 18.04.3
- CentOS 7.6
Please let me know if you experience any issues on other distributions. I’m happy to help and extend this how-to.
Setup
Installation - Ubuntu / Debian
The required packages are:
- ipset
- iptables
- netfilter-persistent
Install them via
sudo apt install ipset iptables netfilter-persistent
Note: iptables is probably already installed.
The installer will ask you if the currently applied iptables rules should be written to the /etc/iptables/rules.v4
and /etc/iptables/rules.v6
. Answer with yes if you applied your own rules!
Installation - CentOS / RedHat
The required packages are:
- ipset
- ipset-service
- iptables
- iptables-services
Install them via
sudo apt install ipset iptables iptables-services
Configuration
The configuration files will be stored in the following location:
Debian / Ubuntu
Content | Location |
---|---|
IPv4 rules | /etc/iptables/rules.v4 |
IPv6 rules | /etc/iptables/rules.v6 |
IPv4 ipsets | /etc/iptables/ipset.v4 |
IPv6 ipsets | /etc/iptables/ipset.v6 |
CentOS
Content | Location |
---|---|
IPv4 rules | /etc/sysconfig/iptables |
IPv6 rules | /etc/sysconfig/iptables6 |
IPv4 ipsets | /etc/iptables/ipset |
IPv6 ipsets | /etc/iptables/ipset6 |
None of the files requires permissions greater than 644.
The examples will show the configuration for IPv4. It works for IPv6, too, but I do not have as much experience with IPv6 as I have with IPv4 so I will not post any examples. The reason is obvious, I guess.
ipset
We will create the following ipsets:
Set name | Purpose |
---|---|
COMPANY-OPS-OFFICE-NETWORKS | SRC networks for SSH, HTTP and HTTPS requests |
UPSTREAM-DNS-SERVERS | IPs of used DNS servers |
MONITORING-MASTERS | IPs of both Icinga2 nodes |
COMPANY-SATELLITES | IPs of all satellites hosting services for the company itself |
CUSTOMER1-SATELLITES | IPs of all satellites in the environment of CUSTOMER1 |
ALL-SATELLITES | A list that groups all *-SATELLITES lists into a single list |
The names above are just examples. Feel free to change them or at least replace COMPANY by the name of your project or company and CUSTOMERx by the actual name (or the actual JIRA project key for example) of the customer.
Example ipset
/ ipset.v4
file
-N -! COMPANY-OPS-OFFICE-NETWORKS hash:net
-A -! COMPANY-OPS-OFFICE-NETWORKS 134.124.144.192/28
-A -! COMPANY-OPS-OFFICE-NETWORKS 234.214.24.72/29
-N -! UPSTREAM-DNS-SERVERS hash:net
-A -! UPSTREAM-DNS-SERVERS 8.8.8.8/32
-A -! UPSTREAM-DNS-SERVERS 8.8.4.4/32
-A -! UPSTREAM-DNS-SERVERS 1.1.1.1/32
-N -! MONITORING-MASTERS hash:net
-A -! MONITORING-MASTERS 10.135.135.71/32
-A -! MONITORING-MASTERS 10.135.135.96/32
-N -! COMPANY-SATELLITES hash:net
-A -! COMPANY-SATELLITES 12.13.14.15/32
-A -! COMPANY-SATELLITES 33.44.55.160/32
-A -! COMPANY-SATELLITES 11.12.33.160/27
-N -! CUSTOMER1-SATELLITES hash:net
-A -! CUSTOMER1-SATELLITES 66.77.88.99/32
-A -! CUSTOMER1-SATELLITES 66.79.87.96/32
-A -! CUSTOMER1-SATELLITES 66.80.88.196/32
-N -! ALL-SATELLITES list:set
-A -! ALL-SATELLITES MONITORING-MASTERS
-A -! ALL-SATELLITES COMPANY-SATELLITES
-A -! ALL-SATELLITES CUSTOMER1-SATELLITES
NOTES:
-
-N
adds a new set -
-A
adds an object to an existing set -
hash:net
&list:set
define the type set type -
-!
is a flag to ignore errors if the applied entry / set does already exists -
MONITORING-MASTERS
are added toALL-SATELLITES
to reduce the need of redundant iptables rules (see below)
Example iptables rules
Be careful and proceed only if you know what you’re doing! Make sure to just add the missing rules to your iptables rule file if you have your additional rules in place already!
The rules
- grant HTTP, HTTPS, SSH access to the server from your own network(s)
- allow connections from satellites to the local icinga2 nodes
- allow both icinga2 nodes to communicate with each other
- allow icinga2 to write to the local carbon-relay daemon and haproxy (or MySQL)
- allow icingaweb2 to connect to the local icinga2 daemon
- allow DNS queries to systemd-resolved and accept DNS responses from the upstream DNS servers
# Generated by iptables-save v1.6.1 on Thu Sep 5 21:42:45 2019
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -m comment --comment "already established Sessions are always allowed" -j ACCEPT
-A INPUT -s 127.0.0.1/32 -p udp -m udp --dport 53 -m comment --comment "DNS queries to the local systemd-resolved" -j ACCEPT
-A INPUT -m set --match-set UPSTREAM-DNS-SERVERS src -p udp -m udp --sport 53 -m comment --comment "Allow DNS responses from upstream servers" -j ACCEPT
-A INPUT -p tcp -i lo --dport 2013 -m comment --comment "Allow connections from icinga2 to carbon-relay" -j ACCEPT
-A INPUT -p tcp -i lo --dport 3306 -m comment --comment "Allow MySQL from localhost (icinga ido)" -j ACCEPT
-A INPUT -p tcp -i lo --dport 3307 -m comment --comment "Allow MySQL from localhost (director)" -j ACCEPT
-A INPUT -p tcp -i lo --dport 3308 -m comment --comment "Allow MySQL from localhost (icingaweb)" -j ACCEPT
-A INPUT -p tcp -i lo --dport 5665 -m comment --comment "Allow connections from icingaweb2 to the local backend" -j ACCEPT
-A INPUT -m set --match-set COMPANY-OPS-OFFICE-NETWORKS src -p tcp -m multiport --dports 22,80,443 -m comment --comment "SSH, HTTP, HTTPS access from company networks" -j ACCEPT
-A INPUT -m set --match-set COMPANY-OPS-OFFICE-NETWORKS src -p icmp -m icmp --icmp-type 8 -m comment --comment "ICMP Echo Requests - COMPANY Office" -j ACCEPT
-A INPUT -m set --match-set COMPANY-OPS-OFFICE-NETWORKS src -p icmp -m icmp --icmp-type 0 -m comment --comment "ICMP Echo Replies - COMPANY Offcie" -j ACCEPT
-A INPUT -m set --match-set COMPANY-OPS-OFFICE-NETWORKS src -p icmp -m icmp --icmp-type 3 -m comment --comment "ICMP Destination Unreachable - COMPANY Office" -j ACCEPT
-A INPUT -m set --match-set COMPANY-OPS-OFFICE-NETWORKS src -p icmp -m icmp --icmp-type 11 -m comment --comment "ICMP Time exceeded - COMPANY Office" -j ACCEPT
-A INPUT -m set --match-set MONITORING-MASTERS src -p vrrp -m comment --comment "Allow VRRP between cluster nodes" -j ACCEPT
-A INPUT -m set --match-set ALL-SATELLITES src -p icmp -m icmp --icmp-type 8 -m comment --comment "ICMP Echo Requests - COMPANY Satellites" -j ACCEPT
-A INPUT -m set --match-set ALL-SATELLITES src -p icmp -m icmp --icmp-type 0 -m comment --comment "ICMP Echo Replies - COMPANY Satellites" -j ACCEPT
-A INPUT -m set --match-set ALL-SATELLITES src -p icmp -m icmp --icmp-type 3 -m comment --comment "ICMP Destination Unreachable - COMPANY Satellites" -j ACCEPT
-A INPUT -m set --match-set ALL-SATELLITES src -p icmp -m icmp --icmp-type 11 -m comment --comment "ICMP Time exceeded - COMPANY Satellites" -j ACCEPT
-A INPUT -m set --match-set ALL-SATELLITES src -p tcp --dport 5665 -m comment --comment "Allow Icinga2 connections from all satellites" -j ACCEPT
COMMIT
NOTE 1: The MySQL rules are only required if a local haproxy as described in this awesome how-to from @anon66228339 Galera MySQL cluster with VIPs and HAProxy for IDO-Mysql and more
Note2: list:set
sets can not be added to list:set
sets. So we have the ICMP rules twice.
Manage services
CentOS
# enable services
sudo systemctl enable ipset
sudo systemctl enable iptables
# start the services
sudo service ipset start
sudo service iptables start
Ubuntu
The ipset service file needs to be created manually. Create /etc/systemd/system/ipset.service
with the following content:
[Unit]
Description=ipset persistent configuration
DefaultDependencies=no
Before=network.target
# ipset sets should be loaded before iptables
# Because creating iptables rules with names of non-existent sets is not possible and the iptables command will fail
Before=netfilter-persistent.service
ConditionFileNotEmpty=/etc/iptables/ipset.v4
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/ipset restore -file /etc/iptables/ipset
ExecStop=/sbin/ipset flush
ExecStopPost=/sbin/ipset destroy
[Install]
WantedBy=multi-user.target
RequiredBy=netfilter-persistent.service
Enable and start the services:
sudo chmod 644 /etc/systemd/system/ipset.service
# enable services (netfilter-persistent should already be enabled)
sudo systemctl enable ipset.service
sudo systemctl enable netfilter-persistent.service
# start services
sudo service ipset start
sudo service netfilter-persistent force-reload
Add iptables rules or ipsets
Additional satellites for the existing sets can be added by adding another line starting with ´-A -! <set-name> <ip>/<subnet>`.
Create additional ipsets for new environments with icinga2 satellites and don’t forget to add the set name to ALL-SATELLITES
.
Reload / restart the ipset and iptables services to apply changes on the ipset or iptables rules files.
Check applied configuration
Finally, you can check the applied configuration by running sudo iptables-save
or sudo iptables -L
and sudo ipset -L
.
Summary
Congratulations, now you have a very clean structured firewall setup. Use comments on iptables rules and choose the ipset names wisely. A clean structure allows you to modify the existing configuration easily and minimizes the risk for failures. Another advantage is that it’s easy to modify the configuration with your configuration management tools.