├── .gitattributes ├── .gitignore ├── .travis.yml ├── 01_common └── iptables-reset.sh ├── CHANGELOG.mkd ├── CONTRIBUTING.mkd ├── LICENSE.txt ├── README.mkd ├── ipv4_basic_ingress ├── README.mkd └── iptables-init.sh ├── ipv6_basic_ingress └── ip6tables-init.sh └── tests ├── install.sh └── run_tests.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize line endings of text files 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /iptables-init-verified.sh 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: bash 2 | env: 3 | global: 4 | - SHELLCHECK_VERSION=0.4.5 5 | install: 6 | - ./tests/install.sh 7 | script: 8 | - ./tests/run_tests.sh 9 | notifications: 10 | email: false 11 | os: 12 | - linux 13 | -------------------------------------------------------------------------------- /01_common/iptables-reset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # src: unknown 4 | ### 5 | # Clear out any and all existing rules 6 | # Copied and pasted from time enternal 7 | # It's uber-overkill, but hey, what good script isn't? 8 | ### 9 | 10 | # Hardcode to have as few dependencies as possible 11 | #iptables_cmd="$(which iptables)" 12 | iptables_cmd="/sbin/iptables" 13 | 14 | if ! command -v "${iptables_cmd}" 1>/dev/null; then 15 | echo "[ipv4] Could not find ${iptables_cmd}. Skipping." >&2 16 | else 17 | while read -r tablename; do 18 | ${iptables_cmd} -F -t "${tablename}" 19 | ${iptables_cmd} -X -t "${tablename}" 20 | 21 | if [ "${tablename}" == "nat" ]; then 22 | ${iptables_cmd} -t nat -P PREROUTING ACCEPT 23 | ${iptables_cmd} -t nat -P POSTROUTING ACCEPT 24 | ${iptables_cmd} -t nat -P OUTPUT ACCEPT 25 | elif [ "${tablename}" == "mangle" ]; then 26 | ${iptables_cmd} -t mangle -P PREROUTING ACCEPT 27 | ${iptables_cmd} -t mangle -P INPUT ACCEPT 28 | ${iptables_cmd} -t mangle -P FORWARD ACCEPT 29 | ${iptables_cmd} -t mangle -P OUTPUT ACCEPT 30 | ${iptables_cmd} -t mangle -P POSTROUTING ACCEPT 31 | elif [ "${tablename}" == "filter" ]; then 32 | ${iptables_cmd} -t filter -P INPUT ACCEPT 33 | ${iptables_cmd} -t filter -P FORWARD ACCEPT 34 | ${iptables_cmd} -t filter -P OUTPUT ACCEPT 35 | fi 36 | done /dev/null; then 44 | echo "[ipv6] Could not find ${ip6tables_cmd}. Skipping." >&2 45 | else 46 | while read -r tablename; do 47 | ${ip6tables_cmd} -F -t "${tablename}" 48 | ${ip6tables_cmd} -X -t "${tablename}" 49 | 50 | if [ "${tablename}" == "nat" ]; then 51 | ${ip6tables_cmd} -t nat -P PREROUTING ACCEPT 52 | ${ip6tables_cmd} -t nat -P POSTROUTING ACCEPT 53 | ${ip6tables_cmd} -t nat -P OUTPUT ACCEPT 54 | elif [ "${tablename}" == "mangle" ]; then 55 | ${ip6tables_cmd} -t mangle -P PREROUTING ACCEPT 56 | ${ip6tables_cmd} -t mangle -P INPUT ACCEPT 57 | ${ip6tables_cmd} -t mangle -P FORWARD ACCEPT 58 | ${ip6tables_cmd} -t mangle -P OUTPUT ACCEPT 59 | ${ip6tables_cmd} -t mangle -P POSTROUTING ACCEPT 60 | elif [ "${tablename}" == "filter" ]; then 61 | ${ip6tables_cmd} -t filter -P INPUT ACCEPT 62 | ${ip6tables_cmd} -t filter -P FORWARD ACCEPT 63 | ${ip6tables_cmd} -t filter -P OUTPUT ACCEPT 64 | fi 65 | done /dev/null" >>/etc/rc.local 49 | ``` 50 | 51 | You'll want to view `/etc/rc.local` and double check you don't see something like `exit 0`. You can either remove it or move that line back to the bottome of the script. 52 | 53 | From that point on your work flow should be first edit the `iptables-init.sh` file. Then if after running that and are sure it works then copy it to `iptables-init-verified.sh`. In this way you should always be able to restart the system and have previous settings. 54 | 55 | The above process is for just IPv4. For IPv6 use `ipv6_basic_ingress`. The process is the same other than the file is called `ip6tables-init.sh`. You can use `iptables-reset.sh` to reset both IPv4 and IPv6. 56 | 57 | ## Monitoring firewall 58 | 59 | As root, run `iptables -nvL` to get a list of all rules and their hit count. 60 | 61 | By default any inbound traffic not handled by iptables is logged before being dropped. You can view this log by running `dmesg` as root. If you run `dmesg -c` it will print the kernel log and then clear it which makes it helpful to see what new log entries are being created. You can comment out that line if you don't care about logging everything (search for "-- log unhandled packets"). 62 | 63 | ## Configuration 64 | 65 | By default it will: 66 | 67 | - allow incoming requests to TCP ports 80 and 443 68 | - allow TCP/22 for ssh with a limiter (8 attempts/min) 69 | - by default will allow all outgoing traffic and block all incoming 70 | - some things will be logging and can be read with `dmesg` 71 | 72 | The file should be pretty self explanatory and be easy to add any more rules you need. 73 | 74 | ## Troubleshooting 75 | 76 | If the environment variable `VERBOSE` exists and is 'true' it will print additional messages while it's running. Additionally if there is a `DEBUG` that is 'true' it will print every line as it runs. Basically use `VERBOSE` if everything works but just want more info and `DEBUG` when things aren't working. 77 | 78 | ## Origin 79 | 80 | Gathered from far too many man pages and web sites to enumerate. This was also originally part of https://github.com/vrillusions/bash-scripts but split it off into it's own repo 81 | 82 | ## TODO 83 | 84 | - Make more user friendly. Currently need to modify things all throughout the file. Should have some variables at the top. 85 | - Make ssh port one of those settings since a lot of people don't run it on a standard port 86 | - Check for an environment variable like `IPTABLES_INIT_CONFIG` and source that file 87 | - Create some sort of generator process so I can set all the rules in one spot and spit out both `iptables` and `ip6tables` lines. 88 | 89 | ## License 90 | 91 | Primary license is the [unlicense](http://unlicense.org/). If where you are located doesn't honor public domain dedications then you may instead license this project under the [MIT license](http://opensource.org/licenses/MIT). Only required to use one of those licenses. See `LICENSE.txt` for actual licenses. 92 | 93 | Join the chat at https://gitter.im/vrillusions/iptables-init 94 | -------------------------------------------------------------------------------- /ipv4_basic_ingress/README.mkd: -------------------------------------------------------------------------------- 1 | ## Summary 2 | 3 | - IPv4 only 4 | - All egress traffic allowed 5 | - All traffic on local interface allowed 6 | - Ingress ICMP 7 | - destination-unreachable 8 | - source-quench 9 | - time-exceeded 10 | - parameter-problem 11 | - echo-request (ping requests) - limited to 10 per second 12 | - Ingress TCP 13 | - 22 - SSH - limited to 8 per minute 14 | - 80 - HTTP 15 | - 443 - HTTPS 16 | - Logging is enabled at a limit of 15 per minute 17 | -------------------------------------------------------------------------------- /ipv4_basic_ingress/iptables-init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Set iptables rules 3 | # 4 | # Environment Variables: 5 | # VERBOSE - if 'true' will print out some extra information. Without this it 6 | # will not output anything unless an error occurs 7 | # DEBUG - if 'true' will print each line as it runs 8 | # 9 | # Syntax notes: 10 | # In an attempt to not have really long lines I split lines up when setting a 11 | # new module (the '-m' option). Trying to be consistent even if the line 12 | # isn't too long. 13 | # 14 | # While the overhead is small keep in mind that iptables rules are evaluated 15 | # from the top down and the first one the matches is the action that is taken. 16 | # If a certain port is really popular you may want to consider moving it further 17 | # up. 18 | # 19 | # Licensed under a dual permissive license of Unlicense and MIT. See LICENSE.txt 20 | # 21 | 22 | # -- Exit on errors (-e) and Exit if try to use an unset variable (-u) -- 23 | set -e 24 | set -u 25 | 26 | # -- Only root can set iptables rules -- 27 | if [[ $EUID -ne 0 ]]; then 28 | printf "%s" "Must be run as root" >&2 29 | exit 1 30 | fi 31 | 32 | # -- Setup our env variables (need this or will exit if not set) -- 33 | readonly VERBOSE="${VERBOSE:-false}" 34 | readonly DEBUG="${DEBUG:-false}" 35 | 36 | if [[ "${DEBUG}" == "true" ]]; then 37 | set -x 38 | fi 39 | 40 | 41 | # == Helper functions == 42 | 43 | # -- print line to screen with timestamp -- 44 | # Usage: log "What to log" 45 | log () { 46 | # Uncomment this line to log messages to syslog 47 | #logger -s -t "${script_name}" -- "$*" 48 | printf "%b\n" "$(date +"%Y-%m-%dT%H:%M:%S%z") $*" 49 | } 50 | 51 | # -- only print line if verbose is true -- 52 | # Usage: verbose "What to log if VERBOSE is true" 53 | verbose () { 54 | if [[ "${VERBOSE}" == "true" ]]; then 55 | log "$*" 56 | fi 57 | } 58 | 59 | 60 | # -- setup iptables_cmd variable -- 61 | # Use this to change command that is run for entire script 62 | iptables_cmd=$(which iptables) || eval "echo 'Could not find iptables'; exit 1" 63 | 64 | 65 | verbose "Setting iptables rules" 66 | verbose "Clearing current rules" 67 | ${iptables_cmd} -F 68 | ${iptables_cmd} -Z 69 | ${iptables_cmd} -X 70 | 71 | 72 | # -- create LIMIT chain for throttled connections -- 73 | verbose "Creating LIMIT chain" 74 | ${iptables_cmd} -N LIMIT 75 | ${iptables_cmd} -A LIMIT -m limit --limit 3/min -j LOG --log-prefix "[LIMIT BLOCK] " 76 | ${iptables_cmd} -A LIMIT -j REJECT --reject-with icmp-port-unreachable 77 | 78 | 79 | # -- start setting specific iptables rules -- 80 | verbose "Creating main iptables rules" 81 | 82 | # -- allow established connections -- 83 | # ## IMPORTANT: For OpenVZ servers ## 84 | # - need to modprobe xt_tcpudp, ip_conntrack, and xt_state. To do this add 85 | # those three names to /etc/modules and restart. 86 | # - uncomment the state line and comment out the conntrack line if it doesn't 87 | # work. 88 | #${iptables_cmd} -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 89 | ${iptables_cmd} -I INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 90 | 91 | 92 | # -- allow all traffic on local interface -- 93 | ${iptables_cmd} -A INPUT -i lo -j ACCEPT 94 | 95 | 96 | # -- ICMP types that should always be allowed -- 97 | # Messages about a host not being reachable 98 | ${iptables_cmd} -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT 99 | # Messages to slow down how fast your sending data 100 | ${iptables_cmd} -A INPUT -p icmp --icmp-type source-quench -j ACCEPT 101 | # Time-to-live is exceeded 102 | ${iptables_cmd} -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT 103 | # Something about the packet that the server sent is wrong 104 | ${iptables_cmd} -A INPUT -p icmp --icmp-type parameter-problem -j ACCEPT 105 | # The ping command defaults to one ping a second. Use 10 per second or else if 106 | # just two people are pinging a server it won't respond or be really 107 | # intermittent. Ping flood attacks are thousands a second so this is still 108 | # a lot better than nothing. Deleting this line will block pings but that isn't 109 | # recommended on servers. 110 | ${iptables_cmd} -A INPUT -p icmp --icmp-type echo-request -j ACCEPT \ 111 | -m limit --limit 10/second 112 | 113 | 114 | # -- limit how fast incoming ssh connections can happen -- 115 | # This sets it to 8 times (value for hitcount) every 60 seconds (value for 116 | # seconds). Feel free to adjust those. This is per source ip meaning if some 117 | # botnet is flooding ssh server you should still be able to ssh from your 118 | # location. 119 | ${iptables_cmd} -A INPUT -p tcp -m tcp --dport 22 \ 120 | -m state --state NEW \ 121 | -m recent --set --name SSHLIMIT --rsource 122 | ${iptables_cmd} -A INPUT -p tcp -m tcp --dport 22 \ 123 | -m state --state NEW \ 124 | -m recent --update --seconds 60 --hitcount 8 --name SSHLIMIT --rsource -j LIMIT 125 | ${iptables_cmd} -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT 126 | 127 | 128 | # -- allow aditional ports -- 129 | # ssh is handled above but if you don't want limiter then comment or remove 130 | # above and uncomment this line 131 | #${iptables_cmd} -A INPUT -p tcp --dport 22 -j ACCEPT 132 | ${iptables_cmd} -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT 133 | # example of just 80 134 | #${iptables_cmd} -A INPUT -p tcp --dport 80 -j ACCEPT 135 | # example to allow full access from a single ip 136 | #${iptables_cmd} -A INPUT -s 192.231.162.123/32 -j ACCEPT 137 | 138 | 139 | # -- log unhandled packets (run `dmesg` to see log entries) -- 140 | # Will be logged to kernel, viewable by running `dmesg`. Once you have things 141 | # setup probably want to comment this out so you're not filling logs 142 | ${iptables_cmd} -A INPUT -m limit --limit 15/min -j LOG --log-prefix "[UNHANDLED INPUT PKT] " 143 | 144 | 145 | # -- set preferences for any traffic that does match above rules -- 146 | # allow unhandled outgoing traffic but silently drop unhandled incoming traffic 147 | ${iptables_cmd} -P INPUT DROP 148 | ${iptables_cmd} -P FORWARD DROP 149 | ${iptables_cmd} -P OUTPUT ACCEPT 150 | 151 | verbose "iptables setup has completed. Can view stats by running 'iptables -nvL' as root" 152 | 153 | exit 0 154 | -------------------------------------------------------------------------------- /ipv6_basic_ingress/ip6tables-init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Set ip6tables rules 3 | # 4 | # Environment Variables: 5 | # VERBOSE - if 'true' will print out some extra information. Without this it 6 | # will not output anything unless an error occurs 7 | # DEBUG - if 'true' will print each line as it runs 8 | # 9 | # Syntax notes: 10 | # In an attempt to not have really long lines I split lines up when setting a 11 | # new module (the '-m' option). Trying to be consistent even if the line 12 | # isn't too long. 13 | # 14 | # While the overhead is small keep in mind that iptables rules are evaluated 15 | # from the top down and the first one the matches is the action that is taken. 16 | # If a certain port is really popular you may want to consider moving it further 17 | # up 18 | # 19 | 20 | set -e 21 | set -u 22 | 23 | if [[ $EUID -ne 0 ]]; then 24 | echo "Must be run as root" 25 | exit 1 26 | fi 27 | 28 | readonly VERBOSE=${VERBOSE:-false} 29 | readonly DEBUG=${DEBUG:-false} 30 | 31 | if [[ "${DEBUG}" == "true" ]]; then 32 | set -x 33 | fi 34 | 35 | # Logging Functions 36 | # Usage: log "What to log" 37 | log () { 38 | printf "%b\n" "$(date +"%Y-%m-%dT%H:%M:%S%z") $*" 39 | } 40 | # Usage: verbose "What to log if VERBOSE is true" 41 | verbose () { 42 | if [[ "${VERBOSE}" == "true" ]]; then 43 | log "$*" 44 | fi 45 | } 46 | 47 | 48 | ip6tables_cmd=$(which ip6tables) || eval "echo 'Please install ip6tables'; exit 1" 49 | 50 | 51 | verbose "Setting ip6tables rules" 52 | verbose "Clearing current rules" 53 | ${ip6tables_cmd} -F 54 | ${ip6tables_cmd} -Z 55 | ${ip6tables_cmd} -X 56 | 57 | 58 | # again, choose either conntrack or state, don't need both 59 | #${ip6tables_cmd} -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 60 | ${ip6tables_cmd} -I INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 61 | ${ip6tables_cmd} -A INPUT -i lo -j ACCEPT 62 | 63 | 64 | # allow link-local communications 65 | ${ip6tables_cmd} -A INPUT -s fe80::/10 -j ACCEPT 66 | 67 | 68 | # for stateless autoconfiguration (restrict NDP messages to hop limit of 255) 69 | # commented out on openvz containers 70 | #${ip6tables_cmd} -A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT 71 | #${ip6tables_cmd} -A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT 72 | #${ip6tables_cmd} -A INPUT -p icmpv6 --icmpv6-type router-solicitation -m hl --hl-eq 255 -j ACCEPT 73 | #${ip6tables_cmd} -A INPUT -p icmpv6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT 74 | 75 | 76 | # ok icmp codes 77 | ${ip6tables_cmd} -A INPUT -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT 78 | ${ip6tables_cmd} -A INPUT -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT 79 | ${ip6tables_cmd} -A INPUT -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT 80 | ${ip6tables_cmd} -A INPUT -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT 81 | ${ip6tables_cmd} -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT -m limit --limit 60/minute 82 | # need this for ip6tables but not iptables 83 | ${ip6tables_cmd} -A INPUT -p icmpv6 --icmpv6-type echo-reply -j ACCEPT 84 | 85 | 86 | # Additional ports to open 87 | ${ip6tables_cmd} -A INPUT -p tcp --dport 22 -j ACCEPT 88 | ${ip6tables_cmd} -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT 89 | 90 | 91 | verbose "Setting defaults" 92 | ${ip6tables_cmd} -P INPUT DROP 93 | ${ip6tables_cmd} -P FORWARD DROP 94 | ${ip6tables_cmd} -P OUTPUT ACCEPT 95 | 96 | 97 | verbose "ip6tables setup has completed. Can view stats by running 'ip6tables -nvL' as root" 98 | 99 | exit 0 100 | -------------------------------------------------------------------------------- /tests/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Installs shellcheck using the precompiled ones at 4 | # https://github.com/caarlos0/shellcheck-docker 5 | # 6 | # Environment Variables: 7 | # SHELLCHECK_VERSION: version of shellcheck to use, defaults to 0.4.3 8 | # 9 | # Exit codes: 10 | # 0 No error 11 | # 96 Problem while parsing options 12 | # 13 | 14 | 15 | set -e 16 | set -u 17 | 18 | 19 | # -- script constants -- 20 | # set script_dir to location this script is running in 21 | readonly script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 22 | 23 | 24 | # -- env variables -- 25 | SHELLCHECK_VERSION="${SHELLCHECK_VERSION:-"0.4.3"}" 26 | 27 | 28 | # -- logging functions -- 29 | # Usage: log "What to log" 30 | log () { 31 | # logger will output to syslog, useful for background tasks 32 | #logger -s -t "${script_name}" -- "$*" 33 | # printf is good for scripts run manually when needed 34 | printf "%b\n" "$(date +"%Y-%m-%dT%H:%M:%S%z") $*" 35 | } 36 | 37 | 38 | if [[ "$(uname -s)" == "Darwin" ]]; then 39 | log "OS X detected, attempting install with homebrew" 40 | brew update 41 | brew install shellcheck 42 | else 43 | log "Performing linux environment install" 44 | if [[ ! -d "${HOME}/bin" ]]; then 45 | mkdir ~/bin 46 | fi 47 | curl -Ls \ 48 | -o "${HOME}/bin/shellcheck" \ 49 | "https://github.com/caarlos0/shellcheck-docker/releases/download/v${SHELLCHECK_VERSION}/shellcheck" 50 | chmod +x "${HOME}/bin/shellcheck" 51 | fi 52 | 53 | log 'Install process finished' 54 | 55 | exit 0 56 | -------------------------------------------------------------------------------- /tests/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Runs tests. In this case it's shellcheck across all *.sh files 4 | # 5 | 6 | 7 | set -e 8 | set -u 9 | 10 | 11 | # -- script constants -- 12 | # set script_dir to location this script is running in 13 | readonly script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 14 | 15 | 16 | PATH="${HOME}/bin:${PATH}" 17 | 18 | 19 | # -- logging functions -- 20 | # Usage: log "What to log" 21 | log () { 22 | # logger will output to syslog, useful for background tasks 23 | #logger -s -t "${script_name}" -- "$*" 24 | # printf is good for scripts run manually when needed 25 | printf "%b\n" "$(date +"%Y-%m-%dT%H:%M:%S%z") $*" 26 | } 27 | 28 | log "Running shellcheck in $(pwd)" 29 | 30 | if [[ "$(uname -s)" == "Darwin" ]]; then 31 | # OS X doesn't have the -r option 32 | xargs_custom_opts='' 33 | else 34 | xargs_custom_opts='-r' 35 | fi 36 | 37 | # If you want a more verbose run then add after 'xargs -0' the options '-n1 -t'. 38 | # This will test a single file at a time and print what file it's testing. Could 39 | # be useful if you hit some weird issue and you don't know what file is causing 40 | # it. Also without those options it doesn't list out the files so you're not 41 | # certain if it's actually being picked up. 42 | retcode=0 43 | find . -type f -name "*.sh" -print0 \ 44 | | xargs -0 ${xargs_custom_opts} shellcheck \ 45 | || retcode=$? 46 | 47 | log "Finished" 48 | 49 | exit $retcode 50 | --------------------------------------------------------------------------------