Last active
January 7, 2024 22:52
-
-
Save jbfriedrich/6bcbc01550bcbfc0ad455c0c73d14f70 to your computer and use it in GitHub Desktop.
DigitalOcean Firewall Scripts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
################################################################################ | |
# DigitalOcean Droplet Firewall Script | |
#=============================================================================== | |
# This Firewall script is supposed to be used with DigitalOcean Droplets. It | |
# uses eth0 as external, and eth1 (if present) as internal network interface. | |
# It automatically detects IP addresses and blocks all traffic but SSH from a | |
# list of safe IP addresses. | |
# Two white lists can be defined, one for internal IPv4 IP addresses and one | |
# for public IPv4 addresses. | |
# Additional rules can be defined in two separate files (one for IPv4, one for | |
# IPv6) which get "sourced in" via iptables-restore. | |
#------------------------------------------------------------------------------- | |
# Version history: | |
# v1.0 | 2015-01-17 | |
# - Script created | |
# v1.1 | 2015-08-24 | |
# - Script cleaned up for upload to GitHub | |
################################################################################ | |
################################################################################ | |
## VARIABLES | |
################################################################################ | |
FW_DIR='/etc/firewall' | |
WHITELIST_EXT=('1.2.3.4/32' '5.6.7.8/32') | |
WHITELIST_EXT_LEN=${#WHITELIST_EXT[@]} | |
WHITELIST_INT=('10.0.0.1' '10.0.0.2' '10.0.0.3') | |
##WHITELIST_INT='' | |
WHITELIST_INT_LEN=${#WHITELIST_INT[@]} | |
IPT4="$(which iptables) -v" | |
IPT6="$(which ip6tables) -v" | |
IPT4_SAVE="$(which iptables-save)" | |
IPT4_RESTORE="$(which iptables-restore)" | |
IPT6_SAVE="$(which ip6tables-save)" | |
IPT6_RESTORE="$(which ip6tables-restore)" | |
IP_CMD="$(which ip)" | |
HOSTNAME_CMD="$(which hostname)" | |
EXTINT='eth0' | |
PRIVINT='eth1' | |
HOST_NAME="$(${HOSTNAME_CMD} --short)" | |
ADDT_RULES_IPV4="${FW_DIR}/${HOST_NAME}_rules.fw4" | |
ADDT_RULES_IPV6="${FW_DIR}/${HOST_NAME}_rules.fw6" | |
EXTIP4="$(${IP_CMD} addr show eth0 | grep -e inet | grep -v inet6 |\ | |
sed -e 's/^\s*inet //g' -e 's/\/[0-9]\{1,2\} brd.*$//g')" | |
EXTIP6="$(${IP_CMD} addr show eth0 | grep inet6 | grep -v 'fe80::' |\ | |
sed -e 's/^\s*inet6 //g' -e 's/ scope.*$//g')" | |
PRIVIP4="$(${IP_CMD} addr show eth1 | grep -e inet | grep -v inet6 |\ | |
sed -e 's/^\s*inet //g' \ | |
-e "s#\.[0-9]\{1,3\}\.[0-9]\{1,3\}\/[0-9]\{2\} brd.*\$##").0.0/16" | |
IPV6_DEFROUTE="$(${IP_CMD} -6 route show default | sed -e 's/default via //g' \ | |
-e 's/ dev.*$//g')" | |
################################################################################ | |
## FUNCTIONS | |
################################################################################ | |
function delfwrules { | |
echo 'INFO: Deleting all existing firewall rules' | |
## DELETE ALL EXISTING IPV4 RULES | |
${IPT4} -F | |
${IPT4} -X | |
${IPT4} -t nat -F | |
${IPT4} -t nat -X | |
## DELETE ALL EXISTING IPV6 RULES | |
${IPT6} -F | |
${IPT6} -X | |
# No IPv6 NAT by default enabled in most kernels | |
#${IPT6} -t nat -F | |
#${IPT6} -t nat -X | |
} | |
function enablefwd { | |
## ENABLE IPV4 FORWARDING | |
echo 'INFO: Enabling IPV4 forwarding' | |
echo 1 > /proc/sys/net/ipv4/ip_forward | |
echo 1 > /proc/sys/net/ipv4/conf/all/forwarding | |
# Enable only if system is a router. Otherwise router advertisements will | |
# be disabled and a static route needs to be defined | |
#echo 1 > /proc/sys/net/ipv6/conf/all/forwarding | |
} | |
function disablefwd { | |
## DISABLE IPV4 FORWARDING | |
echo 'INFO: Disabling IPV4 forwarding' | |
echo 0 > /proc/sys/net/ipv4/ip_forward | |
echo 0 > /proc/sys/net/ipv4/conf/all/forwarding | |
# Enable only if system is a router. Otherwise router advertisements will | |
# be disabled and a static route needs to be defined | |
#echo 0 > /proc/sys/net/ipv6/conf/all/forwarding | |
} | |
function setdefpol4 { | |
if [ "${1}" == "drop" ]; then | |
## DEFAULT POLICY = DROP | |
echo 'INFO: Setting default policy to: DROP' | |
${IPT4} -P INPUT DROP | |
${IPT4} -P OUTPUT DROP | |
${IPT4} -P FORWARD DROP | |
elif [ "${1}" == "reject" ]; then | |
## DEFAULT POLICY = REJECT | |
echo 'INFO: Setting default policy to: REJECT' | |
${IPT4} -P INPUT REJECT | |
${IPT4} -P OUTPUT REJECT | |
${IPT4} -P FORWARD REJECT | |
elif [ "${1}" == "accept" ]; then | |
## DEFAULT POLICY = ACCEPT | |
echo 'INFO: Setting default policy to: ACCEPT' | |
${IPT4} -P INPUT ACCEPT | |
${IPT4} -P OUTPUT ACCEPT | |
${IPT4} -P FORWARD ACCEPT | |
else | |
## DEFAULT POLICY = DROP | |
echo 'WARN: Keyword not identified, setting default policy to DROP' | |
${IPT4} -P INPUT DROP | |
${IPT4} -P OUTPUT DROP | |
${IPT4} -P FORWARD DROP | |
fi | |
} | |
function setdefpol6 { | |
if [ "${1}" == "drop" ]; then | |
## DEFAULT POLICY = DROP | |
echo 'INFO: Setting default policy to: DROP' | |
${IPT6} -P INPUT DROP | |
${IPT6} -P OUTPUT DROP | |
${IPT6} -P FORWARD DROP | |
elif [ "${1}" == "reject" ]; then | |
## DEFAULT POLICY = REJECT | |
echo 'INFO: Setting default policy to: REJECT' | |
${IPT6} -P INPUT REJECT | |
${IPT6} -P OUTPUT REJECT | |
${IPT6} -P FORWARD REJECT | |
elif [ "${1}" == "accept" ]; then | |
## DEFAULT POLICY = ACCEPT | |
echo 'INFO: Setting default policy to: ACCEPT' | |
${IPT6} -P INPUT ACCEPT | |
${IPT6} -P OUTPUT ACCEPT | |
${IPT6} -P FORWARD ACCEPT | |
else | |
## DEFAULT POLICY = DROP | |
echo 'WARN: Keyword not identified, setting default policy to DROP' | |
${IPT6} -P INPUT DROP | |
${IPT6} -P OUTPUT DROP | |
${IPT6} -P FORWARD DROP | |
fi | |
} | |
function allow_loopback { | |
echo 'INFO: Allowing ALL IPv4 from loopback devices' | |
${IPT4} -A INPUT -i lo -j ACCEPT | |
${IPT4} -A OUTPUT -o lo -j ACCEPT | |
${IPT4} -A FORWARD -i lo -o lo -j ACCEPT | |
echo 'INFO: Allowing ALL IPv6 from loopback devices' | |
${IPT6} -A INPUT -i lo -j ACCEPT | |
${IPT6} -A OUTPUT -o lo -j ACCEPT | |
${IPT6} -A FORWARD -i lo -o lo -j ACCEPT | |
} | |
function allow_related_established { | |
# RELATED AND ESTABLISHED CONNECTIONS IPv4 | |
echo 'INFO: Allow all established/related connections (IPv4)' | |
${IPT4} -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT | |
${IPT4} -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT | |
${IPT4} -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT | |
# RELATED AND ESTABLISHED CONNECTIONS IPv6 | |
echo 'INFO: Allow all established/related connections (IPv6)' | |
${IPT6} -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT | |
${IPT6} -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT | |
${IPT6} -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT | |
} | |
function allow_useful_icmp { | |
# ALLOW USEFUL ICMP IPv4 | |
echo 'INFO: Allowing useful ICMP (IPv4)' | |
${IPT4} -A INPUT -d ${EXTIP4} -i ${EXTINT} -p icmp --icmp-type 0 \ | |
-j ACCEPT -m comment --comment "ICMP Echo Reply" | |
${IPT4} -A INPUT -d ${EXTIP4} -i ${EXTINT} -p icmp --icmp-type 3 \ | |
-j ACCEPT -m comment --comment "ICMP Echo Request" | |
${IPT4} -A INPUT -d ${EXTIP4} -i ${EXTINT} -p icmp --icmp-type 8 \ | |
-j ACCEPT -m comment --comment "ICMP Echo Request" | |
${IPT4} -A INPUT -d ${EXTIP4} -i ${EXTINT} -p icmp --icmp-type 11 \ | |
-j ACCEPT -m comment --comment "ICMP Time Exceeded" | |
# ALLOW ICMP IPv6 | |
echo 'INFO: Allowing ICMP (IPv6)' | |
${IPT6} -A INPUT -p icmpv6 -j ACCEPT | |
${IPT6} -A FORWARD -p icmpv6 -j ACCEPT | |
${IPT6} -A OUTPUT -p icmpv6 -j ACCEPT | |
} | |
function allow_ipv6_linklocal { | |
echo 'INFO: Allowing IPv6 link local traffic' | |
${IPT6} -A INPUT -s fe80::/10 -j ACCEPT | |
${IPT6} -A OUTPUT -s fe80::/10 -j ACCEPT | |
} | |
function allow_ipv6_multicast { | |
echo 'INFO: Allowing IPv6 multicast traffic' | |
${IPT6} -A INPUT -d ff00::/8 -j ACCEPT | |
${IPT6} -A OUTPUT -d ff00::/8 -j ACCEPT | |
} | |
function allow_all_out { | |
echo 'INFO: Allowing all traffic out' | |
${IPT4} -A OUTPUT -s ${EXTIP4} -o ${EXTINT} -p all -j ACCEPT | |
${IPT4} -A OUTPUT -s ${PRIVIP4} -o ${PRIVINT} -p all -j ACCEPT | |
${IPT6} -A OUTPUT -o ${EXTINT} -p all -j ACCEPT | |
} | |
function enable_ipv4_logging { | |
echo 'INFO: Enabling logging for all IPv4 rules' | |
# create new rule for logging | |
${IPT4} -N LOGGING | |
# apply rule to all chains | |
${IPT4} -A INPUT -j LOGGING | |
${IPT4} -A OUTPUT -j LOGGING | |
${IPT4} -A FORWARD -j LOGGING | |
# limit logging | |
${IPT4} -A LOGGING -m limit --limit 5/min -j LOG \ | |
--log-prefix "[iptables-dropped]: " --log-level 4 | |
# only log dropped packages | |
${IPT4} -A LOGGING -j DROP | |
} | |
function add_failsafe_rules_ipv4 { | |
source_ip="${1}" | |
echo "INFO: Adding failsafe rules for IP ${source_ip} (IPv4)" | |
${IPT4} -A INPUT -d ${EXTIP4} -i ${EXTINT} -s ${source_ip} -p tcp \ | |
--dport 22 -j ACCEPT | |
${IPT4} -A INPUT -d ${EXTIP4} -i ${EXTINT} -s ${source_ip} -p udp \ | |
--dport 22 -j ACCEPT | |
} | |
function add_failsafe_rules_ipv6 { | |
source_ip="${1}" | |
echo "INFO: Adding failsafe rules for IP ${source_ip} (IPv6)" | |
${IPT6} -A INPUT -d ${EXTIP6} -i ${EXTINT} -s ${source_ip} -p tcp \ | |
--dport 22 -j ACCEPT | |
${IPT6} -A INPUT -d ${EXTIP6} -i ${EXTINT} -s ${source_ip} -p udp \ | |
--dport 22 -j ACCEPT | |
} | |
function allow_int_traffic_from_host { | |
source_ip="${1}" | |
echo "INFO: Allowing traffic from internal host ${source_ip} (IPv4)" | |
${IPT4} -A INPUT -d ${PRIVIP4} -i ${PRIVINT} -s ${source_ip} -p all \ | |
-j ACCEPT | |
} | |
################################################################################ | |
## MAIN | |
################################################################################ | |
if [ "${1}" == "enable" ]; then | |
# delete all current rules | |
delfwrules | |
# enable ip forwarding | |
enablefwd | |
# set default policy to drop | |
setdefpol4 drop | |
setdefpol6 drop | |
allow_related_established | |
allow_useful_icmp | |
allow_loopback | |
allow_all_out | |
if [ -n "${WHITELIST_EXT}" ]; then | |
for (( i=0; i<${WHITELIST_EXT_LEN}; i++ )); do | |
add_failsafe_rules_ipv4 ${WHITELIST_EXT[${i}]} | |
done | |
fi | |
if [ -n "${WHITELIST_INT}" ]; then | |
for (( i=0; i<${WHITELIST_INT_LEN}; i++ )); do | |
allow_int_traffic_from_host ${WHITELIST_INT[${i}]} | |
done | |
fi | |
if [ -f ${ADDT_RULES_IPV4} ]; then | |
${IPT4_RESTORE} -n < ${ADDT_RULES_IPV4} | |
fi | |
enable_ipv4_logging | |
if [ -f ${ADDT_RULES_IPV6} ]; then | |
${IPT6_RESTORE} -n < ${ADDT_RULES_IPV6} | |
fi | |
elif [ "${1}" == "disable" ]; then | |
delfwrules | |
disablefwd | |
setdefpol4 accept | |
setdefpol6 accept | |
else | |
echo "Usage: `basename $0` enable | disable | fwdonly" | |
echo " enable: Enable firewall" | |
echo " disable: Disable firewall" | |
exit 0 | |
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
################################################################################ | |
# DigitalOcean Droplet Firewall Rules | |
#=============================================================================== | |
# Host: server.name.org | |
# Public IPv4: | |
# Public IPv6: | |
# Internal IPv4: | |
#------------------------------------------------------------------------------- | |
# These firewall rules are to be used with the iptables-save / iptables-restore | |
# script suite. | |
#------------------------------------------------------------------------------- | |
# Version history: | |
# * v1.0 | 2015-01-19 | |
# - Rules created | |
################################################################################ | |
## NAT RULES | |
*nat | |
:PREROUTING ACCEPT [0:0] | |
:INPUT ACCEPT [0:0] | |
:OUTPUT ACCEPT [0:0] | |
:POSTROUTING ACCEPT [0:0] | |
COMMIT | |
## FILTER RULES | |
*filter | |
# Allow DNS access from everywhere | |
-A INPUT -d 1.2.3.4 -i eth0 -p udp --dport 53 -j ACCEPT | |
-A INPUT -d 1.2.3.4 -i eth0 -p tcp --dport 53 -j ACCEPT | |
# Allow SSH from home network | |
-A INPUT -d 1.2.3.4 -i eth0 -p udp --dport 22 -s 5.6.7.8/32 -j ACCEPT | |
-A INPUT -d 1.2.3.4 -i eth0 -p tcp --dport 22 -s 5.6.7.8/32 -j ACCEPT | |
# Allow DNS management from home network (temporary) | |
-A INPUT -i eth0 -p tcp --dport 80 -s 5.6.7.8 -j ACCEPT | |
-A INPUT -i eth0 -p tcp --dport 443 -s 5.6.7.8 -j ACCEPT | |
COMMIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
################################################################################ | |
# DigitalOcean Droplet Firewall Rules | |
#=============================================================================== | |
# Host: | |
# Public IPv4: | |
# Public IPv6: | |
# Internal IPv4: | |
#------------------------------------------------------------------------------- | |
# These firewall rules are to be used with the iptables-save / iptables-restore | |
# script suite. | |
#------------------------------------------------------------------------------- | |
# Version history: | |
# * v1.0 | 2015-01-19 | |
# - Rules created | |
################################################################################ | |
## NAT RULES | |
#*nat | |
#:PREROUTING ACCEPT [0:0] | |
#:INPUT ACCEPT [0:0] | |
#:OUTPUT ACCEPT [0:0] | |
#:POSTROUTING ACCEPT [0:0] | |
#COMMIT | |
## FILTER RULES | |
*filter | |
# Allow DNS access from everywhere | |
-A INPUT -d 2a03:xxxx:x:xx::xxx:xxx1 -i eth0 -p udp --dport 53 -j ACCEPT | |
-A INPUT -d 2a03:xxxx:x:xx::xxx:xxx1 -i eth0 -p tcp --dport 53 -j ACCEPT | |
COMMIT |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment