diff --git a/setup/start/cloudron-firewall.sh b/setup/start/cloudron-firewall.sh index 03e65dcf7..44bba7a0f 100755 --- a/setup/start/cloudron-firewall.sh +++ b/setup/start/cloudron-firewall.sh @@ -4,10 +4,15 @@ set -eu -o pipefail echo "==> Setting up firewall" -####### IPv4 +has_ipv6=$(cat /proc/net/if_inet6 >/dev/null 2>&1 && echo "yes" || echo "no") -iptables -t filter -N CLOUDRON || true -iptables -t filter -F CLOUDRON # empty any existing rules +function ipxtables() { + iptables "$@" + [[ "${has_ipv6}" == "yes" ]] && ip6tables "$@" +} + +ipxtables -t filter -N CLOUDRON || true +ipxtables -t filter -F CLOUDRON # empty any existing rules # first setup any user IP block lists ipset create cloudron_blocklist hash:net || true @@ -20,22 +25,22 @@ if ! iptables -t filter -C DOCKER-USER -m set --match-set cloudron_blocklist src fi # allow related and establisted connections -iptables -t filter -A CLOUDRON -m state --state RELATED,ESTABLISHED -j ACCEPT -iptables -t filter -A CLOUDRON -p tcp -m tcp -m multiport --dports 22,80,202,443 -j ACCEPT # 202 is the alternate ssh port +ipxtables -t filter -A CLOUDRON -m state --state RELATED,ESTABLISHED -j ACCEPT +ipxtables -t filter -A CLOUDRON -p tcp -m tcp -m multiport --dports 22,80,202,443 -j ACCEPT # 202 is the alternate ssh port # whitelist any user ports. we used to use --dports but it has a 15 port limit (XT_MULTI_PORTS) ports_json="/home/yellowtent/platformdata/firewall/ports.json" if allowed_tcp_ports=$(node -e "console.log(JSON.parse(fs.readFileSync('${ports_json}', 'utf8')).allowed_tcp_ports.join(','))" 2>/dev/null); then IFS=',' arr=(${allowed_tcp_ports}) for p in "${arr[@]}"; do - iptables -A CLOUDRON -p tcp -m tcp --dport "${p}" -j ACCEPT + ipxtables -A CLOUDRON -p tcp -m tcp --dport "${p}" -j ACCEPT done fi if allowed_udp_ports=$(node -e "console.log(JSON.parse(fs.readFileSync('${ports_json}', 'utf8')).allowed_udp_ports.join(','))" 2>/dev/null); then IFS=',' arr=(${allowed_udp_ports}) for p in "${arr[@]}"; do - iptables -A CLOUDRON -p udp -m udp --dport "${p}" -j ACCEPT + ipxtables -A CLOUDRON -p udp -m udp --dport "${p}" -j ACCEPT done fi @@ -44,7 +49,6 @@ ipset flush cloudron_ldap_allowlist ldap_allowlist_json="/home/yellowtent/platformdata/firewall/ldap_allowlist.txt" if [[ -f "${ldap_allowlist_json}" ]]; then - # without the -n block, any last line without a new line won't be read it! while read -r line || [[ -n "$line" ]]; do [[ -z "${line}" ]] && continue # ignore empty lines @@ -53,53 +57,56 @@ if [[ -f "${ldap_allowlist_json}" ]]; then done < "${ldap_allowlist_json}" # ldap server we expose 3004 and also redirect from standard ldaps port 636 - iptables -t nat -I PREROUTING -p tcp --dport 636 -j REDIRECT --to-ports 3004 + ipxtables -t nat -I PREROUTING -p tcp --dport 636 -j REDIRECT --to-ports 3004 iptables -t filter -A CLOUDRON -m set --match-set cloudron_ldap_allowlist src -p tcp --dport 3004 -j ACCEPT fi # turn and stun service -iptables -t filter -A CLOUDRON -p tcp -m multiport --dports 3478,5349 -j ACCEPT -iptables -t filter -A CLOUDRON -p udp -m multiport --dports 3478,5349 -j ACCEPT -iptables -t filter -A CLOUDRON -p udp -m multiport --dports 50000:51000 -j ACCEPT +ipxtables -t filter -A CLOUDRON -p tcp -m multiport --dports 3478,5349 -j ACCEPT +ipxtables -t filter -A CLOUDRON -p udp -m multiport --dports 3478,5349 -j ACCEPT +ipxtables -t filter -A CLOUDRON -p udp -m multiport --dports 50000:51000 -j ACCEPT +# ICMPv6 is very fundamental to IPv6 connectivity unlike ICMPv4 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 +ip6tables -t filter -A CLOUDRON -p ipv6-icmp -j ACCEPT + +ipxtables -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) +ipxtables -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 +ipxtables -t filter -A CLOUDRON -m limit --limit 2/min -j LOG --log-prefix "Packet dropped: " --log-level 7 +ipxtables -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 +# prepend our chain to the filter table +iptables -t filter -C INPUT -j CLOUDRON 2>/dev/null || iptables -t filter -I INPUT -j CLOUDRON +ip6tables -t filter -C INPUT -j CLOUDRON 2>/dev/null || ip6tables -t filter -I INPUT -j CLOUDRON # 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 +ipxtables -t filter -N CLOUDRON_RATELIMIT || true +ipxtables -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 +ipxtables -t filter -N CLOUDRON_RATELIMIT_LOG || true +ipxtables -t filter -F CLOUDRON_RATELIMIT_LOG # empty any existing rules +ipxtables -t filter -A CLOUDRON_RATELIMIT_LOG -m limit --limit 2/min -j LOG --log-prefix "IPTables RateLimit: " --log-level 7 +ipxtables -t filter -A CLOUDRON_RATELIMIT_LOG -j DROP # http https for port in 80 443; do - iptables -A CLOUDRON_RATELIMIT -p tcp --syn --dport ${port} -m connlimit --connlimit-above 5000 -j CLOUDRON_RATELIMIT_LOG + ipxtables -A CLOUDRON_RATELIMIT -p tcp --syn --dport ${port} -m connlimit --connlimit-above 5000 -j CLOUDRON_RATELIMIT_LOG done # ssh 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 5 -j CLOUDRON_RATELIMIT_LOG + ipxtables -A CLOUDRON_RATELIMIT -p tcp --dport ${port} -m state --state NEW -m recent --set --name "public-${port}" + ipxtables -A CLOUDRON_RATELIMIT -p tcp --dport ${port} -m state --state NEW -m recent --update --name "public-${port}" --seconds 10 --hitcount 5 -j CLOUDRON_RATELIMIT_LOG done # ldaps for port in 636 3004; do - iptables -A CLOUDRON_RATELIMIT -p tcp --syn --dport ${port} -m connlimit --connlimit-above 5000 -j CLOUDRON_RATELIMIT_LOG + ipxtables -A CLOUDRON_RATELIMIT -p tcp --syn --dport ${port} -m connlimit --connlimit-above 5000 -j CLOUDRON_RATELIMIT_LOG done # docker translates (dnat) 25, 587, 993, 4190 in the PREROUTING step @@ -117,31 +124,9 @@ for port in 3306 5432 6379 27017; do iptables -A CLOUDRON_RATELIMIT -p tcp --syn -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m connlimit --connlimit-above 5000 -j CLOUDRON_RATELIMIT_LOG done -if ! iptables -t filter -C INPUT -j CLOUDRON_RATELIMIT 2>/dev/null; then - iptables -t filter -I INPUT 1 -j CLOUDRON_RATELIMIT -fi +iptables -t filter -C INPUT -j CLOUDRON_RATELIMIT 2>/dev/null || iptables -t filter -I INPUT 1 -j CLOUDRON_RATELIMIT +ip6tables -t filter -C INPUT -j CLOUDRON_RATELIMIT 2>/dev/null || ip6tables -t filter -I INPUT 1 -j CLOUDRON_RATELIMIT # Workaround 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 - -####### IPv6 - -# if file exists and has some content (despite being 0 size) -if cat /proc/net/if_inet6 >/dev/null 2>&1; then - echo "==> Setting up IPv6 firewall" - ip6tables -t filter -N CLOUDRON || true - ip6tables -t filter -F CLOUDRON # empty any existing rules - - ip6tables -t filter -A CLOUDRON -m state --state RELATED,ESTABLISHED -j ACCEPT - ip6tables -t filter -A CLOUDRON -p tcp -m tcp -m multiport --dports 22,80,202,443 -j ACCEPT # 202 is the alternate ssh port - - ip6tables -t filter -A CLOUDRON -m limit --limit 2/min -j LOG --log-prefix "IP6Tables Packet Dropped: " --log-level 7 - # ip6tables -t filter -A CLOUDRON -j DROP - - if ! ip6tables -t filter -C INPUT -j CLOUDRON 2>/dev/null; then - ip6tables -t filter -I INPUT -j CLOUDRON - fi -fi - -echo "==> Setting up firewall done" +ipxtables -D FORWARD -j CLOUDRON_RATELIMIT || true +ipxtables -I FORWARD 1 -j CLOUDRON_RATELIMIT