Skip to content

Instantly share code, notes, and snippets.

@jbfriedrich
Last active January 7, 2024 22:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbfriedrich/6bcbc01550bcbfc0ad455c0c73d14f70 to your computer and use it in GitHub Desktop.
Save jbfriedrich/6bcbc01550bcbfc0ad455c0c73d14f70 to your computer and use it in GitHub Desktop.
DigitalOcean Firewall Scripts
#!/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
################################################################################
# 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
################################################################################
# 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