This commit is contained in:
Freya Murphy 2023-11-10 19:44:48 -05:00
commit 8b7fe69ca3
No known key found for this signature in database
GPG key ID: 988032A5638EE799
6 changed files with 357 additions and 0 deletions

12
Dockerfile Normal file
View file

@ -0,0 +1,12 @@
FROM alpine
RUN apk add --no-cache wireguard-tools bind-tools bird openrc udev-init-scripts-openrc
COPY ./wait.initd /etc/init.d/wait
COPY ./inet2.initd /etc/init.d/inet2
RUN sed -i 's/#rc_sys=""/rc_sys="docker"/' /etc/rc.conf && \
rc-update add wait && \
rc-update add inet2
COPY ./setup.sh /setup.sh
COPY ./bin /usr/local/bin
ENTRYPOINT ["/setup.sh"]

35
bin/config.awk Executable file
View file

@ -0,0 +1,35 @@
#!/run/current-system/profile/bin/awk -f
BEGIN {
FS = "[ ]"; # use a single space as field separator and don't trim input
ind = 0; # indentation level
last = ARGC - 3; # last argument index
exitcode = 1; # whether anything has been matched
if(last < 0) { # there should be at least one argument after the filename
exit 1;
}
ARGC = 2; # don't read ARGV[2] and onward as files
}
END {
exit exitcode;
}
$0 != "" { # exit when the indentation block is exited
for(i = 0; i < ind; i++) {
if(! sub(/^\t/, "")) {
exit exitcode;
}
}
}
# if on the last argument, interpret it as a key and print the value
ind == last && $1 == ARGV[ind + 2] {
exitcode = 0;
print substr($0, length($1) + 2);
}
# if not on the last argument, find the string exactly and increment indentation
ind != last && $0 == ARGV[ind + 2] {
ind++;
}

51
bin/mkwgconfig.sh Executable file
View file

@ -0,0 +1,51 @@
#!/usr/bin/env sh
# args: /path/to/interface-config /path/to/output.conf
inter="$1"
configfile="$3"
if [ -z "$configfile" ]; then
configfile=/run/inet2/inet2.conf
fi
getval() {
/usr/local/bin/config.awk "$configfile" "$@"
}
k() {
while read -r line; do
echo "$1 = $line"
done
}
(
echo "[Interface]"
getval "interface $inter" ListenPort | k ListenPort
getval "interface $inter" PrivateKey | k PrivateKey
getval PrivateKey | k PrivateKey
echo
getval "interface $inter" peer | while read -r peer; do
echo "[Peer]"
getval "interface $inter" "peer $peer" PublicKey | k PublicKey
getval "interface $inter" "peer $peer" AllowedIPs | k AllowedIPs
domain="$(getval "interface $inter" "peer $peer" Domain)"
if [ -n "$domain" ]; then
# it doesn't like domain names in the Endpoint field, so resolve dns here
v4="$(dig +short "$domain")"
[ ! "$?" = "0" ] && v4=""
v6="$(dig +short -t aaaa "$domain")"
[ ! "$?" = "0" ] && v6=""
if getval "interface $inter" "peer $peer" IPv4; then
v6=""
fi
addr="[$v6]"
[ "$addr" = "[]" ] && addr="$v4"
echo "Endpoint = $addr:$(getval "interface $inter" "peer $peer" Port)"
else
getval "interface $inter" "peer $peer" Endpoint | k Endpoint
fi
getval "interface $inter" "peer $peer" PersistentKeepalive | k PersistentKeepalive
echo
done
) > "$2"

108
inet2.initd Executable file
View file

@ -0,0 +1,108 @@
#!/sbin/openrc-run
name="inet2"
description="Sets up wireguard interfaces connected via the host's internet connection"
extra_started_commands="reloadwg"
run() {
printf '$ \x1b[32;1m%s\x1b[0m\n' "$*"
"$@"
}
step() {
printf '\x1b[34;1m>> %s\x1b[0m\n' "$*"
}
getval() {
/usr/local/bin/config.awk /run/inet2/inet2.conf "$@"
}
runscripts() {
if [ -n "$(getval "interface $2" "$1")" ]; then
step "Running $1 for $2"
getval "interface $2" "$1" | while read -r line; do
(eval "$line")
done
fi
}
start() {
rm -rf /run/inet2/config 2>/dev/null
rm -rf /run/inet2/wg 2>/dev/null
cp /config/inet2.conf /run/inet2/inet2.conf
mkdir /run/inet2/wg
getval Loopback | while read -r addr; do
run ip addr add "$addr" dev lo
done
getval interface | while read -r inter; do
step "Generating config for $inter"
run mkwgconfig.sh "$inter" /run/inet2/wg/"$inter"
# create the wireguard interface *in the default namespace*
step "Adding Wireguard interface $inter"
run ip link add name "$inter" type wireguard
# set up the new network from the config
step "Setting Wireguard config for $inter"
run wg setconf "$inter" /run/inet2/wg/"$inter"
# the config doesn't actually add any addresses, do that here
step "Adding host addresses for $inter"
getval "interface $inter" Address | while read -r addr; do
run ip addr add "$addr" dev "$inter"
done
runscripts PreUp "$inter"
step "Bringing interface up"
run ip link set dev "$inter" up
getval "interface $inter" Route | while read -r line; do
read -r route via addr2 < <(printf "%s" "$line")
if [ "$via" = "via" ]; then
run ip route add "$route" via "$addr2" dev "$inter"
else
run ip route add "$route" dev "$inter"
fi
done
runscripts PostUp "$inter"
done
step "Done!"
}
stop() {
if [ -f /run/inet2/inet2.conf ]; then
getval Loopback | while read -r addr; do
run ip addr del "$addr" dev lo
done
getval interface | while read -r inter; do
runscripts PreDown "$inter"
step "Bringing $inter down"
run ip link del "$inter"
runscripts PostDown "$inter"
done
rm -rf /run/inet2/inet2.conf
fi
}
# just reloads the wireguard configs for existing interfaces
# for if a peer's domain name resolves to a different ip address now
# and it needs to be re-resolved without taking down the connection
reloadwg() {
if [ -f /run/inet2/inet2.conf ]; then
getval interface | while read -r inter; do
step "Generating config for $inter"
run mkwgconfig.sh "$inter" /run/inet2/wg/"$inter" /config/inet2.conf
step "Setting Wireguard config for $inter"
run wg setconf "$inter" /run/inet2/wg/"$inter"
done
fi
}

