repositories / bugabinga.net
bugabinga.net
personal infrastructure for bugabinga!
owned by admin
scripts/remote/apply-wireguard-mtu.sh
Raw#!/usr/bin/env bash
set -euo pipefail
umask 077
[[ "$#" -eq 1 || ("$#" -eq 2 && "$2" = --check) ]] || {
echo 'usage: apply-wireguard-mtu.sh MTU [--check]' >&2
exit 2
}
mtu="$1"
[[ "$mtu" =~ ^[0-9]+$ && "$mtu" -ge 1280 && "$mtu" -le 1420 ]] || {
echo 'invalid WireGuard MTU' >&2
exit 2
}
check_only=false
[[ "${2:-}" = --check ]] && check_only=true
interface=wg0
config=/etc/wireguard/wg0.conf
fail() {
printf '%s\n' "$*" >&2
exit 1
}
if [[ "${WG_AUTO_MTU_TEST:-}" = 1 ]]; then
config="${WG_AUTO_MTU_CONFIG:-$config}"
else
[[ "$config" = /etc/wireguard/wg0.conf ]] || fail 'invalid WireGuard config path'
fi
[[ -f "$config" && ! -L "$config" ]] || fail 'invalid WireGuard config path'
validate_config() {
awk '
function clean(line) { sub(/[[:space:]]*[#;].*$/, "", line); return line }
/^[[:space:]]*\[/ {
section = tolower($0)
in_interface = (section ~ /^[[:space:]]*\[interface\][[:space:]]*$/)
if (in_interface) interfaces++
next
}
in_interface {
line = clean($0)
lower = tolower(line)
sub(/[[:space:]]+$/, "", lower)
if (lower ~ /^[[:space:]]*saveconfig[[:space:]]*=/) {
sub(/^[[:space:]]*saveconfig[[:space:]]*=[[:space:]]*/, "", lower)
if (lower == "true") save_config = 1
}
if (lower ~ /^[[:space:]]*mtu[[:space:]]*=/) {
mtus++
sub(/^[[:space:]]*mtu[[:space:]]*=[[:space:]]*/, "", lower)
line = lower
if (line !~ /^[0-9]+[[:space:]]*$/) bad_mtu = 1
}
}
END { exit !(interfaces == 1 && !save_config && mtus <= 1 && !bad_mtu) }
' "$config" || fail 'WireGuard config has invalid Interface, SaveConfig, or MTU directives'
}
link_info() {
ip -o link show dev "$interface" || fail 'WireGuard interface is absent'
}
parse_link() {
local link="$1"
ifindex="$(awk -F: '{gsub(/^[[:space:]]+/, "", $1); print $1}' <<<"$link")"
live_mtu="$(awk '{for (i = 1; i <= NF; i++) if ($i == "mtu") { print $(i + 1); exit }}' <<<"$link")"
[[ "$ifindex" =~ ^[0-9]+$ && "$live_mtu" =~ ^[0-9]+$ && "$link" == *'<'*'UP'*'>'* ]] || fail 'WireGuard interface is down or malformed'
}
render_config() {
awk -v mtu="$mtu" '
tolower($0) ~ /^[[:space:]]*\[interface\][[:space:]]*$/ {
print
in_interface = 1
next
}
/^[[:space:]]*\[/ {
if (in_interface && !replaced) print "MTU = " mtu
in_interface = 0
print
next
}
in_interface && $0 ~ /^[[:space:]]*[Mm][Tt][Uu][[:space:]]*=/ {
print "MTU = " mtu
replaced = 1
next
}
{ print }
END { if (in_interface && !replaced) print "MTU = " mtu }
' "$config"
}
validate_config
link_before="$(link_info)"
parse_link "$link_before"
ifindex_before="$ifindex"
old_mtu="$live_mtu"
if [[ "$check_only" = true ]]; then
printf '%s\n' "$mtu"
exit 0
fi
config_dir="$(dirname "$config")"
stage="$(mktemp "$config_dir/.wg0.conf.mtu.XXXXXX")"
cp -a -- "$config" "$stage"
render_config >"$stage.render"
cat "$stage.render" >"$stage"
rm -f -- "$stage.render"
if cmp -s "$config" "$stage" && [[ "$old_mtu" = "$mtu" ]]; then
rm -f -- "$stage"
printf '%s\n' "$mtu"
exit 0
fi
backup_dir="$(mktemp -d /var/tmp/WG-auto-mtu.XXXXXX)"
chmod 0700 "$backup_dir"
backup="$backup_dir/wg0.conf.before"
config_changed=false
mtu_change_attempted=false
cleanup() {
local status="$1"
if [[ "$status" -ne 0 ]]; then
if [[ "$mtu_change_attempted" = true ]]; then ip link set dev "$interface" mtu "$old_mtu" || true; fi
if [[ "$config_changed" = true ]]; then cp -a -- "$backup" "$config" || true; fi
printf 'Rollback evidence retained at %s\n' "$backup_dir" >&2
fi
rm -f -- "$stage"
exit "$status"
}
trap 'status=$?; cleanup "$status"' EXIT
cp -a -- "$config" "$backup"
chmod 0600 "$backup"
if ! cmp -s "$config" "$stage"; then
mv -f -- "$stage" "$config"
config_changed=true
fi
if [[ "$old_mtu" != "$mtu" ]]; then
mtu_change_attempted=true
ip link set dev "$interface" mtu "$mtu"
fi
link_after="$(link_info)"
parse_link "$link_after"
[[ "$ifindex" = "$ifindex_before" && "$live_mtu" = "$mtu" ]] || fail 'WireGuard interface identity, state, or MTU changed unexpectedly'
printf '%s %s\n' "$mtu" "$backup_dir"