iptables fun

Published on:
#!/bin/bash

# /etc/init.d/vimmau-whitelist

# Controls access to  aztek banana and jersey server ports.
# Allows us to whitelist a lot more ip ranges than AWS
# security groups allow.

# To be run only on vimmau hosts.

# AWS security groups will grant full access to banana server--$BANANA_PORT,
# and jersey server--$JERSEY_PORT. We control whitelists here.

# banana and jersey servers may or may not be on the same box. And we may move
# them around. Currently all aztek services run on a single box. We may
# move banana and/or jersey off to a separate box. So this script needs to
# always turn off access on boxes that don't have banana or jersey running
# as well as turn on access if they do.

# NOTE: We assume we are the only one that does anything with iptables on
# a given box. We broadly nuke all chains in all tables, etc.

# whitelist of current customer ip ranges
CUST_WHITELIST=/home/ubuntu/customer.whitelist
# whitelist for rfc1918 addresses and
# whitelist for other special access like the vpn endpoint
RFC1918_WHITELIST=/home/ubuntu/rfc1918-custom.whitelist
HASHSIZE=4096

if [ -z "$ALLOW_BANANA" ]; then
  # so since we currently run banana and jersey on same server we will
  # just hard code this to true. we will need to get more fancy if
  # banana and jersey on different servers.
  ALLOW_BANANA=true
fi
if [ -z "$ALLOW_JERSEY" ]; then
  # so since we currently run banana and jersey on same server we will
  # just hard code this to true. we will need to get more fancy if
  # banana and jersey on different servers.
  ALLOW_JERSEY=true
fi
if [ -z "$BANANA_PORT" ]; then
  BANANA_PORT=4959
fi
if [ -z "$JERSEY_PORT" ]; then
  JERSEY_PORT=4960
fi

echo "ALLOW_BANANA: $ALLOW_BANANA"
echo "ALLOW_JERSEY: $ALLOW_JERSEY"
echo "BANANA_PORT: $BANANA_PORT"
echo "JERSEY_PORT: $JERSEY_PORT"

start() {
  echo "start"
  # nuke all rules in all chains which essentially allows everything.
  open
  port80and443
  whitelist
  if [ "$ALLOW_BANANA" = true ] ; then
    allow_port $BANANA_PORT
  else
    echo "not allowing banana $BANANA_PORT"
  fi
  if [ "$ALLOW_JERSEY" = true ] ; then
    allow_port $JERSEY_PORT
  else
    echo "not allowing jersey $JERSEY_PORT"
  fi
  block_port $BANANA_PORT
  block_port $JERSEY_PORT
}

# So this redirects 80 to 4959 and 443 to 4960 -- doh. The way this works
# customers can use either port and it will work. No need to set or configure
# per customer ports. We will of course have to set port per customer for
# vimmau receptor, but not for server side.
# We can allow all 4 in AWS security groups or just the two we know the
# customer will use.
port80and443() {
  iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 4959
  iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 4960
}

# opens all ports and deletes the whitelist.
open() {
  iptables -F
  iptables -X
  iptables -t nat -F
  iptables -t nat -X
  iptables -t mangle -F
  iptables -t mangle -X
  iptables -P INPUT ACCEPT
  iptables -P FORWARD ACCEPT
  iptables -P OUTPUT ACCEPT
  ipset destroy whitelist &>/dev/null || true
}

# blocks 'all' ports and deletes the whitelist.
stop() {
  open
  block_port $BANANA_PORT
  block_port $JERSEY_PORT
}

whitelist() {
  echo "whitelist"
  ipset flush whitelist &>/dev/null || true
  load_whitelist $CUST_WHITELIST
  load_whitelist $RFC1918_WHITELIST
}

load_whitelist() {
  if [ -f $1 ] ; then
    # make sure there's an ipset named 'whitelist'.
    # this command seems to be idempotent.
    ipset -exist create whitelist hash:net hashsize $HASHSIZE
    # loop over lines in file
    while read rule; do
      # skip lines that start with #
      if [[ ${rule:0:1} == '#' ]]; then
        echo "skipping $rule"
      else
        ipset -exist add whitelist $rule
      fi
    done <"$1"
  else
    echo "$1 not found"
  fi
}

allow_port() {
  echo "allowing port: $1"
  iptables -v -A INPUT -m set --match-set whitelist src -p TCP --dport $1 -j ACCEPT
}

block_port() {
  echo "blocking port: $1"
  iptables -v -A INPUT -p TCP --dport $1 -j LOG --log-prefix "IPTABLES DROPPED: "
  iptables -v -A INPUT -p TCP --dport $1 -j DROP
}

status() {
  echo ""
  iptables -t nat -nvL
  echo ""
  iptables -nvL
  echo ""
  ipset list
}

if [ "$1" == "start" ]; then
  start
elif [ "$1" == "reload" ]; then
  whitelist
elif [ "$1" == "stop" ]; then
  stop
elif [ "$1" == "open" ]; then
  open
elif [ "$1" == "status" ]; then
  status
else
  echo "whitelist.sh <start|stop|reload|status>"
  echo "  start - allow only whitelisted ports and ips"
  echo "  stop - block both ports for all ips"
  echo "  reload - reload list of whitelisted ips"
  echo "  open - allow all ips for both ports (DANGER)"
  echo "  status - show rules and whitelist"
fi