diff --git a/setup/start.sh b/setup/start.sh index 5c2db2d4f..22fff0918 100755 --- a/setup/start.sh +++ b/setup/start.sh @@ -33,89 +33,6 @@ timedatectl set-ntp 1 timedatectl set-timezone UTC hostnamectl set-hostname "${arg_fqdn}" -echo "==> Setting up firewall" -iptables -t filter -N CLOUDRON || true -iptables -t filter -F CLOUDRON # empty any existing rules - -# NOTE: keep these in sync with src/apps.js validatePortBindings -# allow ssh, http, https, ping, dns -iptables -t filter -I CLOUDRON -m state --state RELATED,ESTABLISHED -j ACCEPT -# caas has ssh on port 202 -if [[ "${arg_provider}" == "caas" ]]; then - iptables -A CLOUDRON -p tcp -m tcp -m multiport --dports 25,80,202,443,587,993,4190 -j ACCEPT -else - iptables -A CLOUDRON -p tcp -m tcp -m multiport --dports 25,80,22,443,587,993,4190 -j ACCEPT -fi - -iptables -t filter -A CLOUDRON -p icmp --icmp-type echo-request -j ACCEPT -iptables -t filter -A CLOUDRON -p icmp --icmp-type echo-reply -j ACCEPT -iptables -t filter -A CLOUDRON -p udp --sport 53 -j ACCEPT -iptables -t filter -A CLOUDRON -s 172.18.0.0/16 -j ACCEPT # required to accept any connections from apps to our IP: -iptables -t filter -A CLOUDRON -i lo -j ACCEPT # required for localhost connections (mysql) - -# log dropped incoming. keep this at the end of all the rules -iptables -t filter -A CLOUDRON -m limit --limit 2/min -j LOG --log-prefix "IPTables Packet Dropped: " --log-level 7 -iptables -t filter -A CLOUDRON -j DROP - -if ! iptables -t filter -C INPUT -j CLOUDRON 2>/dev/null; then - iptables -t filter -I INPUT -j CLOUDRON -fi - -# Setup rate limit chain (the recent info is at /proc/net/xt_recent) -iptables -t filter -N CLOUDRON_RATELIMIT || true -iptables -t filter -F CLOUDRON_RATELIMIT # empty any existing rules - -# log dropped incoming. keep this at the end of all the rules -iptables -t filter -N CLOUDRON_RATELIMIT_LOG || true -iptables -t filter -F CLOUDRON_RATELIMIT_LOG # empty any existing rules -iptables -t filter -A CLOUDRON_RATELIMIT_LOG -m limit --limit 2/min -j LOG --log-prefix "IPTables RateLimit: " --log-level 7 -iptables -t filter -A CLOUDRON_RATELIMIT_LOG -j DROP - -# http https -for port in 80 443; do - iptables -A CLOUDRON_RATELIMIT -p tcp --dport ${port} -m state --state NEW -m recent --set --name "public-${port}" - iptables -A CLOUDRON_RATELIMIT -p tcp --dport ${port} -m state --state NEW -m recent --update --name "public-${port}" --seconds 5 --hitcount 250 -j CLOUDRON_RATELIMIT_LOG -done - -# ssh smtp ssh msa imap sieve -for port in 22 202; do - iptables -A CLOUDRON_RATELIMIT -p tcp --dport ${port} -m state --state NEW -m recent --set --name "public-${port}" - iptables -A CLOUDRON_RATELIMIT -p tcp --dport ${port} -m state --state NEW -m recent --update --name "public-${port}" --seconds 10 --hitcount 10 -j CLOUDRON_RATELIMIT_LOG -done - -# TODO: move docker platform rules to platform.js so it can be specialized to rate limit only when destination is the mail container - -# docker translates (dnat) 25, 587, 993, 4190 in the PREROUTING step -for port in 2525 4190 9993; do - iptables -A CLOUDRON_RATELIMIT -p tcp ! -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m state --state NEW -m recent --set --name "public-${port}" - iptables -A CLOUDRON_RATELIMIT -p tcp ! -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m state --state NEW -m recent --update --name "public-${port}" --seconds 10 --hitcount 10 -j CLOUDRON_RATELIMIT_LOG -done - -# ldap, imap, sieve -for port in 3002 4190 9993; do - iptables -A CLOUDRON_RATELIMIT -p tcp -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m state --state NEW -m recent --set --name "private-${port}" - iptables -A CLOUDRON_RATELIMIT -p tcp -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m state --state NEW -m recent --update --name "private-${port}" --seconds 10 --hitcount 10 -j CLOUDRON_RATELIMIT_LOG -done - -# cloudron docker network: smtp mysql postgresql redis mongodb -for port in 2525 3306 5432 6379 27017; do - iptables -A CLOUDRON_RATELIMIT -p tcp -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m state --state NEW -m recent --set --name "private-${port}" - iptables -A CLOUDRON_RATELIMIT -p tcp -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m state --state NEW -m recent --update --name "private-${port}" --seconds 5 --hitcount 250 -j CLOUDRON_RATELIMIT_LOG -done - -# For ssh, http, https -if ! iptables -t filter -C INPUT -j CLOUDRON_RATELIMIT 2>/dev/null; then - iptables -t filter -I INPUT 1 -j CLOUDRON_RATELIMIT -fi - -# For smtp, imap etc routed via docker/nat -if ! iptables -t filter -C FORWARD -j CLOUDRON_RATELIMIT 2>/dev/null; then - iptables -t filter -I FORWARD 1 -j CLOUDRON_RATELIMIT -fi - -# so it gets restored across reboot -mkdir -p /etc/iptables && iptables-save > /etc/iptables/rules.v4 - echo "==> Configuring docker" cp "${script_dir}/start/docker-cloudron-app.apparmor" /etc/apparmor.d/docker-cloudron-app systemctl enable apparmor @@ -221,7 +138,11 @@ cp -r "${script_dir}/start/systemd/." /etc/systemd/system/ systemctl daemon-reload systemctl enable unbound systemctl enable cloudron.target -systemctl enable iptables-restore +systemctl disable iptables-restore || true # old versions of cloudron had this service +systemctl enable cloudron-firewall + +# update firewall rules +systemctl restart cloudron-firewall # For logrotate systemctl enable --now cron diff --git a/setup/start/cloudron-firewall.sh b/setup/start/cloudron-firewall.sh new file mode 100755 index 000000000..17f459b39 --- /dev/null +++ b/setup/start/cloudron-firewall.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +set -eu -o pipefail + +echo "==> Setting up firewall" +iptables -t filter -N CLOUDRON || true +iptables -t filter -F CLOUDRON # empty any existing rules + +# NOTE: keep these in sync with src/apps.js validatePortBindings +# allow ssh, http, https, ping, dns +iptables -t filter -I CLOUDRON -m state --state RELATED,ESTABLISHED -j ACCEPT +# caas has ssh on port 202 +iptables -A CLOUDRON -p tcp -m tcp -m multiport --dports 22,25,80,202,443,587,993,4190 -j ACCEPT + +iptables -t filter -A CLOUDRON -p icmp --icmp-type echo-request -j ACCEPT +iptables -t filter -A CLOUDRON -p icmp --icmp-type echo-reply -j ACCEPT +iptables -t filter -A CLOUDRON -p udp --sport 53 -j ACCEPT +iptables -t filter -A CLOUDRON -s 172.18.0.0/16 -j ACCEPT # required to accept any connections from apps to our IP: +iptables -t filter -A CLOUDRON -i lo -j ACCEPT # required for localhost connections (mysql) + +# log dropped incoming. keep this at the end of all the rules +iptables -t filter -A CLOUDRON -m limit --limit 2/min -j LOG --log-prefix "IPTables Packet Dropped: " --log-level 7 +iptables -t filter -A CLOUDRON -j DROP + +if ! iptables -t filter -C INPUT -j CLOUDRON 2>/dev/null; then + iptables -t filter -I INPUT -j CLOUDRON +fi + +# Setup rate limit chain (the recent info is at /proc/net/xt_recent) +iptables -t filter -N CLOUDRON_RATELIMIT || true +iptables -t filter -F CLOUDRON_RATELIMIT # empty any existing rules + +# log dropped incoming. keep this at the end of all the rules +iptables -t filter -N CLOUDRON_RATELIMIT_LOG || true +iptables -t filter -F CLOUDRON_RATELIMIT_LOG # empty any existing rules +iptables -t filter -A CLOUDRON_RATELIMIT_LOG -m limit --limit 2/min -j LOG --log-prefix "IPTables RateLimit: " --log-level 7 +iptables -t filter -A CLOUDRON_RATELIMIT_LOG -j DROP + +# http https +for port in 80 443; do + iptables -A CLOUDRON_RATELIMIT -p tcp --dport ${port} -m state --state NEW -m recent --set --name "public-${port}" + iptables -A CLOUDRON_RATELIMIT -p tcp --dport ${port} -m state --state NEW -m recent --update --name "public-${port}" --seconds 5 --hitcount 250 -j CLOUDRON_RATELIMIT_LOG +done + +# ssh smtp ssh msa imap sieve +for port in 22 202; do + iptables -A CLOUDRON_RATELIMIT -p tcp --dport ${port} -m state --state NEW -m recent --set --name "public-${port}" + iptables -A CLOUDRON_RATELIMIT -p tcp --dport ${port} -m state --state NEW -m recent --update --name "public-${port}" --seconds 10 --hitcount 10 -j CLOUDRON_RATELIMIT_LOG +done + +# TODO: move docker platform rules to platform.js so it can be specialized to rate limit only when destination is the mail container + +# docker translates (dnat) 25, 587, 993, 4190 in the PREROUTING step +for port in 2525 4190 9993; do + iptables -A CLOUDRON_RATELIMIT -p tcp ! -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m state --state NEW -m recent --set --name "public-${port}" + iptables -A CLOUDRON_RATELIMIT -p tcp ! -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m state --state NEW -m recent --update --name "public-${port}" --seconds 10 --hitcount 10 -j CLOUDRON_RATELIMIT_LOG +done + +# ldap, imap, sieve +for port in 3002 4190 9993; do + iptables -A CLOUDRON_RATELIMIT -p tcp -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m state --state NEW -m recent --set --name "private-${port}" + iptables -A CLOUDRON_RATELIMIT -p tcp -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m state --state NEW -m recent --update --name "private-${port}" --seconds 10 --hitcount 10 -j CLOUDRON_RATELIMIT_LOG +done + +# cloudron docker network: smtp mysql postgresql redis mongodb +for port in 2525 3306 5432 6379 27017; do + iptables -A CLOUDRON_RATELIMIT -p tcp -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m state --state NEW -m recent --set --name "private-${port}" + iptables -A CLOUDRON_RATELIMIT -p tcp -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m state --state NEW -m recent --update --name "private-${port}" --seconds 5 --hitcount 250 -j CLOUDRON_RATELIMIT_LOG +done + +# For ssh, http, https +if ! iptables -t filter -C INPUT -j CLOUDRON_RATELIMIT 2>/dev/null; then + iptables -t filter -I INPUT 1 -j CLOUDRON_RATELIMIT +fi + +# For smtp, imap etc routed via docker/nat +# Workaroud issue where Docker insists on adding itself first in FORWARD table +iptables -D FORWARD -j CLOUDRON_RATELIMIT || true +iptables -I FORWARD 1 -j CLOUDRON_RATELIMIT + diff --git a/setup/start/systemd/cloudron-firewall.service b/setup/start/systemd/cloudron-firewall.service new file mode 100644 index 000000000..5f02ece93 --- /dev/null +++ b/setup/start/systemd/cloudron-firewall.service @@ -0,0 +1,12 @@ +[Unit] +Description=Cloudron Firewall +After=docker.service +PartOf=docker.service + +[Service] +Type=oneshot +ExecStart="/home/yellowtent/box/setup/start/cloudron-firewall.sh" +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/setup/start/systemd/iptables-restore.service b/setup/start/systemd/iptables-restore.service deleted file mode 100644 index 82f104a86..000000000 --- a/setup/start/systemd/iptables-restore.service +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=IPTables Restore -Before=docker.service - -[Service] -Type=oneshot -ExecStart=/sbin/iptables-restore /etc/iptables/rules.v4 -RemainAfterExit=yes - -[Install] -WantedBy=multi-user.target