Redesign of the nft firewall based on an existing Ansible playbook for the same purpose.
This commit is contained in:
193
sbin/fw-apply
Executable file
193
sbin/fw-apply
Executable file
@@ -0,0 +1,193 @@
|
||||
#!/bin/bash
|
||||
# *** [ Ansible managed file: DO NOT EDIT DIRECTLY ] ***
|
||||
|
||||
#
|
||||
# fw-apply
|
||||
#
|
||||
# Generic firewall loader (Ansible-independent):
|
||||
# - Reads config from /etc/nftables.conf.d/nft-fw.conf (optional)
|
||||
# - Renders /etc/nftables.conf.in using envsubst
|
||||
# - Validates with: nft -c -f <rendered>
|
||||
# - Applies by deleting ONLY table inet fw_static and loading the new definition
|
||||
# (keeps fail2ban tables intact)
|
||||
#
|
||||
# Usage:
|
||||
# fw-apply -> validate + apply
|
||||
# fw-apply --check -> validate only (no changes)
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
############################
|
||||
# Argument handling
|
||||
############################
|
||||
DO_APPLY=true
|
||||
if [[ "${1:-}" == "--check" ]]; then
|
||||
DO_APPLY=false
|
||||
elif [[ "${1:-}" != "" ]]; then
|
||||
echo "Usage: $0 [--check]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
############################
|
||||
# Config location
|
||||
############################
|
||||
CFG_DIR="/etc/nftables.conf.d"
|
||||
CFG_FILE="$CFG_DIR/nft-fw.conf"
|
||||
|
||||
############################
|
||||
# Defaults (used if config missing/partial)
|
||||
############################
|
||||
EXT_IF="eth0"
|
||||
PRIV_IF="enp7s0"
|
||||
PRIV_NET="172.20.0.0/21"
|
||||
|
||||
ALLOW_SSH_PUBLIC_IN="true"
|
||||
ALLOW_APT_PUBLIC_OUT="true"
|
||||
|
||||
ALLOW_ICMP4_PUBLIC="true"
|
||||
ALLOW_ICMP6_PUBLIC="true"
|
||||
FORCE_ICMP6_ESSENTIAL="true"
|
||||
|
||||
############################
|
||||
# Load config if present
|
||||
############################
|
||||
if [[ -r "$CFG_FILE" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$CFG_FILE"
|
||||
fi
|
||||
|
||||
############################
|
||||
# Helpers
|
||||
############################
|
||||
normalize_bool() {
|
||||
# Robust against CRLF and leading/trailing whitespace.
|
||||
local v
|
||||
v="$(printf '%s' "${1:-}" | tr -d '\r' | xargs)"
|
||||
case "${v,,}" in
|
||||
true|yes|1) echo true ;;
|
||||
false|no|0|"") echo false ;;
|
||||
*) echo false ;;
|
||||
esac
|
||||
}
|
||||
|
||||
ALLOW_SSH_PUBLIC_IN="$(normalize_bool "$ALLOW_SSH_PUBLIC_IN")"
|
||||
ALLOW_APT_PUBLIC_OUT="$(normalize_bool "$ALLOW_APT_PUBLIC_OUT")"
|
||||
ALLOW_ICMP4_PUBLIC="$(normalize_bool "$ALLOW_ICMP4_PUBLIC")"
|
||||
ALLOW_ICMP6_PUBLIC="$(normalize_bool "$ALLOW_ICMP6_PUBLIC")"
|
||||
FORCE_ICMP6_ESSENTIAL="$(normalize_bool "$FORCE_ICMP6_ESSENTIAL")"
|
||||
|
||||
############################
|
||||
# Determine external usage
|
||||
############################
|
||||
# EXT_IF is considered "in use" if any externally-relevant feature is enabled.
|
||||
NEED_EXT=false
|
||||
if [[ "$ALLOW_SSH_PUBLIC_IN" == "true" || "$ALLOW_APT_PUBLIC_OUT" == "true" ]]; then
|
||||
NEED_EXT=true
|
||||
fi
|
||||
|
||||
############################
|
||||
# Build envsubst blocks (must contain REAL newlines)
|
||||
############################
|
||||
SSH_PUBLIC_IN_RULE=""
|
||||
APT_PUBLIC_OUT_RULES=""
|
||||
ICMP_PUBLIC_IN_RULES=""
|
||||
ICMP_PUBLIC_OUT_RULES=""
|
||||
|
||||
# SSH IN (public)
|
||||
if [[ "$ALLOW_SSH_PUBLIC_IN" == "true" ]]; then
|
||||
SSH_PUBLIC_IN_RULE="$(cat <<EOF
|
||||
iif "$EXT_IF" tcp dport 22 accept
|
||||
EOF
|
||||
)"
|
||||
fi
|
||||
|
||||
# APT OUT (public) - DNS + HTTP/HTTPS
|
||||
if [[ "$ALLOW_APT_PUBLIC_OUT" == "true" ]]; then
|
||||
APT_PUBLIC_OUT_RULES="$(cat <<EOF
|
||||
oif "$EXT_IF" udp dport 53 accept
|
||||
oif "$EXT_IF" tcp dport 53 accept
|
||||
oif "$EXT_IF" tcp dport { 80, 443 } accept
|
||||
EOF
|
||||
)"
|
||||
fi
|
||||
|
||||
# ICMPv4 echo
|
||||
if [[ "$ALLOW_ICMP4_PUBLIC" == "true" ]]; then
|
||||
ICMP_PUBLIC_IN_RULES="$(cat <<EOF
|
||||
iif "$EXT_IF" ip protocol icmp accept
|
||||
EOF
|
||||
)"
|
||||
ICMP_PUBLIC_OUT_RULES="$(cat <<EOF
|
||||
oif "$EXT_IF" ip protocol icmp accept
|
||||
EOF
|
||||
)"
|
||||
fi
|
||||
|
||||
# ICMPv6 essential (no echo) if EXT used and forcing enabled
|
||||
ALLOW_ICMP6_ESSENTIAL=false
|
||||
if [[ "$FORCE_ICMP6_ESSENTIAL" == "true" && "$NEED_EXT" == "true" ]]; then
|
||||
ALLOW_ICMP6_ESSENTIAL=true
|
||||
fi
|
||||
|
||||
if [[ "$ALLOW_ICMP6_ESSENTIAL" == "true" ]]; then
|
||||
[[ -n "$ICMP_PUBLIC_IN_RULES" ]] && ICMP_PUBLIC_IN_RULES+=$'\n'
|
||||
[[ -n "$ICMP_PUBLIC_OUT_RULES" ]] && ICMP_PUBLIC_OUT_RULES+=$'\n'
|
||||
|
||||
ICMP_PUBLIC_IN_RULES+=$(cat <<EOF
|
||||
iif "$EXT_IF" icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect, packet-too-big, time-exceeded, parameter-problem, destination-unreachable } accept
|
||||
EOF
|
||||
)
|
||||
ICMP_PUBLIC_OUT_RULES+=$(cat <<EOF
|
||||
oif "$EXT_IF" icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect, packet-too-big, time-exceeded, parameter-problem, destination-unreachable } accept
|
||||
EOF
|
||||
)
|
||||
fi
|
||||
|
||||
# ICMPv6 echo (ping6)
|
||||
if [[ "$ALLOW_ICMP6_PUBLIC" == "true" ]]; then
|
||||
[[ -n "$ICMP_PUBLIC_IN_RULES" ]] && ICMP_PUBLIC_IN_RULES+=$'\n'
|
||||
[[ -n "$ICMP_PUBLIC_OUT_RULES" ]] && ICMP_PUBLIC_OUT_RULES+=$'\n'
|
||||
|
||||
ICMP_PUBLIC_IN_RULES+=$(cat <<EOF
|
||||
iif "$EXT_IF" icmpv6 type { echo-request, echo-reply } accept
|
||||
EOF
|
||||
)
|
||||
ICMP_PUBLIC_OUT_RULES+=$(cat <<EOF
|
||||
oif "$EXT_IF" icmpv6 type { echo-request, echo-reply } accept
|
||||
EOF
|
||||
)
|
||||
fi
|
||||
|
||||
############################
|
||||
# Export for envsubst
|
||||
############################
|
||||
export EXT_IF PRIV_IF PRIV_NET
|
||||
export SSH_PUBLIC_IN_RULE APT_PUBLIC_OUT_RULES
|
||||
export ICMP_PUBLIC_IN_RULES ICMP_PUBLIC_OUT_RULES
|
||||
|
||||
############################
|
||||
# Render + validate + apply
|
||||
############################
|
||||
TMP="$(mktemp -p /run nft-fw.XXXXXX)"
|
||||
trap 'rm -f "$TMP"' EXIT
|
||||
|
||||
envsubst < /etc/nftables.conf.in > "$TMP"
|
||||
|
||||
# Replace only our table (keep fail2ban intact)
|
||||
nft delete table inet fw_static 2>/dev/null || true
|
||||
|
||||
# Validate syntax
|
||||
nft -c -f "$TMP"
|
||||
|
||||
if [[ "$DO_APPLY" == "true" ]]; then
|
||||
nft -f "$TMP"
|
||||
install -m 600 "$TMP" /etc/nftables.conf
|
||||
else
|
||||
echo "fw-apply: check mode"
|
||||
[[ -r "$CFG_FILE" ]] && echo "- config loaded: $CFG_FILE" || echo "- config loaded: defaults only"
|
||||
echo "- rendered nftables ruleset: OK"
|
||||
echo "- syntax check (nft -c): OK"
|
||||
echo "- no changes applied"
|
||||
fi
|
||||
|
||||
8
sbin/fw-stop
Executable file
8
sbin/fw-stop
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# *** [ Ansible managed file: DO NOT EDIT DIRECTLY ] ***
|
||||
|
||||
|
||||
nft delete table inet fw_static 2>/dev/null || true
|
||||
|
||||
Reference in New Issue
Block a user