Add script to block koji for external user at the time of branching #12877

Merged
jnsamyak merged 1 commit from main into main 2025-08-12 10:42:10 +00:00

View file

@ -0,0 +1,165 @@
#!/usr/bin/env bash
# =============================================================================
# Koji HTTP Toggle (nftables)
# -----------------------------------------------------------------------------
# Purpose:
# Quickly BLOCK or UNBLOCK external HTTP submissions to Koji by dropping
# TCP/80 from specified proxies (proxy01/proxy10 by default) using nftables.
#
# What it does:
# - Resolves hostnames to IPv4 addresses (via `getent ahostsv4`)
# - Ensures `table inet filter` and `chain input` exist
# - Creates a named set `koji_http_block_src` and a rule that drops
# tcp dport 80 if ip saddr is in that set
# - Adds/removes IPs to/from the set (idempotent)
#
# Usage:
# sudo /usr/local/sbin/koji-http-toggle.sh block # block proxy01/proxy10
# sudo /usr/local/sbin/koji-http-toggle.sh status # show current state
# sudo /usr/local/sbin/koji-http-toggle.sh unblock # remove block
#
# Install:
# sudo install -m 0755 koji-http-toggle.sh /usr/local/sbin/koji-http-toggle.sh
#
# Env overrides (optional):
# PORT=<port> default: 80
# PROXY01=<hostname> default: proxy01.rdu3.fedoraproject.org
# PROXY10=<hostname> default: proxy10.rdu3.fedoraproject.org
#
# Examples:
# sudo PROXY01=proxy01.example.org PROXY10=proxy10.example.org \
# /usr/local/sbin/koji-http-toggle.sh block
#
# Notes:
# - IPv4 only (adjust type to ip6_addr + `getent ahostsv6` if you need IPv6).
# - If firewalld manages your host, prefer firewalld rich rules instead.
# - To persist direct-nft config across reboot:
# sudo sh -c 'nft list ruleset > /etc/nftables.conf'
# sudo systemctl enable --now nftables
#
# Troubleshooting:
# - "syntax error near `}'" → quote the chain creation command (we do).
# - "No such file or directory" → table/chain likely missing; the script
# creates them automatically.
# =============================================================================
set -euo pipefail
# Defaults (override with env vars if needed)
PORT="${PORT:-80}"
HOSTS=(
"${PROXY01:-proxy01.rdu3.fedoraproject.org}"
"${PROXY10:-proxy10.rdu3.fedoraproject.org}"
)
TABLE="inet filter" # nft table and family
CHAIN="input" # nft chain to hook into
SET="koji_http_block_src" # named set of source IPs to block
COMMENT="koji-http-block" # helpful tag for future auditing
# Resolve each hostname to a single IPv4 address (first match)
resolve_ips() {
local ip
for h in "${HOSTS[@]}"; do
ip="$(getent ahostsv4 "$h" | awk '{print $1; exit}')" || true
if [[ -z "${ip:-}" ]]; then
echo "ERROR: could not resolve IPv4 for $h" >&2
exit 1
fi
echo "$ip"
done
}
# Ensure table and chain exist; chain hooks into input with default accept
ensure_table_chain() {
if ! nft list table $TABLE >/dev/null 2>&1; then
nft add table $TABLE
fi
if ! nft list chain $TABLE $CHAIN >/dev/null 2>&1; then
nft "add chain $TABLE $CHAIN { type filter hook input priority 0; policy accept; }"
fi
}
# Ensure the named set exists to hold source IPs
ensure_set() {
if ! nft list set $TABLE $SET >/dev/null 2>&1; then
nft "add set $TABLE $SET { type ipv4_addr; comment \"$COMMENT\"; }"
fi
}
# Ensure a single drop rule exists that references the named set
ensure_rule() {
if ! nft list chain $TABLE $CHAIN | grep -q "tcp dport $PORT .* @$SET .* drop"; then
nft add rule $TABLE $CHAIN tcp dport $PORT ip saddr @$SET drop comment \"$COMMENT\"
fi
}
# Add the proxies' IPs into the set (safe to run multiple times)
block() {
ensure_table_chain
ensure_set
ensure_rule
local ips=()
mapfile -t ips < <(resolve_ips)
# Build "{ ip1,ip2,... }" for nft add element
local elems
elems="$(printf "{ %s }" "$(IFS=,; echo "${ips[*]}")")"
# Add elements; ignore if already present
nft add element $TABLE $SET "$elems" 2>/dev/null || true
echo "Blocked HTTP (tcp/$PORT) from: ${ips[*]}"
}
# Remove all IPs from the set and delete the drop rule
unblock() {
# Flush the set if it exists
if nft list set $TABLE $SET >/dev/null 2>&1; then
nft flush set $TABLE $SET
fi
# Remove the rule by handle (safer than pattern-delete)
if nft list chain $TABLE $CHAIN | grep -q "tcp dport $PORT .* @$SET .* drop"; then
local handle
handle="$(nft -a list chain $TABLE $CHAIN | awk '/tcp dport '"$PORT"'.* @'"$SET"'.* drop/ {print $NF}')"
if [[ -n "${handle:-}" ]]; then
nft delete rule $TABLE $CHAIN handle "$handle"
fi
fi
echo "Unblocked. (Set cleared and rule removed if present.)"
}
# Show current table, chain, and set contents
status() {
echo "=== nft: $TABLE ==="
nft list table $TABLE 2>/dev/null || { echo "(no $TABLE)"; return; }
echo
echo "=== Elements in @$SET ==="
nft list set $TABLE $SET 2>/dev/null || echo "(no set $SET)"
}
usage() {
cat <<EOF
Usage: $0 {block|unblock|status}
Env overrides:
PORT=<port> (default: 80)
PROXY01=<hostname> (default: proxy01.rdu3.fedoraproject.org)
PROXY10=<hostname> (default: proxy10.rdu3.fedoraproject.org)
Examples:
sudo $0 block
sudo $0 status
sudo $0 unblock
EOF
}
case "${1:-}" in
block) block ;;
unblock) unblock ;;
status) status ;;
*) usage; exit 1 ;;
esac