/root/bin/mk-iptables-conf-client.sh (1)

From RaySoft
#!/bin/bash -
#-------------------------------------------------------------------------------
# mk-iptables-conf-client.sh
# ==========================
#
# Project   Gentoo 4 Shuttle DS57Ux
# Scope     Linux
# Copyright (C) 2022 by RaySoft, Zurich, Switzerland
# License   GNU General Public License (GPL) 2.0
#           https://www.gnu.org/licenses/gpl2.txt
#
#-------------------------------------------------------------------------------
#
# http://ipset.netfilter.org/iptables.man.html
# http://ipset.netfilter.org/ip6tables.man.html
#
# http://ipset.netfilter.org/iptables-extensions.man.html
#
# http://www.iana.org/assignments/icmp-parameters
# http://www.iana.org/assignments/icmpv6-parameters
#
#-------------------------------------------------------------------------------

set -o 'noglob' -o 'pipefail' # -o 'xtrace' -o 'errexit'

#-------------------------------------------------------------------------------
# Variables
# ---------

IF_LAN='eno1'
IF_LO='lo'

#-------------------------------------------------------------------------------
# Define binaries
# ---------------

BASENAME=('/usr/bin/basename')
IPTABLES=('/sbin/iptables')
IP6TABLES=('/sbin/ip6tables')
SYSCTL=('/usr/sbin/sysctl')

BINS=("${BASENAME[0]}" "${IPTABLES[0]}" "${IP6TABLES[0]}" "${SYSCTL[0]}")

#-------------------------------------------------------------------------------
# Carp (Library: My)
# ------------------

