initial commit
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# OS / editor
|
||||||
|
.DS_Store
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# Built artifacts
|
||||||
|
*.tar.gz
|
||||||
|
*.zip
|
||||||
15
README.fail2ban
Normal file
15
README.fail2ban
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# !! Wichtig!!
|
||||||
|
#
|
||||||
|
# In the file '/etc/fail2ban/jail.local', replace 'bannaction' with 'nftables-multiport':
|
||||||
|
#
|
||||||
|
# [DEFAULT]
|
||||||
|
# banaction = nftables-multiport
|
||||||
|
#
|
||||||
|
# and restart fail2ban service
|
||||||
|
#
|
||||||
|
# systemctl restart fail2ban
|
||||||
|
|
||||||
|
|
||||||
155
bin/fw-apply
Executable file
155
bin/fw-apply
Executable file
@@ -0,0 +1,155 @@
|
|||||||
|
#!/usr//bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
########################
|
||||||
|
# Args
|
||||||
|
########################
|
||||||
|
DO_APPLY=true
|
||||||
|
if [[ "${1:-}" == "--check" ]]; then
|
||||||
|
DO_APPLY=false
|
||||||
|
elif [[ "${1:-}" != "" ]]; then
|
||||||
|
echo "Usage: $0 [--check]" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
########################
|
||||||
|
# Defaults
|
||||||
|
########################
|
||||||
|
EXT_IF="eth0"
|
||||||
|
PRIV_IF="enp7s0"
|
||||||
|
PRIV_NET="172.20.0.0/21"
|
||||||
|
|
||||||
|
ALLOW_SSH_PUBLIC_IN=false
|
||||||
|
ALLOW_APT_PUBLIC_OUT=false
|
||||||
|
|
||||||
|
ALLOW_ICMP4_PUBLIC=true
|
||||||
|
ALLOW_ICMP6_PUBLIC=true
|
||||||
|
|
||||||
|
########################
|
||||||
|
# Optional config file
|
||||||
|
########################
|
||||||
|
CFG="/etc/default/nft-fw"
|
||||||
|
if [[ -r "$CFG" ]]; then
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$CFG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
########################
|
||||||
|
# Normalize booleans
|
||||||
|
########################
|
||||||
|
normalize_bool() {
|
||||||
|
# trim whitespace + remove CR (Windows line endings)
|
||||||
|
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_ICMP4_PUBLIC="$(normalize_bool "${ALLOW_ICMP4_PUBLIC:-true}")"
|
||||||
|
ALLOW_ICMP6_PUBLIC="$(normalize_bool "${ALLOW_ICMP6_PUBLIC:-true}")"
|
||||||
|
ALLOW_SSH_PUBLIC_IN="$(normalize_bool "${ALLOW_SSH_PUBLIC_IN:-false}")"
|
||||||
|
ALLOW_APT_PUBLIC_OUT="$(normalize_bool "${ALLOW_APT_PUBLIC_OUT:-false}")"
|
||||||
|
|
||||||
|
|
||||||
|
NEED_EXT=false
|
||||||
|
if [[ "$ALLOW_SSH_PUBLIC_IN" == "true" || "$ALLOW_APT_PUBLIC_OUT" == "true" || "$ALLOW_ICMP4_PUBLIC" == "true" || "$ALLOW_ICMP6_PUBLIC" == "true" ]]; then
|
||||||
|
NEED_EXT=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
EFFECTIVE_ALLOW_ICMP6_PUBLIC="$ALLOW_ICMP6_PUBLIC"
|
||||||
|
if [[ "$NEED_EXT" == "true" ]]; then
|
||||||
|
EFFECTIVE_ALLOW_ICMP6_PUBLIC=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
########################
|
||||||
|
# Build optional rule blocks
|
||||||
|
########################
|
||||||
|
ICMP_PUBLIC_IN_RULES=""
|
||||||
|
ICMP_PUBLIC_OUT_RULES=""
|
||||||
|
SSH_PUBLIC_IN_RULE=""
|
||||||
|
APT_PUBLIC_OUT_RULES=""
|
||||||
|
|
||||||
|
|
||||||
|
# ICMPv4 optional
|
||||||
|
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 optional (oder erzwungen über EFFECTIVE_ALLOW_ICMP6_PUBLIC)
|
||||||
|
if [[ "$EFFECTIVE_ALLOW_ICMP6_PUBLIC" == "true" ]]; then
|
||||||
|
# Falls schon was drin ist, füge sauber eine Leerzeile/Zeilenumbruch hinzu
|
||||||
|
[[ -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" ip6 nexthdr icmpv6 accept
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
ICMP_PUBLIC_OUT_RULES+=$(cat <<EOF
|
||||||
|
oif "$EXT_IF" ip6 nexthdr icmpv6 accept
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [[ "$ALLOW_SSH_PUBLIC_IN" == "true" ]]; then
|
||||||
|
SSH_PUBLIC_IN_RULE="$(cat <<EOF
|
||||||
|
iif "$EXT_IF" tcp dport 22 accept
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
export EXT_IF PRIV_IF PRIV_NET
|
||||||
|
export ICMP_PUBLIC_IN_RULES ICMP_PUBLIC_OUT_RULES
|
||||||
|
export SSH_PUBLIC_IN_RULE APT_PUBLIC_OUT_RULES
|
||||||
|
|
||||||
|
########################
|
||||||
|
# Render
|
||||||
|
########################
|
||||||
|
TMP="$(mktemp -p /run nft-fw.XXXXXX)"
|
||||||
|
trap 'rm -f "$TMP"' EXIT
|
||||||
|
|
||||||
|
envsubst < /etc/nftables.conf.in > "$TMP"
|
||||||
|
|
||||||
|
########################
|
||||||
|
# Remove only our table (do NOT touch fail2ban)
|
||||||
|
########################
|
||||||
|
nft delete table inet fw_static 2>/dev/null || true
|
||||||
|
|
||||||
|
########################
|
||||||
|
# Validate + apply
|
||||||
|
########################
|
||||||
|
nft -c -f "$TMP"
|
||||||
|
|
||||||
|
if [[ "$DO_APPLY" == "true" ]]; then
|
||||||
|
nft -f "$TMP"
|
||||||
|
install -m 600 "$TMP" /etc/nftables.conf
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo " fw-apply: check mode"
|
||||||
|
[[ -r "$CFG" ]] && echo " - config loaded: $CFG" || echo " - config loaded: defaults only"
|
||||||
|
echo " - rendered nftables ruleset: OK"
|
||||||
|
echo " - syntax check (nft -c): OK"
|
||||||
|
echo " - no changes applied"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
5
bin/fw-stop
Executable file
5
bin/fw-stop
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Enstferne
|
||||||
|
nft delete table inet fw_static 2>/dev/null || true
|
||||||
16
etc-default/nft-fw
Normal file
16
etc-default/nft-fw
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Öffentliches Interface
|
||||||
|
EXT_IF=eth0
|
||||||
|
|
||||||
|
# Privates Interface
|
||||||
|
PRIV_IF=enp7s0
|
||||||
|
|
||||||
|
# Privates Netz
|
||||||
|
PRIV_NET=172.20.0.0/21
|
||||||
|
|
||||||
|
# Public access toggles
|
||||||
|
ALLOW_SSH_PUBLIC_IN=true
|
||||||
|
ALLOW_APT_PUBLIC_OUT=true
|
||||||
|
|
||||||
|
# ICMP toggles (separat)
|
||||||
|
ALLOW_ICMP4_PUBLIC=true
|
||||||
|
ALLOW_ICMP6_PUBLIC=true
|
||||||
71
install.sh
Executable file
71
install.sh
Executable file
@@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
say(){ echo "[nft-fw-nd-priv] $*"; }
|
||||||
|
|
||||||
|
say "Creating directories..."
|
||||||
|
install -d -m 0755 /usr/local/sbin
|
||||||
|
|
||||||
|
say "Installing template..."
|
||||||
|
install -m 0644 "$REPO_DIR/templates/nftables.conf.in" /etc/nftables.conf.in
|
||||||
|
|
||||||
|
say "Installing scripts..."
|
||||||
|
install -m 0755 "$REPO_DIR/bin/fw-apply" /usr/local/sbin/fw-apply
|
||||||
|
install -m 0755 "$REPO_DIR/bin/fw-stop" /usr/local/sbin/fw-stop
|
||||||
|
|
||||||
|
|
||||||
|
say "Installing default config (won't overwrite existing)..."
|
||||||
|
if [[ ! -f /etc/default/nft-fw ]]; then
|
||||||
|
install -m 0644 "$REPO_DIR/etc-default/nft-fw-nd-priv" /etc/default/nft-fw
|
||||||
|
else
|
||||||
|
say "Config already exists at /etc/default/nft-fw (leaving as-is)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
say "Installing systemd unit..."
|
||||||
|
install -m 0644 "$REPO_DIR/systemd/nft-fw.service" /etc/systemd/system/nft-fw.service
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable nft-fw.service
|
||||||
|
|
||||||
|
say "Switching iptables binaries to nft backend (if available)..."
|
||||||
|
set_alt() {
|
||||||
|
local name="$1" target="$2"
|
||||||
|
if command -v update-alternatives >/dev/null 2>&1; then
|
||||||
|
if update-alternatives --list "$name" >/dev/null 2>&1; then
|
||||||
|
if update-alternatives --list "$name" | grep -qx "$target"; then
|
||||||
|
update-alternatives --set "$name" "$target" || true
|
||||||
|
say "Set alternative: $name -> $target"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Common paths on Debian/Ubuntu
|
||||||
|
set_alt iptables /usr/sbin/iptables-nft
|
||||||
|
set_alt ip6tables /usr/sbin/ip6tables-nft
|
||||||
|
set_alt arptables /usr/sbin/arptables-nft
|
||||||
|
set_alt ebtables /usr/sbin/ebtables-nft
|
||||||
|
|
||||||
|
say "Configuring fail2ban banaction for nftables (if installed)..."
|
||||||
|
if [[ -d /etc/fail2ban && -x /usr/bin/fail2ban-client ]]; then
|
||||||
|
install -d -m 0755 /etc/fail2ban/jail.d
|
||||||
|
cat > /etc/fail2ban/jail.d/nft-fw-nd-priv.local <<'JEOF'
|
||||||
|
[DEFAULT]
|
||||||
|
# Prefer nftables actions when the system uses nft backend
|
||||||
|
banaction = nftables-multiport
|
||||||
|
banaction_allports = nftables-allports
|
||||||
|
JEOF
|
||||||
|
say "Wrote /etc/fail2ban/jail.d/nft-fw-nd-priv.local"
|
||||||
|
systemctl restart fail2ban || true
|
||||||
|
else
|
||||||
|
say "fail2ban not found; skipping."
|
||||||
|
fi
|
||||||
|
|
||||||
|
say "Applying firewall now..."
|
||||||
|
/usr/local/sbin/fw-apply
|
||||||
|
|
||||||
|
say "Done. Edit /etc/default/nft-fw-nd-priv and re-run: fw-apply"
|
||||||
|
|
||||||
|
|
||||||
16
systemd/nft-fw.service
Normal file
16
systemd/nft-fw.service
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Apply nftables firewall (parameterized)
|
||||||
|
Documentation=man:nft(8)
|
||||||
|
After=local-fs.target
|
||||||
|
Wants=network-pre.target
|
||||||
|
Before=network-pre.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/sbin/fw-apply
|
||||||
|
ExecStop=/usr/local/sbin/fw-stop
|
||||||
|
RemainAfterExit=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
43
templates/nftables.conf.in
Normal file
43
templates/nftables.conf.in
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/sbin/nft -f
|
||||||
|
|
||||||
|
table inet fw_static {
|
||||||
|
|
||||||
|
chain input {
|
||||||
|
type filter hook input priority 0;
|
||||||
|
policy drop;
|
||||||
|
|
||||||
|
iif "lo" accept
|
||||||
|
ct state established,related accept
|
||||||
|
|
||||||
|
# Public: ICMP (optional)
|
||||||
|
$ICMP_PUBLIC_IN_RULES
|
||||||
|
|
||||||
|
# Public: SSH IN (optional)
|
||||||
|
$SSH_PUBLIC_IN_RULE
|
||||||
|
|
||||||
|
# Private network (in)
|
||||||
|
iif "$PRIV_IF" ip saddr $PRIV_NET accept
|
||||||
|
}
|
||||||
|
|
||||||
|
chain output {
|
||||||
|
type filter hook output priority 0;
|
||||||
|
policy drop;
|
||||||
|
|
||||||
|
oif "lo" accept
|
||||||
|
ct state established,related accept
|
||||||
|
|
||||||
|
# Public: ICMP (optional)
|
||||||
|
$ICMP_PUBLIC_OUT_RULES
|
||||||
|
|
||||||
|
# Public: APT OUT (optional) - includes DNS + HTTP/HTTPS
|
||||||
|
$APT_PUBLIC_OUT_RULES
|
||||||
|
|
||||||
|
# Private network (out)
|
||||||
|
oif "$PRIV_IF" ip daddr $PRIV_NET accept
|
||||||
|
}
|
||||||
|
|
||||||
|
chain forward {
|
||||||
|
type filter hook forward priority 0;
|
||||||
|
policy drop;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user