How to make iptables configuration persistent using systemd

Make iptables configuration persistent using systemd file with additional possibility to disable firewall after defined period of time.

Shell script

Create /sbin/iptables-firewall.sh shell script. Edit firewall_start function to apply custom iptables configuration.

#!/bin/bash
# Configure iptables firewall

# Limit PATH
PATH="/sbin:/usr/sbin:/bin:/usr/bin"

# iptables configuration
firewall_start() {
  # Define chain to allow particular source addresses
  iptables -N chain-incoming-ssh
  iptables -A chain-incoming-ssh -s 192.168.1.149 -j ACCEPT -m comment --comment "local access"
  iptables -A chain-incoming-ssh -p tcp --dport 22 -j LOG  --log-prefix "[fw-inc-ssh] " -m limit --limit 6/min --limit-burst 4
  iptables -A chain-incoming-ssh -j DROP

  # Define chain to log and drop incoming packets
  iptables -N chain-incoming-log-and-drop
  iptables -A chain-incoming-log-and-drop -j LOG --log-prefix "[fw-inc-drop] " -m limit --limit 6/min --limit-burst 4
  iptables -A chain-incoming-log-and-drop -j DROP

  # Define chain to log and drop outgoing packets
  iptables -N chain-outgoing-log-and-drop
  iptables -A chain-outgoing-log-and-drop -j LOG --log-prefix "[fw-out-drop] " -m limit --limit 6/min --limit-burst 4
  iptables -A chain-outgoing-log-and-drop -j DROP

  # Drop invalid packets
  iptables -A INPUT -m conntrack --ctstate INVALID -j chain-incoming-log-and-drop

  # Accept everthing on loopback
  iptables -A INPUT  -i lo -j ACCEPT
  iptables -A OUTPUT -o lo -j ACCEPT

  # ACCEPT incoming packets for established connections
  iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

  # Accept incoming ICMP
  iptables -A INPUT -p icmp -j ACCEPT

  # Accept incoming SSH
  iptables -A INPUT -p tcp --dport 22 -j chain-incoming-ssh

  # Accept outgoing packets for established connections
  iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

  # Accept outgoing DNS
  iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT
  iptables -A OUTPUT -p udp --dport 53 -j ACCEPT

  # Accept outgoing NTP
  iptables -A OUTPUT -p tcp --dport 123 -j ACCEPT
  iptables -A OUTPUT -p udp --dport 123 -j ACCEPT

  # Accept outgoing HTTP/S
  iptables -A OUTPUT -p tcp --dport 80  -j ACCEPT
  iptables -A OUTPUT -p tcp --dport 443 -j ACCEPT

  # Accept outgoing SSH
  iptables -A OUTPUT -p tcp --dport 22  -j ACCEPT

  # Accept outgoing ICMP
  iptables -A OUTPUT -p icmp -j ACCEPT

  # Log not accounted outgoing traffic
  iptables -A OUTPUT -j chain-outgoing-log-and-drop

  # Log not accounted forwarding traffic
  iptables -A FORWARD -j chain-incoming-log-and-drop

  # Drop everything else
  iptables -P INPUT   DROP
  iptables -P FORWARD DROP
  iptables -P OUTPUT  DROP
}

# clear iptables configuration
firewall_stop() {
  iptables -F
  iptables -X
  iptables -P INPUT   ACCEPT
  iptables -P FORWARD ACCEPT
  iptables -P OUTPUT  ACCEPT
}

# execute action
case "$1" in
  start|restart)
    echo "Starting firewall"
    firewall_stop
    firewall_start
    ;;
  stop)
    echo "Stopping firewall"
    firewall_stop
    ;;
esac

Ensure that shell script owned by root.

$ sudo chown root:root /sbin/iptables-firewall.sh

Ensure that script is executable.

$ sudo chmod 750 /sbin/iptables-firewall.sh

systemd configuration

Create iptables-firewall service.

$ cat << EOF | sudo tee /etc/systemd/system/iptables-firewall.service
[Unit]
Description=iptables firewall service
After=network.target

[Service]
Type=oneshot
ExecStart=/home/milosz/iptables-firewall.sh start
RemainAfterExit=true
ExecStop=/home/milosz/iptables-firewall.sh stop
StandardOutput=journal

[Install]
WantedBy=multi-user.target
EOF

Create iptables-firewall-test service.

$ cat << EOF | sudo tee /etc/systemd/system/iptables-firewall-test.service
[Unit]
Description=iptables firewall service test
BindsTo=iptables-firewall.service
After=iptables-firewall.service

[Service]
Type=oneshot
ExecStart=/usr/bin/systemd-run --on-active=180 --timer-property=AccuracySec=1s /bin/systemctl stop iptables-firewall.service
StandardOutput=journal

[Install]
WantedBy=multi-user.target
EOF

Reload systemd manager configuration.

$ sudo systemctl daemon-reload

Usage

Test iptables firewall using iptables-firewall-test service.

$ sudo systemctl start iptables-firewall-test

It will start iptables-firewall service.

$ sudo systemctl status iptables-firewall
‚óŹ iptables-firewall.service - iptables firewall service
   Loaded: loaded (/etc/systemd/system/iptables-firewall.service; disabled; vendor preset: enabled)
   Active: active (exited) since Tue 2018-01-02 07:14:42 CST; 2min 32s ago
  Process: 772 ExecStart=/home/milosz/iptables-firewall.sh start (code=exited, status=0/SUCCESS)
 Main PID: 772 (code=exited, status=0/SUCCESS)
    Tasks: 0 (limit: 4915)
   CGroup: /system.slice/iptables-firewall.service

Jan 02 07:14:41 debian systemd[1]: Starting iptables firewall service...
Jan 02 07:14:41 debian iptables-firewall.sh[772]: Starting firewall
Jan 02 07:14:42 debian systemd[1]: Started iptables firewall service.

It will also schedule temporary timer to stop iptables-firewall service after 3 minutes.

$ sudo systemctl list-timers
NEXT                         LEFT          LAST                         PASSED      UNIT
Tue 2018-01-02 07:17:42 CST  2min 21s left n/a                          n/a         run-rb4c64fbca4ee4c
Tue 2018-01-02 10:58:01 CST  3h 42min left Mon 2018-01-01 18:54:12 CST  12h ago     apt-daily.timer
Wed 2018-01-03 06:29:06 CST  23h left      Tue 2018-01-02 06:10:06 CST  1h 5min ago apt-daily-upgrade.t
Wed 2018-01-03 06:59:14 CST  23h left      Tue 2018-01-02 06:59:14 CST  16min ago   systemd-tmpfiles-cl

4 timers listed.
Pass --all to see loaded but inactive timers, too.

Enable iptables-firewall service at boot.

$ sudo systemctl enable iptables-firewall

Start iptables-firewall service.

$ sudo systemctl start iptables-firewall

There is nothing more than standard systemd operations. Use iptables-firewall-test service to test iptables configuration, but remember that it will stop iptables-firewall service after 3 minutes.

Milosz Galazka's Picture

About Milosz Galazka

Milosz is a Linux Foundation Certified Engineer working for a successful Polish company as a system administrator and a long time supporter of Free Software Foundation and Debian operating system.