147
setup.sh Executable file
View file

@ -0,0 +1,147 @@
#!/bin/sh
run() {
printf '$ \x1b[32;1m%s\x1b[0m\n' "$*"
"$@"
}
step() {
printf '\x1b[34;1m>> %s\x1b[0m\n' "$*"
}
getval() {
/usr/local/bin/config.awk /config/inet2.conf "$@"
}
haskey() {
getval interface | while read -r inter; do
if getval "interface $inter" "$1"; then
echo "true"
return
fi
done
}
# ensure the /run/inet2 directory is empty (docker doesn't mount tmpfs to /run)
# /run/inet2 is used for storage during runtime - restarting the container should clear it
rm -rf /run/inet2 2>/dev/null
mkdir /run/inet2
# ensure the /var/lib/inet2 directory exists
# /var/lib/inet2 is used for storage for the entire lifetime of the container - restarting the container shouldn't clear it
if [ ! -d /var/lib/inet2 ]; then
mkdir -p /var/lib/inet2
fi
# these are disabled in the docker netns
step "Enabling IPv6"
run sysctl net.ipv6.conf.all.disable_ipv6=0 net.ipv6.conf.default.disable_ipv6=0 net.ipv6.conf.all.forwarding=1
ospf="$(haskey OSPF)"
escapebird() {
sed -e 's/\\/\\\\/g;s/"/\\"/g'
}
if [ -n "$ospf" ]; then
step "Creating Bird configuration"
touch /var/log/bird.log
chown bird:bird /var/log/bird.log
selfas=$(getval AS)
(
cat <<EOF
log "/var/log/bird.log" all;
$(getval RouterID | while read -r line; do echo "router id $line;"; done)
protocol kernel {
ipv4 {
export filter { if source ~ [RTS_BGP, RTS_OSPF, RTS_OSPF_IA, RTS_OSPF_EXT1, RTS_OSPF_EXT2] then accept; else reject; };
import all;
};
learn;
scan time 10;
}
protocol kernel {
ipv6 {
export filter { if source ~ [RTS_BGP, RTS_OSPF, RTS_OSPF_IA, RTS_OSPF_EXT1, RTS_OSPF_EXT2] then accept; else reject; };
import all;
};
learn;
scan time 10;
}
protocol device {
}
protocol direct {
ipv4;
ipv6;
}
EOF
if [ -n "$ospf" ]; then
interfacelist=$(
echo " area 0 {"
echo " interface \"lo\" { stub; };"
getval interface | while read -r inter; do
val="$(getval "interface $inter" OSPF)"
if [ "$?" = "0" ]; then
echo " interface \"$(printf "%s" "$inter" | escapebird)\" {"
if [ -n "$val" ]; then
echo " $val;";
fi
echo " };"
fi
done
echo " };"
)
cat <<EOF
protocol ospf v3 ospf4 {
ipv4 {
import all;
export filter { if source ~ [RTS_DEVICE, RTS_INHERIT] then accept; else reject; };
};
$interfacelist
}
protocol ospf v3 ospf6 {
ipv6 {
import all;
export filter { if source ~ [RTS_DEVICE, RTS_INHERIT] then accept; else reject; };
};
$interfacelist
}
EOF
) > /etc/bird.conf
chown root:bird /etc/bird.conf
chmod 640 /etc/bird.conf
step "Enabling BIRD"
run rc-update add bird
fi
if [ ! -f /var/lib/inet2/setupDone ]; then
if [ -f /config/setup.sh ]; then
step "Running /config/setup.sh"
/config/setup.sh
fi
touch /var/lib/inet2/setupDone
fi
if [ -f /config/start.sh ]; then
step "Running /config/start.sh"
/config/start.sh
fi
if [ "$#" = "0" ]; then
step "Starting OpenRC"
rm -rf /run/openrc 2>/dev/null
mkdir /run/openrc
touch /run/openrc/softlevel
exec /sbin/openrc
else
"$@"
fi

4
wait.initd Executable file
View file

@ -0,0 +1,4 @@
#!/sbin/openrc-run
# this container may need openrc to keep running without any services
# ordinarily that would cause it to stop, so this is a dummy service
start(){ sleep infinity & }