diff --git a/Dockerfile b/Dockerfile index 0008476..b145165 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,17 @@ 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 apk add --no-cache openrc udev-init-scripts-openrc wireguard-tools bind-tools bird +RUN mkdir -p /var/lib/inet2 +COPY ./bin /usr/local/bin +COPY ./lib /var/lib/inet2 +COPY ./deployments/openrc/inet2.initd /etc/init.d/inet2 +COPY ./deployments/docker/wait.initd /etc/init.d/wait 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"] +RUN rm -rf /run/openrc 2>/dev/null +RUN mkdir /run/openrc +RUN touch /run/openrc/softlevel +COPY ./deployments/docker/entrypoint.sh / +ENTRYPOINT ["/entrypoint.sh"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..38ac300 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +.PHONY: install + +install: + mkdir -p /var/lib/inet2 + cp ./bin/* /usr/local/bin + cp ./lib/* /var/lib/inet2 + +alpine: + apk add --no-cache wireguard-tools bind-tools bird + cp ./deployments/openrc/inet2.initd /etc/init.d/inet2 + rc-update add inet2 diff --git a/README.md b/README.md index dd82fda..13bf01d 100644 --- a/README.md +++ b/README.md @@ -1,102 +1,116 @@ # freyanet -this container allows perring multiple servers togeather with ospf creating an internetwork on any subnet +peer multiple ospf nodes over wireguard to make an internal network ## running -`docker run --privileged --network host -v $PWD/config:/config g.freya.cat/freya/freyanet` - -or with docker-compose: +### docker ```yml version: "3" services: inet2: - image: g.freya.cat/freya/freyanet - network_mode: host # needed otherwise internal network wont be accessable - privileged: true - volumes: - - ./config:/config + image: g.freya.cat/freya/freyanet + network_mode: host # needed otherwise internal network wont be accessable + privileged: true + volumes: + - ./inet2.conf:/etc/inet2.conf ``` +### host + +run the following commands with the provided makefile + +```bash +$ make +$ make +``` + +the current supported os's are: `alpine` + +start the `inet2` service to start freyanet + ## config -create a file at /config/inet2.conf +- if running in docker mount a file called `inet2.conf` at `/etc/inet2.conf` +- if running on host make a file called `/etc/inet2.conf` ``` -# specify router id +# specify router id for ospf RouterID 10.1.1.1 +# optionally assign static addresses to the loopback interface +Loopback 1.2.3.4 + # specify routed subnets +# ips that are not in these subnets will be ignored +# put the ip blocks for your entire internal network Subnet 10.0.0.0/8 -Subnet fd:cafe::/48 +Subnet fd:cafe::/32 + +# specify node stubnets +# ip blocks that this node is gurenteed to route +Stubnet 10.1.0.0/8 +Stubnet fd:cafe:dead::/48 # optional global private key gets used for all interfaces PrivateKey = {host private key} +# create a peered wireguard interface +# specify addresses, routes, ports, pre/post commands, and peers +# keys can be generated with wg genkey and wg pubkey interface interfacename - # indentation is a single tab per level + # indentation is a single tab per level + # otherwise file will fail to parse - # set the address(es) to assign to the interface - # route lines are usually the same as Address but with host bits zeroed - Address 10.2.255.1/30 - Route 10.2.255.0/30 # must specify route - # you can also set ipv6 - Address fd:cafe::ffff/64 - Address fd:cafe::/64 - # link local is also possible (and prefered for peering routers) - # link-local addresses should *not* have an associated Route line - Address fe80::1/64 - Route 1.1.1.0/24 - # more syntax options - Route 1.1.2.0/24 via 1.1.1.2 - Route default via 1.1.1.3 - # optional: set Gateway and Gateway6 to configure a default gateway - # through this interface - Gateway 1.1.1.2 - Gateway6 fe80::1111:1111:1111:1111 - # port to listen on in the host's network namespace, over udp - # you probably have to allow this through your firewall - ListenPort {host port} - # omit if using global private key - PrivateKey {host private key} - # all optional - PreUp command - PostUp command - PreDown command - PostDown command - - # if running ospf on this interface - OSPF - # if running ospf on this interface and it's a stub network (no other routers) - OSPF stub - - peer peername - PublicKey {peer public key} - # if the peer is a router, it has to have AllowedIPs set to everything and be the only - # peer on the interface - AllowedIPs 0.0.0.0/0, ::/0 - # either: - Domain = {domain name of peer} - Port = {peer port} - # or: - Endpoint = {peer ip}:{peer port} - # make domain enpoint resolve with ipv (ipv6 is default) - IPv4 - - # optional - PersistentKeepalive = 25 - -# optionally assign static addresses to the loopback interface -# this has its uses for making things ibgp not dependent on a specific interface being up -Loopback 1.2.3.4 + # set the address(es) to assign to the interface + # route lines are usually the same as Address but with host bits zeroed + Address 10.2.255.1/30 + Route 10.2.255.0/30 # must specify route + # you can also set ipv6 + Address fd:cafe::ffff/64 + Address fd:cafe::/64 + # link local is also possible (and prefered for peering routers) + # link-local addresses should *not* have an associated Route line + Address fe80::1/64 + Route 1.1.1.0/24 + # more syntax options + Route 1.1.2.0/24 via 1.1.1.2 + Route default via 1.1.1.3 + # port to listen on in the host's network namespace, over udp + # you probably have to allow this through your firewall + ListenPort {host port} + # omit if using global private key + PrivateKey {host private key} + # all optional + PreUp command + PostUp command + PreDown command + PostDown command + + # if running ospf on this interface + OSPF + # if running ospf on this interface and it's a stub network (no other routers) + OSPF stub + + peer peername + PublicKey {peer public key} + # if the peer is a router, it has to have AllowedIPs set to everything and be the only + # peer on the interface + AllowedIPs 0.0.0.0/0, ::/0 + # either: + Domain = {domain name of peer} + Port = {peer port} + # or: + Endpoint = {peer ip}:{peer port} + # make domain enpoint resolve with ipv (ipv6 is default) + IPv4 + + # optional + PersistentKeepalive = 25 ``` -and other optional files: -- `/config/setup.sh` gets run on the first run of the container with the host's networking -- `/config/start.sh` gets run every time the container starts up with the host's networking - -### Licenses +## licenses | License | Author | Project | |---------|--------|---------| diff --git a/inet2.initd b/bin/inet2.sh similarity index 52% rename from inet2.initd rename to bin/inet2.sh index 5fe8c7c..7a93cbb 100755 --- a/inet2.initd +++ b/bin/inet2.sh @@ -1,20 +1,6 @@ -#!/sbin/openrc-run -name="inet2" -description="Sets up wireguard interfaces connected via the host's internet connection" +#!/usr/bin/env sh -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 "$@" -} +. /var/lib/inet2/inet2.sh runscripts() { if [ -n "$(getval "interface $2" "$1")" ]; then @@ -25,23 +11,24 @@ runscripts() { fi } - - start() { + step "Starting inet2" + step "Removing old Wireguard interfaces" for file in /sys/class/net/*; do # Clear all wireguard interfaces type=$(cat "$file/type") if [ "$type" = "65534" ]; then - ifname="$(basename $file)" - ip link del "$ifname" + ifname="$(basename $file)" + run ip link del "$ifname" fi done - 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 - + rm -fr /run/inet2/wg 2> /dev/null + mkdir -p /run/inet2/wg + + mkbirdconfig.sh + + step "Setting loopback addresses" getval Loopback | while read -r addr; do run ip addr add "$addr" dev lo done @@ -84,36 +71,35 @@ start() { } stop() { - if [ -f /run/inet2/inet2.conf ]; then - getval Loopback | while read -r addr; do - run ip addr del "$addr" dev lo - done + step "Stopping inet2" + step "Removing loopback" + 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 + getval interface | while read -r inter; do + runscripts PreDown "$inter" - rm -rf /run/inet2/inet2.conf - fi + step "Bringing $inter down" + run ip link del "$inter" + + runscripts PostDown "$inter" + done } -# 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 +reload() { + 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 } +restart() { + stop + start +} + +$1 diff --git a/bin/mkbirdconfig.sh b/bin/mkbirdconfig.sh new file mode 100755 index 0000000..bf5c1f3 --- /dev/null +++ b/bin/mkbirdconfig.sh @@ -0,0 +1,84 @@ +#!/bin/sh + +. /var/lib/inet2/inet2.sh + +escapebird() { + sed -e 's/\\/\\\\/g;s/"/\\"/g' +} + +step "Creating Bird configuration" + +touch /var/log/bird.log +chown bird:bird /var/log/bird.log + +interfacelist=$( + 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)\" {" + echo " type ptp;" + if [ -n "$val" ]; then + echo " $val;"; + fi + echo " };" + fi + done +) + +filter4=$(getval "Subnet" | grep -v ':' | while read -r line; do printf "%s+," "$line"; done | sed 's/,$//') +filter6=$(getval "Subnet" | grep ':' | while read -r line; do printf "%s+," "$line"; done | sed 's/,$//') + +(cat < /etc/bird.conf + +chown root:bird /etc/bird.conf +chmod 640 /etc/bird.conf diff --git a/bin/mkwgconfig.sh b/bin/mkwgconfig.sh index dcbb98f..6ee11a9 100755 --- a/bin/mkwgconfig.sh +++ b/bin/mkwgconfig.sh @@ -1,14 +1,11 @@ -#!/bin/sh +#!/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 +configfile=/etc/inet2.conf getval() { - /usr/local/bin/config.awk "$configfile" "$@" + /var/lib/inet2/config.awk "$configfile" "$@" } k() { diff --git a/deployments/docker/entrypoint.sh b/deployments/docker/entrypoint.sh new file mode 100755 index 0000000..f1daef3 --- /dev/null +++ b/deployments/docker/entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +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 + diff --git a/wait.initd b/deployments/docker/wait.initd similarity index 100% rename from wait.initd rename to deployments/docker/wait.initd diff --git a/deployments/openrc/inet2.initd b/deployments/openrc/inet2.initd new file mode 100755 index 0000000..3d12227 --- /dev/null +++ b/deployments/openrc/inet2.initd @@ -0,0 +1,26 @@ +#!/sbin/openrc-run +name="inet2" +description="Sets up wireguard interfaces connected via the host's internet connection" + +extra_started_commands="reloadwg" + +start() { + /usr/local/bin/inet2.sh start + rc-service bird start 2> /dev/null +} + +stop() { + /usr/local/bin/inet2.sh stop + rc-service bird stop 2> /dev/null +} + +restart() { + stop + start +} + +reload() { + /usr/local/bin/inet2.sh reload + rc-service bird restart 2> /dev/null +} + diff --git a/bin/config.awk b/lib/config.awk similarity index 100% rename from bin/config.awk rename to lib/config.awk diff --git a/lib/inet2.sh b/lib/inet2.sh new file mode 100755 index 0000000..672286e --- /dev/null +++ b/lib/inet2.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env sh + +run() { + printf '$ \x1b[32;1m%s\x1b[0m\n' "$*" + "$@" +} +step() { + printf '\x1b[34;1m>> %s\x1b[0m\n' "$*" +} + +getval() { + /var/lib/inet2/config.awk /etc/inet2.conf "$@" +} + +haskey() { + getval interface | while read -r inter; do + if getval "interface $inter" "$1"; then + echo "true" + return + fi + done +} diff --git a/setup.sh b/setup.sh deleted file mode 100755 index 6d51389..0000000 --- a/setup.sh +++ /dev/null @@ -1,149 +0,0 @@ -#!/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 < /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 -