things
This commit is contained in:
commit
8b7fe69ca3
6 changed files with 357 additions and 0 deletions
12
Dockerfile
Normal file
12
Dockerfile
Normal 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
35
bin/config.awk
Executable 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
51
bin/mkwgconfig.sh
Executable 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
108
inet2.initd
Executable 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
147
setup.sh
Executable 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
4
wait.initd
Executable 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 & }
|
Loading…
Reference in a new issue