Add per-VM NAT and DNS config

This commit is contained in:
Thales Maciel 2026-01-29 16:22:28 -03:00
parent 60b1865ece
commit 68cf5f2cbb
No known key found for this signature in database
GPG key ID: 33112E6833C34679
3 changed files with 165 additions and 4 deletions

130
nat.sh Executable file
View file

@ -0,0 +1,130 @@
#!/usr/bin/env bash
set -euo pipefail
log() {
printf '[nat] %s\n' "$*"
}
usage() {
cat <<'EOF'
Usage: ./nat.sh <up|down|status> <id-or-name-prefix>
Manage per-VM NAT rules for internet access.
EOF
}
get_prop() {
local info="$1"
local key="$2"
awk -F= -v k="$key" '$1==k {print $2}' "$info"
}
find_vm_info() {
local query="$1"
local info match_count=0 match=""
for info in state/vms/*/info; do
[[ -f "$info" ]] || continue
local id name
id="$(get_prop "$info" "id")"
name="$(get_prop "$info" "name")"
if [[ "$id" == "$query"* || "$name" == "$query"* ]]; then
match="$info"
match_count=$((match_count + 1))
fi
done
if (( match_count == 0 )); then
log "no VM found for prefix: $query"
exit 1
fi
if (( match_count > 1 )); then
log "multiple VMs found for prefix: $query"
exit 1
fi
printf '%s' "$match"
}
default_uplink() {
ip route show default 2>/dev/null | awk '/default/ {print $5; exit}'
}
ensure_iptables() {
if ! command -v iptables >/dev/null 2>&1; then
log "iptables not found"
exit 1
fi
}
ACTION="${1:-}"
QUERY="${2:-}"
if [[ -z "$ACTION" || -z "$QUERY" ]]; then
usage
exit 1
fi
INFO_FILE="$(find_vm_info "$QUERY")"
GUEST_IP="$(get_prop "$INFO_FILE" "guest_ip")"
TAP="$(get_prop "$INFO_FILE" "tap")"
if [[ -z "$GUEST_IP" || -z "$TAP" ]]; then
log "missing guest_ip or tap in $INFO_FILE"
exit 1
fi
UPLINK="$(default_uplink)"
if [[ -z "$UPLINK" ]]; then
log "failed to detect uplink interface"
exit 1
fi
ensure_iptables
nat_rule=(-t nat -s "${GUEST_IP}/32" -o "$UPLINK" -j MASQUERADE)
fwd_out_rule=(-i "$TAP" -o "$UPLINK" -j ACCEPT)
fwd_in_rule=(-i "$UPLINK" -o "$TAP" -m state --state "RELATED,ESTABLISHED" -j ACCEPT)
case "$ACTION" in
up)
sudo sysctl -w net.ipv4.ip_forward=1 >/dev/null
sudo iptables -t nat -C POSTROUTING "${nat_rule[@]}" 2>/dev/null || \
sudo iptables -t nat -A POSTROUTING "${nat_rule[@]}"
sudo iptables -C FORWARD "${fwd_out_rule[@]}" 2>/dev/null || \
sudo iptables -A FORWARD "${fwd_out_rule[@]}"
sudo iptables -C FORWARD "${fwd_in_rule[@]}" 2>/dev/null || \
sudo iptables -A FORWARD "${fwd_in_rule[@]}"
log "NAT enabled for $GUEST_IP via $UPLINK"
;;
down)
sudo iptables -t nat -C POSTROUTING "${nat_rule[@]}" 2>/dev/null && \
sudo iptables -t nat -D POSTROUTING "${nat_rule[@]}" || true
sudo iptables -C FORWARD "${fwd_out_rule[@]}" 2>/dev/null && \
sudo iptables -D FORWARD "${fwd_out_rule[@]}" || true
sudo iptables -C FORWARD "${fwd_in_rule[@]}" 2>/dev/null && \
sudo iptables -D FORWARD "${fwd_in_rule[@]}" || true
log "NAT disabled for $GUEST_IP"
;;
status)
sysctl net.ipv4.ip_forward | sed 's/^/[nat] /'
if sudo iptables -t nat -C POSTROUTING "${nat_rule[@]}" 2>/dev/null; then
log "nat: installed"
else
log "nat: missing"
fi
if sudo iptables -C FORWARD "${fwd_out_rule[@]}" 2>/dev/null; then
log "forward out: installed"
else
log "forward out: missing"
fi
if sudo iptables -C FORWARD "${fwd_in_rule[@]}" 2>/dev/null; then
log "forward in: installed"
else
log "forward in: missing"
fi
;;
*)
usage
exit 1
;;
esac