my::carp() {
  local action=()

  if [[ "$1" =~ ^-e([0-9]*)$ ]]; then
    action=('exit' "${BASH_REMATCH[1]:-1}"); shift
  fi

  echo -n "$("${BASENAME[@]}" "$0")[${BASH_LINENO[-2]}]" 1>&2

  for ((i = ${#FUNCNAME[@]}-2; i >= 1; i--)); do
    echo -n ">${FUNCNAME[${i}]}[${BASH_LINENO[$((i - 1))]}]" 1>&2
  done

  echo ": $*" 1>&2

  if [[ "${action[0]}" == 'exit' ]]; then
    "${action[@]}"
  fi
}

#-------------------------------------------------------------------------------
# iptables (Library: iptables)
# ----------------------------

it::iptables() {
  local target="$1"; shift

  local func_info="${FUNCNAME[1]}[${BASH_LINENO[0]}]"
  local log_info="iptables (${func_info}:${target}): "

  if [[ "$1" == 'log' ]]; then
    shift

    if ! "${IPTABLES[@]}" "$@" \
           --match 'limit' --limit '1/second' \
           --jump 'LOG' --log-level 'info' --log-prefix "${log_info}"; then
      my::carp -e 'Error executing iptables'
    fi
  fi

  if ! "${IPTABLES[@]}" "$@" \
         --match 'comment' --comment "${func_info}" \
         --jump "${target}"; then
    my::carp -e 'Error executing iptables'
  fi
}

#-------------------------------------------------------------------------------
# ip6tables (Library: iptables)
# -----------------------------

it::ip6tables() {
  local target="$1"; shift

  local func_info="${FUNCNAME[1]}[${BASH_LINENO[0]}]"
  local log_info="iptables (${func_info}:${target}): "

  if [[ "$1" == 'log' ]]; then
    shift

    if ! "${IP6TABLES[@]}" "$@" \
           --match 'limit' --limit '1/second' \
           --jump 'LOG' --log-level 'info' --log-prefix "${log_info}"; then
      my::carp -e 'Error executing iptables'
    fi
  fi

  if ! "${IP6TABLES[@]}" "$@" \
         --match 'comment' --comment "${func_info}" \
         --jump "${target}"; then
    my::carp -e 'Error executing iptables'
  fi
}

#-------------------------------------------------------------------------------
# Reset (Library: iptables)
# -------------------------

it::reset() {
  echo -n 'IPv4: Reset policies & rules in table:'

  for table in 'raw' 'filter' 'nat'; do
    echo -n " ${table},"

    if ! "${IPTABLES[@]}" --table "${table}" --flush; then
      my::carp -e 'Error executing iptables'
    fi

    if ! "${IPTABLES[@]}" --table "${table}" --delete-chain; then
      my::carp -e 'Error executing iptables'
    fi
  done

  echo
  echo -n 'IPv6: Reset policies & rules in table:'

  for table in 'filter'; do
    echo -n " ${table},"

    if ! "${IP6TABLES[@]}" --table "${table}" --flush; then
      my::carp -e 'Error executing iptables'
    fi

    if ! "${IP6TABLES[@]}" --table "${table}" --delete-chain; then
      my::carp -e 'Error executing iptables'
    fi
  done

  echo
}

#-------------------------------------------------------------------------------
# Defaults (Library: iptables)
# ----------------------------

it::defaults() {
  echo -n 'IPv4: Set default policy & rules:'

  for chain in 'INPUT' 'OUTPUT'; do
    echo -n " ${chain},"

    # Set default policy to DROP
    if ! "${IPTABLES[@]}" --policy "${chain}" 'DROP'; then
      my::carp -e 'Error executing iptables'
    fi

    # Allow all known (ESTABLISHED or RELATED) connections
    it::iptables 'ACCEPT' \
      --append "${chain}" \
      --match 'conntrack' --ctstate 'ESTABLISHED,RELATED'
  done

  echo
  echo -n 'IPv6: Set default policy & rules:'

  for chain in 'INPUT' 'OUTPUT'; do
    echo -n " ${chain},"

    # Set default policy to DROP
    if ! "${IP6TABLES[@]}" --policy "${chain}" 'DROP'; then
      my::carp -e 'Error executing iptables'
    fi
  done

  echo
}

#-------------------------------------------------------------------------------
# Loopback (Library: iptables)
# ----------------------------

it::loopback() {
  echo 'IPv4: Set Loopback rules'

  it::iptables 'ACCEPT' \
    --append 'INPUT' \
    --in-interface "${IF_LO}" \
    --match 'conntrack' --ctstate 'NEW'

  it::iptables 'ACCEPT' \
    --append 'OUTPUT' \

  echo 'IPv6: Set Loopback rules'

  it::ip6tables 'ACCEPT' \
    --append 'INPUT' \
    --in-interface "${IF_LO}" \
    --match 'conntrack' --ctstate 'NEW'

  it::ip6tables 'ACCEPT' \
    --append 'OUTPUT' \
    --out-interface "${IF_LO}" \
    --match 'conntrack' --ctstate 'NEW'
}

#-------------------------------------------------------------------------------
# From LAN to Client (Library: iptables)
# --------------------------------------

it::lan2cl() {
  local tcp_ports='ssh'
  # local udp_ports=''

  local defaults=('--append' 'INPUT' \
                  '--in-interface' "${IF_LAN}" \
                  '--match' 'conntrack' '--ctstate' 'NEW')

  echo 'IPv4: Set LAN to Client rules:'

  echo '- Allow ICMP'
  it::iptables 'ACCEPT' "${defaults[@]}" \
    --protocol 'icmp'

  echo "- Allow TCP: ${tcp_ports}"
  it::iptables 'ACCEPT' \
    "${defaults[@]}" \
    --protocol 'tcp' --syn \
    --match 'multiport' --destination-ports "${tcp_ports}"

  # echo "- Allow UDP: ${udp_ports}"
  # it::iptables 'ACCEPT' "${defaults[@]}" \
  #   --protocol 'udp' \
  #   --match 'multiport' --destination-ports "${udp_ports}"

  echo '- Log & drop'
  it::iptables 'DROP' 'log' "${defaults[@]}"
}

#-------------------------------------------------------------------------------
# From Client to LAN (Library: iptables)
# --------------------------------------

it::cl2lan() {
  local tcp_ports='domain,http,https,rsync,submission'
  local udp_ports='bootpc,bootps,domain,ntp'

  local defaults=('--append' 'OUTPUT' \
                  '--out-interface' "${IF_LAN}" \
                  '--match' 'conntrack' '--ctstate' 'NEW')

  echo 'IPv4: Set Client to LAN rules:'

  echo '- Allow ICMP'
  it::iptables 'ACCEPT' "${defaults[@]}" \
    --protocol 'icmp'

  echo "- Allow TCP: ${tcp_ports}"
  it::iptables 'ACCEPT' "${defaults[@]}" \
    --protocol 'tcp' --syn \
    --match 'multiport' --destination-ports "${tcp_ports}"

  echo "- Allow UDP: ${udp_ports}"
  it::iptables 'ACCEPT' "${defaults[@]}" \
    --protocol 'udp' \
    --match 'multiport' --destination-ports "${udp_ports}"

  echo '- Log & drop'
  it::iptables 'DROP' 'log' "${defaults[@]}"
}

#-------------------------------------------------------------------------------
# List all (Library: iptables)
# ----------------------------

it::list_all() {
  for table in 'raw' 'filter' 'nat'; do
    echo
    echo "IPv4: Rules on table ${table}"

    if ! "${IPTABLES[@]}" --table "${table}" --verbose --line-numbers --list; then
      my::carp -e 'Error executing iptables'
    fi
  done

  for table in 'filter'; do
    echo
    echo "IPv6: Rules on table ${table}"

    if ! "${IP6TABLES[@]}" --table "${table}" --verbose --line-numbers --list; then
      my::carp -e 'Error executing iptables'
    fi
  done
}

#-------------------------------------------------------------------------------
# MAIN
# ----

# Test binaries

echo -n 'Main: Test if binaries are available:'

for bin in "${BINS[@]}"; do
  echo -n " ${bin},"

  if ! type -P "${bin}" >'/dev/null' 2>&1; then
    echo
    my_carp -e "Error finding binary: ${bin}"
  fi
done

echo

# Test kernel flags

echo -n 'IPv4: Test if kernel flags are set:'

for flag in 'ip_dynaddr'; do
  ipv4_flag="net.ipv4.${flag}"

  echo -n " ${ipv4_flag},"

  if [[ $("${SYSCTL[@]}" -n "${ipv4_flag}" 2>'/dev/null') -eq 0 ]]; then
    echo
    my::carp -e "Error finding kernel flag: ${ipv4_flag}"
  fi
done

echo

it::reset
it::defaults
it::loopback
it::lan2cl
it::cl2lan

it::list_all

#-------------------------------------------------------------------------------

exit 0

Usage