#!/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