This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -18,6 +18,7 @@
|
|||||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
# User-specific stuff
|
# User-specific stuff
|
||||||
|
.idea
|
||||||
.idea/**/workspace.xml
|
.idea/**/workspace.xml
|
||||||
.idea/**/tasks.xml
|
.idea/**/tasks.xml
|
||||||
.idea/**/usage.statistics.xml
|
.idea/**/usage.statistics.xml
|
||||||
|
|||||||
@@ -30,3 +30,18 @@ steps:
|
|||||||
password:
|
password:
|
||||||
from_secret: registry_password
|
from_secret: registry_password
|
||||||
depends_on: []
|
depends_on: []
|
||||||
|
|
||||||
|
- name: build-wireguard-sidecar
|
||||||
|
image: woodpeckerci/plugin-docker-buildx:6.1.1
|
||||||
|
settings:
|
||||||
|
registry: registry.apps.millslan.net
|
||||||
|
repo: registry.apps.millslan.net/garrettmills/wireguard-sidecar
|
||||||
|
tags: latest
|
||||||
|
platforms: linux/amd64
|
||||||
|
context: wireguard-sidecar
|
||||||
|
dockerfile: wireguard-sidecar/Dockerfile
|
||||||
|
username:
|
||||||
|
from_secret: registry_username
|
||||||
|
password:
|
||||||
|
from_secret: registry_password
|
||||||
|
depends_on: []
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
FROM fedora:44
|
FROM fedora:44
|
||||||
|
|
||||||
# TODO: k8s client
|
|
||||||
|
|
||||||
ENV TARGET_USER=garrettmills
|
ENV TARGET_USER=garrettmills
|
||||||
ENV TARGET_UID=1000
|
ENV TARGET_UID=1000
|
||||||
ENV TARGET_GID=1000
|
ENV TARGET_GID=1000
|
||||||
|
|||||||
12
wireguard-sidecar/Dockerfile
Normal file
12
wireguard-sidecar/Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
FROM fedora:latest
|
||||||
|
|
||||||
|
RUN dnf install -y wg-quick curl iproute openresolv iptables-nft procps-ng \
|
||||||
|
&& dnf clean all -y
|
||||||
|
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
COPY wg-convert-config.sh /wg-convert-config.sh
|
||||||
|
COPY wait-vpn-ready.sh /wait-vpn-ready.sh
|
||||||
|
RUN chmod +x /entrypoint.sh /wg-convert-config.sh /wait-vpn-ready.sh
|
||||||
|
|
||||||
|
ENV WG_TARGET=wg0
|
||||||
|
CMD ["/entrypoint.sh"]
|
||||||
8
wireguard-sidecar/Makefile
Normal file
8
wireguard-sidecar/Makefile
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
.PHONY: image
|
||||||
|
image: Dockerfile
|
||||||
|
docker build -t ${DOCKER_REGISTRY}/wireguard-sidecar:latest -f Dockerfile .
|
||||||
|
|
||||||
|
.PHONY: push
|
||||||
|
push:
|
||||||
|
docker push ${DOCKER_REGISTRY}/wireguard-sidecar:latest
|
||||||
80
wireguard-sidecar/entrypoint.sh
Normal file
80
wireguard-sidecar/entrypoint.sh
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
ACTING_AS_PID1="${ACTING_AS_PID1:-yes}"
|
||||||
|
WG_TARGET="${WG_TARGET:-wg0}"
|
||||||
|
REAL_PUBLIC_IP="$(curl -4 ifconfig.me)"
|
||||||
|
CURRENT_PUBLIC_IP="${REAL_PUBLIC_IP}"
|
||||||
|
DETECT_LEAK="${DETECT_LEAK:-yes}"
|
||||||
|
|
||||||
|
wgs::log() {
|
||||||
|
echo "[wgs]" "$(date -u +%Y-%m-%dT%H:%M:%S%Z)" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
wgs::convert_wg_config() {
|
||||||
|
# Some directives in a wg-config do not work well in containers, e.g. "DNS"
|
||||||
|
# So, convert the ones we can to a container-safe equivalent.
|
||||||
|
/wg-convert-config.sh "/etc/wireguard/${WG_TARGET}.conf" "/etc/wireguard/ctr${WG_TARGET}.conf"
|
||||||
|
}
|
||||||
|
|
||||||
|
wgs::up() {
|
||||||
|
wgs::convert_wg_config
|
||||||
|
wg-quick up "ctr${WG_TARGET}"
|
||||||
|
|
||||||
|
# wg-quick's AllowedIPs=0.0.0.0/0 installs "lookup main suppress_prefixlength 0",
|
||||||
|
# which hides the pod's default route so everything else falls into the tunnel.
|
||||||
|
# That catches replies to inbound connections too, and the web UI goes dark for
|
||||||
|
# any client outside this node's pod subnet. Replies are sourced from the pod
|
||||||
|
# IP, so keying on that restores them without touching outbound traffic, which
|
||||||
|
# is routed before a source is chosen and so never matches this rule.
|
||||||
|
# Deleted first because the pod's netns outlives the container: on a restart
|
||||||
|
# the previous rule is still installed and a second add would stack.
|
||||||
|
ip -4 rule del from "$POD_IP" lookup main priority 100 2>/dev/null || true
|
||||||
|
ip -4 rule add from "$POD_IP" lookup main priority 100
|
||||||
|
}
|
||||||
|
|
||||||
|
wgs::refresh_public_ip() {
|
||||||
|
CURRENT_PUBLIC_IP="$(curl -4 ifconfig.me)"
|
||||||
|
}
|
||||||
|
|
||||||
|
wgs::public_ip_is_masked() {
|
||||||
|
wgs::refresh_public_ip
|
||||||
|
[ "$CURRENT_PUBLIC_IP" != "$REAL_PUBLIC_IP" ] && return 0 || return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
wgs::down() {
|
||||||
|
wgs::log "Cleaning up..."
|
||||||
|
ip -4 rule del from "$POD_IP" lookup main priority 100 2>/dev/null || true
|
||||||
|
wg-quick down "ctr${WG_TARGET}" || true
|
||||||
|
rm -f /vpn-online.touch
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
wgs::pid1() {
|
||||||
|
wgs::log "Starting PID1..."
|
||||||
|
trap wgs::down INT TERM
|
||||||
|
|
||||||
|
wgs::log "Our real public IP is: ${REAL_PUBLIC_IP}"
|
||||||
|
|
||||||
|
wgs::up
|
||||||
|
wgs::refresh_public_ip
|
||||||
|
wgs::log "Our new public IP is: ${CURRENT_PUBLIC_IP}"
|
||||||
|
|
||||||
|
if [ "$DETECT_LEAK" = yes ] && [ "$CURRENT_PUBLIC_IP" = "$REAL_PUBLIC_IP" ]; then
|
||||||
|
wgs::log "Failed to mask IP on startup"
|
||||||
|
wgs::down
|
||||||
|
fi
|
||||||
|
|
||||||
|
touch /vpn-online.touch
|
||||||
|
|
||||||
|
while :; do
|
||||||
|
sleep 60
|
||||||
|
if [ "$DETECT_LEAK" = yes ] && ! wgs::public_ip_is_masked; then
|
||||||
|
wgs::log "Detected a leak; stopping"
|
||||||
|
wgs::down
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$ACTING_AS_PID1" = yes ]; then
|
||||||
|
wgs::pid1
|
||||||
|
fi
|
||||||
6
wireguard-sidecar/wait-vpn-ready.sh
Normal file
6
wireguard-sidecar/wait-vpn-ready.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
while ! [ -f /vpn-online.touch ]; do
|
||||||
|
echo "Waiting for VPN to come online..."
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
113
wireguard-sidecar/wg-convert-config.sh
Normal file
113
wireguard-sidecar/wg-convert-config.sh
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# wg-convert-config — rewrite a WireGuard config's DNS= line as an equivalent
|
||||||
|
# PostUp command, for containers where openresolv/resolvconf can't run.
|
||||||
|
#
|
||||||
|
# usage: wg-convert-config <wg0.conf> [out.conf]
|
||||||
|
# wg-convert-config <wg0.conf> - # write to stdout
|
||||||
|
#
|
||||||
|
# With no output path, writes ./ctr<basename of input>.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
prog=${0##*/}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat >&2 <<EOF
|
||||||
|
usage: $prog <wg0.conf> [out.conf]
|
||||||
|
|
||||||
|
Moves DNS= out of [Interface] and appends an equivalent PostUp that writes
|
||||||
|
/etc/resolv.conf directly. Handles comma-separated addresses (v4 and v6) and
|
||||||
|
treats non-IP values as search domains. Use - as out.conf for stdout.
|
||||||
|
Defaults to ./ctr<input basename>.
|
||||||
|
EOF
|
||||||
|
exit "${1:-2}"
|
||||||
|
}
|
||||||
|
|
||||||
|
case ${1:-} in
|
||||||
|
-h | --help) usage 0 ;;
|
||||||
|
"" | -*) usage ;;
|
||||||
|
esac
|
||||||
|
(($# <= 2)) || usage
|
||||||
|
|
||||||
|
src=$1
|
||||||
|
dst=${2:-ctr${src##*/}}
|
||||||
|
|
||||||
|
[[ -r $src ]] || { printf '%s: cannot read %s\n' "$prog" "$src" >&2; exit 1; }
|
||||||
|
if [[ $dst != - && -e $dst && $src -ef $dst ]]; then
|
||||||
|
printf '%s: refusing to overwrite the source file\n' "$prog" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
convert() {
|
||||||
|
awk '
|
||||||
|
BEGIN { q = "\047" }
|
||||||
|
|
||||||
|
function flushpend( i) {
|
||||||
|
for (i = 1; i <= npend; i++) print pend[i]
|
||||||
|
npend = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function emit( i, out) {
|
||||||
|
if (!nns && !nsd) return
|
||||||
|
out = "PostUp = printf " q "%s\\n" q
|
||||||
|
for (i = 1; i <= nns; i++) out = out " " q "nameserver " ns[i] q
|
||||||
|
if (nsd) {
|
||||||
|
out = out " " q "search"
|
||||||
|
for (i = 1; i <= nsd; i++) out = out " " sd[i]
|
||||||
|
out = out q
|
||||||
|
}
|
||||||
|
print out " > /etc/resolv.conf"
|
||||||
|
if (nns > 3)
|
||||||
|
printf("%s: %d nameservers emitted; glibc reads only the first 3\n",
|
||||||
|
prog, nns) > "/dev/stderr"
|
||||||
|
nns = 0; nsd = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Section header: close out the [Interface] block before leaving it.
|
||||||
|
/^[[:space:]]*\[/ {
|
||||||
|
if (iface) emit()
|
||||||
|
flushpend()
|
||||||
|
iface = (tolower($0) ~ /^[[:space:]]*\[interface\][[:space:]]*$/)
|
||||||
|
print
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
# DNS = a, b, c (keys are case-insensitive, # starts a comment)
|
||||||
|
iface && tolower($0) ~ /^[[:space:]]*dns[[:space:]]*=/ {
|
||||||
|
s = $0
|
||||||
|
sub(/#.*/, "", s)
|
||||||
|
sub(/^[^=]*=/, "", s)
|
||||||
|
gsub(/,/, " ", s)
|
||||||
|
n = split(s, a, /[[:space:]]+/)
|
||||||
|
for (i = 1; i <= n; i++) {
|
||||||
|
if (a[i] == "") continue
|
||||||
|
if (a[i] ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ ||
|
||||||
|
(a[i] ~ /:/ && a[i] ~ /^[0-9a-fA-F:.%]+$/))
|
||||||
|
ns[++nns] = a[i]
|
||||||
|
else
|
||||||
|
sd[++nsd] = a[i]
|
||||||
|
}
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
# Hold blank lines so the PostUp lands above them, not under [Peer].
|
||||||
|
iface && /^[[:space:]]*$/ { pend[++npend] = $0; next }
|
||||||
|
|
||||||
|
{ flushpend(); print }
|
||||||
|
|
||||||
|
END { if (iface) emit(); flushpend() }
|
||||||
|
' prog="$prog" "$src"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $dst == - ]]; then
|
||||||
|
convert
|
||||||
|
else
|
||||||
|
tmp=$(mktemp -- "$dst.XXXXXX")
|
||||||
|
trap 'rm -f -- "$tmp"' EXIT
|
||||||
|
convert > "$tmp"
|
||||||
|
chmod --reference="$src" -- "$tmp" 2>/dev/null || chmod 600 -- "$tmp"
|
||||||
|
mv -- "$tmp" "$dst"
|
||||||
|
trap - EXIT
|
||||||
|
printf '%s: wrote %s\n' "$prog" "$dst" >&2
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user