383 lines
13 KiB
YAML
383 lines
13 KiB
YAML
---
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: amneziawg-scripts
|
|
data:
|
|
firewall-up.sh: |
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
PORT="${1:-5847}"
|
|
VPN_CIDR="${2:-10.8.0.0/16}"
|
|
FORWARD_CHAIN="AMNEZIAWG-FORWARD"
|
|
IPTABLES=(iptables -w 30)
|
|
|
|
external_interface() {
|
|
ip route get 1.1.1.1 | awk '{for (i=1;i<=NF;i++) if ($i=="dev") {print $(i+1); exit}}'
|
|
}
|
|
|
|
ensure_rule() {
|
|
local mode="$1"
|
|
shift
|
|
local table_args=()
|
|
if [ "${1:-}" = "-t" ]; then
|
|
table_args=("$1" "$2")
|
|
shift 2
|
|
fi
|
|
|
|
local chain="$1"
|
|
shift
|
|
|
|
if ! "${IPTABLES[@]}" "${table_args[@]}" -C "${chain}" "$@" >/dev/null 2>&1; then
|
|
"${IPTABLES[@]}" "${table_args[@]}" "${mode}" "${chain}" "$@"
|
|
fi
|
|
}
|
|
|
|
delete_rule() {
|
|
local table_args=()
|
|
if [ "${1:-}" = "-t" ]; then
|
|
table_args=("$1" "$2")
|
|
shift 2
|
|
fi
|
|
|
|
local chain="$1"
|
|
shift
|
|
|
|
while "${IPTABLES[@]}" "${table_args[@]}" -D "${chain}" "$@" >/dev/null 2>&1; do
|
|
true
|
|
done
|
|
}
|
|
|
|
reset_chain() {
|
|
local chain="$1"
|
|
"${IPTABLES[@]}" -N "${chain}" >/dev/null 2>&1 || true
|
|
"${IPTABLES[@]}" -F "${chain}"
|
|
}
|
|
|
|
EXT_IF="$(external_interface || true)"
|
|
if [ -z "${EXT_IF}" ]; then
|
|
EXT_IF="$(ip route show default | awk '{print $5; exit}')"
|
|
fi
|
|
if [ -z "${EXT_IF}" ]; then
|
|
echo "Unable to detect external interface"
|
|
exit 1
|
|
fi
|
|
|
|
sysctl -w net.ipv4.ip_forward=1
|
|
|
|
# Remove rules written by older revisions. In particular, the unqualified
|
|
# tailscale UDP DROP also blocks Flannel VXLAN (UDP/8472).
|
|
delete_rule INPUT -i tailscale0 -p udp -m comment --comment amneziawg-block-tailscale -j DROP
|
|
delete_rule INPUT -i "${EXT_IF}" -p udp -m comment --comment amneziawg-allow-external -j ACCEPT
|
|
delete_rule INPUT -i "${EXT_IF}" -p udp --dport "${PORT}" -m comment --comment amneziawg-allow-external -j ACCEPT
|
|
delete_rule INPUT -i tailscale0 -p udp --dport "${PORT}" -m comment --comment amneziawg-block-tailscale -j DROP
|
|
delete_rule INPUT -i awg0 -m comment --comment amneziawg-awg-input -j ACCEPT
|
|
delete_rule FORWARD -i awg0 -m comment --comment amneziawg-forward-in -j ACCEPT
|
|
delete_rule FORWARD -o awg0 -m comment --comment amneziawg-forward-out -j ACCEPT
|
|
delete_rule INPUT -m comment --comment amneziawg-input-jump -j AMNEZIAWG-INPUT
|
|
"${IPTABLES[@]}" -F AMNEZIAWG-INPUT >/dev/null 2>&1 || true
|
|
"${IPTABLES[@]}" -X AMNEZIAWG-INPUT >/dev/null 2>&1 || true
|
|
|
|
reset_chain "${FORWARD_CHAIN}"
|
|
"${IPTABLES[@]}" -A "${FORWARD_CHAIN}" -i awg0 -m comment --comment amneziawg-forward-in -j ACCEPT
|
|
"${IPTABLES[@]}" -A "${FORWARD_CHAIN}" -o awg0 -m comment --comment amneziawg-forward-out -j ACCEPT
|
|
"${IPTABLES[@]}" -A "${FORWARD_CHAIN}" -j RETURN
|
|
ensure_rule -A FORWARD -m comment --comment amneziawg-forward-jump -j "${FORWARD_CHAIN}"
|
|
|
|
ensure_rule -A -t nat POSTROUTING -s "${VPN_CIDR}" -o "${EXT_IF}" -m comment --comment amneziawg-masquerade -j MASQUERADE
|
|
|
|
/scripts/firewall-check.sh "${PORT}" "${VPN_CIDR}"
|
|
|
|
firewall-down.sh: |
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
PORT="${1:-5847}"
|
|
VPN_CIDR="${2:-10.8.0.0/16}"
|
|
FORWARD_CHAIN="AMNEZIAWG-FORWARD"
|
|
IPTABLES=(iptables -w 30)
|
|
|
|
external_interface() {
|
|
ip route get 1.1.1.1 | awk '{for (i=1;i<=NF;i++) if ($i=="dev") {print $(i+1); exit}}'
|
|
}
|
|
|
|
delete_rule() {
|
|
local table_args=()
|
|
if [ "${1:-}" = "-t" ]; then
|
|
table_args=("$1" "$2")
|
|
shift 2
|
|
fi
|
|
|
|
local chain="$1"
|
|
shift
|
|
|
|
while "${IPTABLES[@]}" "${table_args[@]}" -D "${chain}" "$@" >/dev/null 2>&1; do
|
|
true
|
|
done
|
|
}
|
|
|
|
EXT_IF="$(external_interface || true)"
|
|
if [ -z "${EXT_IF}" ]; then
|
|
EXT_IF="$(ip route show default | awk '{print $5; exit}')"
|
|
fi
|
|
|
|
if [ -n "${EXT_IF}" ]; then
|
|
delete_rule INPUT -i "${EXT_IF}" -p udp -m comment --comment amneziawg-allow-external -j ACCEPT
|
|
delete_rule INPUT -i "${EXT_IF}" -p udp --dport "${PORT}" -m comment --comment amneziawg-allow-external -j ACCEPT
|
|
delete_rule -t nat POSTROUTING -s "${VPN_CIDR}" -o "${EXT_IF}" -m comment --comment amneziawg-masquerade -j MASQUERADE
|
|
fi
|
|
|
|
delete_rule INPUT -i tailscale0 -p udp --dport "${PORT}" -m comment --comment amneziawg-block-tailscale -j DROP
|
|
delete_rule INPUT -i tailscale0 -p udp -m comment --comment amneziawg-block-tailscale -j DROP
|
|
delete_rule INPUT -i awg0 -m comment --comment amneziawg-awg-input -j ACCEPT
|
|
delete_rule FORWARD -i awg0 -m comment --comment amneziawg-forward-in -j ACCEPT
|
|
delete_rule FORWARD -o awg0 -m comment --comment amneziawg-forward-out -j ACCEPT
|
|
delete_rule INPUT -m comment --comment amneziawg-input-jump -j AMNEZIAWG-INPUT
|
|
delete_rule FORWARD -m comment --comment amneziawg-forward-jump -j "${FORWARD_CHAIN}"
|
|
|
|
"${IPTABLES[@]}" -F AMNEZIAWG-INPUT >/dev/null 2>&1 || true
|
|
"${IPTABLES[@]}" -X AMNEZIAWG-INPUT >/dev/null 2>&1 || true
|
|
"${IPTABLES[@]}" -F "${FORWARD_CHAIN}" >/dev/null 2>&1 || true
|
|
"${IPTABLES[@]}" -X "${FORWARD_CHAIN}" >/dev/null 2>&1 || true
|
|
|
|
firewall-check.sh: |
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
PORT="${1:-5847}"
|
|
VPN_CIDR="${2:-10.8.0.0/16}"
|
|
FORWARD_CHAIN="AMNEZIAWG-FORWARD"
|
|
IPTABLES=(iptables -w 5)
|
|
|
|
external_interface() {
|
|
ip route get 1.1.1.1 | awk '{for (i=1;i<=NF;i++) if ($i=="dev") {print $(i+1); exit}}'
|
|
}
|
|
|
|
EXT_IF="$(external_interface || true)"
|
|
if [ -z "${EXT_IF}" ]; then
|
|
EXT_IF="$(ip route show default | awk '{print $5; exit}')"
|
|
fi
|
|
if [ -z "${EXT_IF}" ]; then
|
|
echo "Unable to detect external interface" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if "${IPTABLES[@]}" -C INPUT -i tailscale0 -p udp -m comment --comment amneziawg-block-tailscale -j DROP >/dev/null 2>&1; then
|
|
echo "Unsafe broad UDP DROP on tailscale0 is present" >&2
|
|
exit 1
|
|
fi
|
|
if "${IPTABLES[@]}" -C INPUT -i "${EXT_IF}" -p udp -m comment --comment amneziawg-allow-external -j ACCEPT >/dev/null 2>&1; then
|
|
echo "Unsafe broad UDP ACCEPT on ${EXT_IF} is present" >&2
|
|
exit 1
|
|
fi
|
|
if "${IPTABLES[@]}" -C INPUT -m comment --comment amneziawg-input-jump -j AMNEZIAWG-INPUT >/dev/null 2>&1; then
|
|
echo "Unexpected AmneziaWG INPUT chain is present" >&2
|
|
exit 1
|
|
fi
|
|
|
|
"${IPTABLES[@]}" -C FORWARD -m comment --comment amneziawg-forward-jump -j "${FORWARD_CHAIN}"
|
|
"${IPTABLES[@]}" -C "${FORWARD_CHAIN}" -i awg0 -m comment --comment amneziawg-forward-in -j ACCEPT
|
|
"${IPTABLES[@]}" -C "${FORWARD_CHAIN}" -o awg0 -m comment --comment amneziawg-forward-out -j ACCEPT
|
|
|
|
"${IPTABLES[@]}" -t nat -C POSTROUTING -s "${VPN_CIDR}" -o "${EXT_IF}" -m comment --comment amneziawg-masquerade -j MASQUERADE
|
|
awg show awg0 >/dev/null 2>&1
|
|
|
|
run.sh: |
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SERVER_CONFIG="/etc/amnezia/server/awg0.conf"
|
|
CLIENTS_DIR="${AMNEZIAWG_CLIENTS_DIR:-/run/amnezia/clients}"
|
|
RUNTIME_CONFIG="/run/amnezia/awg0.conf"
|
|
SYNC_CONFIG="/run/amnezia/awg0.sync.conf"
|
|
STATUS_FILE="/run/amnezia/reload-status"
|
|
RELOAD_INTERVAL="${AMNEZIAWG_RELOAD_INTERVAL:-10}"
|
|
|
|
cleanup() {
|
|
if awg show awg0 >/dev/null 2>&1; then
|
|
awg-quick down "${RUNTIME_CONFIG}" || ip link delete awg0 || true
|
|
fi
|
|
}
|
|
|
|
render_config() {
|
|
mkdir -p "$(dirname "${RUNTIME_CONFIG}")"
|
|
local tmp_config="${RUNTIME_CONFIG}.tmp"
|
|
cp "${SERVER_CONFIG}" "${tmp_config}"
|
|
chmod 0600 "${tmp_config}"
|
|
|
|
local clients_found=0
|
|
for client_config in "${CLIENTS_DIR}"/*; do
|
|
[ -f "${client_config}" ] || continue
|
|
[ -s "${client_config}" ] || continue
|
|
printf '\n' >> "${tmp_config}"
|
|
cat "${client_config}" >> "${tmp_config}"
|
|
clients_found=1
|
|
done
|
|
|
|
if [ "${clients_found}" = "0" ]; then
|
|
echo "No client peer configs found in ${CLIENTS_DIR}; starting without peers"
|
|
fi
|
|
|
|
mv "${tmp_config}" "${RUNTIME_CONFIG}"
|
|
chmod 0600 "${RUNTIME_CONFIG}"
|
|
}
|
|
|
|
client_config_hash() {
|
|
{
|
|
for client_config in "${CLIENTS_DIR}"/*; do
|
|
[ -f "${client_config}" ] || continue
|
|
sha256sum "${client_config}"
|
|
done
|
|
} | sha256sum | awk '{print $1}'
|
|
}
|
|
|
|
write_reload_status() {
|
|
local state="${1}"
|
|
local hash="${2:-}"
|
|
local applied_at_ms=""
|
|
if [ "${state}" = "applied" ]; then
|
|
applied_at_ms="$(($(date +%s) * 1000))"
|
|
fi
|
|
|
|
mkdir -p "$(dirname "${STATUS_FILE}")"
|
|
{
|
|
printf 'state=%s\n' "${state}"
|
|
printf 'hash=%s\n' "${hash}"
|
|
printf 'applied_at_ms=%s\n' "${applied_at_ms}"
|
|
} > "${STATUS_FILE}.tmp"
|
|
mv "${STATUS_FILE}.tmp" "${STATUS_FILE}"
|
|
}
|
|
|
|
apply_live_config() {
|
|
render_config
|
|
awg-quick strip "${RUNTIME_CONFIG}" > "${SYNC_CONFIG}"
|
|
chmod 0600 "${SYNC_CONFIG}"
|
|
awg syncconf awg0 "${SYNC_CONFIG}"
|
|
}
|
|
|
|
watch_client_config() {
|
|
local last_hash="${1}"
|
|
while true; do
|
|
sleep "${RELOAD_INTERVAL}" &
|
|
wait "$!" || return 0
|
|
|
|
local current_hash
|
|
current_hash="$(client_config_hash)"
|
|
if [ "${current_hash}" = "${last_hash}" ]; then
|
|
continue
|
|
fi
|
|
|
|
echo "Detected AmneziaWG client peer config change; applying with awg syncconf"
|
|
if apply_live_config; then
|
|
last_hash="${current_hash}"
|
|
write_reload_status applied "${current_hash}"
|
|
awg show awg0 || true
|
|
else
|
|
echo "ERROR: failed to hot-reload AmneziaWG client peer config" >&2
|
|
write_reload_status error "${current_hash}"
|
|
fi
|
|
done
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
trap 'exit 0' TERM INT
|
|
|
|
initial_hash="$(client_config_hash)"
|
|
render_config
|
|
cleanup
|
|
awg-quick up "${RUNTIME_CONFIG}"
|
|
awg show awg0 || true
|
|
write_reload_status applied "${initial_hash}"
|
|
watch_client_config "${initial_hash}"
|
|
|
|
client-secret-sync.sh: |
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
CLIENT_SECRET="${AMNEZIAWG_CLIENT_SECRET:-amneziawg-clients}"
|
|
CLIENT_SECRET_KEY="${AMNEZIAWG_CLIENT_SECRET_KEY:-peers.conf}"
|
|
CLIENTS_DIR="${AMNEZIAWG_CLIENTS_DIR:-/run/amnezia/clients}"
|
|
PEERS_FILE="${CLIENTS_DIR}/peers.conf"
|
|
SYNC_INTERVAL="${AMNEZIAWG_CLIENT_SECRET_SYNC_INTERVAL:-5}"
|
|
NAMESPACE="${POD_NAMESPACE:-$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)}"
|
|
|
|
write_empty_once() {
|
|
mkdir -p "${CLIENTS_DIR}"
|
|
if [ ! -f "${PEERS_FILE}" ]; then
|
|
: > "${PEERS_FILE}"
|
|
chmod 0600 "${PEERS_FILE}"
|
|
fi
|
|
}
|
|
|
|
sync_once() {
|
|
mkdir -p "${CLIENTS_DIR}"
|
|
local tmp_file="${PEERS_FILE}.tmp"
|
|
local encoded=""
|
|
|
|
if ! encoded="$(kubectl get secret "${CLIENT_SECRET}" -n "${NAMESPACE}" -o "go-template={{ index .data \"${CLIENT_SECRET_KEY}\" }}" 2>/dev/null)"; then
|
|
echo "WARN: failed to read Secret ${NAMESPACE}/${CLIENT_SECRET}; keeping current peers" >&2
|
|
write_empty_once
|
|
return 0
|
|
fi
|
|
|
|
if [ -n "${encoded}" ]; then
|
|
printf '%s' "${encoded}" | base64 -d > "${tmp_file}"
|
|
else
|
|
: > "${tmp_file}"
|
|
fi
|
|
chmod 0600 "${tmp_file}"
|
|
|
|
if [ -f "${PEERS_FILE}" ] && cmp -s "${tmp_file}" "${PEERS_FILE}"; then
|
|
rm -f "${tmp_file}"
|
|
return 0
|
|
fi
|
|
|
|
mv "${tmp_file}" "${PEERS_FILE}"
|
|
echo "Synced AmneziaWG client peers from Secret ${NAMESPACE}/${CLIENT_SECRET}:${CLIENT_SECRET_KEY}"
|
|
}
|
|
|
|
if [ "${1:-}" = "once" ]; then
|
|
sync_once
|
|
exit 0
|
|
fi
|
|
|
|
while true; do
|
|
sync_once || true
|
|
sleep "${SYNC_INTERVAL}"
|
|
done
|
|
|
|
status-patch.sh: |
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
STATUS_FILE="/run/amnezia/reload-status"
|
|
PATCH_INTERVAL="${AMNEZIAWG_STATUS_PATCH_INTERVAL:-5}"
|
|
NAMESPACE="${POD_NAMESPACE:-$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)}"
|
|
: "${POD_NAME:?POD_NAME is required}"
|
|
|
|
last_file_hash=""
|
|
|
|
patch_status() {
|
|
local state="unknown"
|
|
local hash=""
|
|
local applied_at_ms=""
|
|
|
|
# The file is generated by run.sh and contains only shell assignments.
|
|
# shellcheck disable=SC1090
|
|
source "${STATUS_FILE}"
|
|
|
|
kubectl patch pod "${POD_NAME}" -n "${NAMESPACE}" --type merge -p "{\"metadata\":{\"annotations\":{\"amnezia-fellow.hexor.cy/client-secret-reload-status\":\"${state}\",\"amnezia-fellow.hexor.cy/client-secret-applied-at-ms\":\"${applied_at_ms}\",\"amnezia-fellow.hexor.cy/client-secret-applied-hash\":\"${hash}\"}}}"
|
|
}
|
|
|
|
while true; do
|
|
if [ -f "${STATUS_FILE}" ]; then
|
|
file_hash="$(sha256sum "${STATUS_FILE}" | awk '{print $1}')"
|
|
if [ "${file_hash}" != "${last_file_hash}" ]; then
|
|
patch_status || true
|
|
last_file_hash="${file_hash}"
|
|
fi
|
|
fi
|
|
sleep "${PATCH_INTERVAL}"
|
|
done
|