From 32a5497c00dd176cb29a2767e468433ff128536e Mon Sep 17 00:00:00 2001 From: Kyle Harding Date: Wed, 14 Feb 2024 15:44:30 -0500 Subject: [PATCH] Add tailscale service and bind DNS to provided interfaces Signed-off-by: Kyle Harding --- README.md | 10 +++--- adguard/Dockerfile | 11 ++++++ adguard/entrypoint.sh | 40 ++++++++++++++++++++++ docker-compose.yml | 74 +++++++++++++++++++++++++++-------------- tailscale/Dockerfile | 15 +++++++++ tailscale/entrypoint.sh | 18 ++++++++++ 6 files changed, 139 insertions(+), 29 deletions(-) create mode 100644 adguard/Dockerfile create mode 100644 adguard/entrypoint.sh create mode 100644 tailscale/Dockerfile create mode 100644 tailscale/entrypoint.sh diff --git a/README.md b/README.md index 3078a46..97df6d7 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,12 @@ flashing a device, downloading the project and pushing it via the [balena CLI](h Application environment variables apply to all services within the application, and can be applied fleet-wide to apply to multiple devices. -| Name | Description | -| -------------- | ---------------------------------------------------------------------------------------------------------------- | -| `TZ` | Inform services of the [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) in your location | -| `SET_HOSTNAME` | Set a custom hostname on application start. Default is `adguard`. | +| Name | Description | +| ---------------- | ---------------------------------------------------------------------------------------------------------------- | +| `TZ` | Inform services of the [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) in your location | +| `SET_HOSTNAME` | Set a custom hostname on application start. Default is `adguard`. | +| `DNS_INTERFACES` | Optionally provide a space-separated list of interfaces to bind DNS. For example: `eth0 wlan0 tailscale0`. | +| `TS_AUTH_KEY` | [Tailscale auth key](https://tailscale.com/kb/1085/auth-keys) to join an existing Tailnet. Default is unset. | ## Usage diff --git a/adguard/Dockerfile b/adguard/Dockerfile new file mode 100644 index 0000000..68afc15 --- /dev/null +++ b/adguard/Dockerfile @@ -0,0 +1,11 @@ +# https://hub.docker.com/r/adguard/adguardhome +FROM adguard/adguardhome:v0.107.44@sha256:049ef6c019a629b33db2ef957665b257fbd5761970a1517a309807fb128befb1 + +# hadolint ignore=DL3018 +RUN apk add --no-cache yq + +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +ENTRYPOINT [ "/entrypoint.sh" ] +CMD [ ] diff --git a/adguard/entrypoint.sh b/adguard/entrypoint.sh new file mode 100644 index 0000000..a9d8f20 --- /dev/null +++ b/adguard/entrypoint.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env sh + +set -e + +CONFIG_FILE="/opt/adguardhome/conf/AdGuardHome.yaml" + +get_ipv4() { + ip -4 -o addr show "${1}" | awk '{print $4}' | cut -d "/" -f 1 +} + +# get the ipv4 for each provided interface, if any +for interface in ${DNS_INTERFACES}; do + while true; do + ipv4_address="$(get_ipv4 "${interface}")" + + if [ -n "${ipv4_address}" ]; then + bind_hosts="${bind_hosts:+${bind_hosts},}${ipv4_address}" + break + fi + + # this loop will run forever if the interface never gets an ipv4 address + echo "Waiting for interface ${interface} to get an IPv4 address..." + sleep 5 + done +done + +# if we found ipv4 addresses for the interfaces, update the config yaml +if [ -n "${bind_hosts}" ]; then + echo "Setting bind hosts to ${bind_hosts}..." + bind_hosts="[${bind_hosts}]" yq e '.dns.bind_hosts = env(bind_hosts)' "${CONFIG_FILE}" >"${CONFIG_FILE}.tmp" + mv "${CONFIG_FILE}.tmp" "${CONFIG_FILE}" +fi + +# start adguard with the updated configuration +exec /opt/adguardhome/AdGuardHome \ + --no-check-update \ + --web-addr 0.0.0.0:80 \ + -c "${CONFIG_FILE}" \ + -w /opt/adguardhome/work \ + "$@" diff --git a/docker-compose.yml b/docker-compose.yml index 3296bdb..b857d4c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,31 +1,55 @@ -version: '2.1' +version: "2.1" volumes: - adguardhome_work: - adguardhome_conf: + adguardhome_work: {} + adguardhome_conf: {} + tailscale: {} services: - # https://hub.docker.com/r/adguard/adguardhome - adguard: - image: adguard/adguardhome:v0.107.44@sha256:049ef6c019a629b33db2ef957665b257fbd5761970a1517a309807fb128befb1 - network_mode: host - privileged: true - volumes: - - 'adguardhome_work:/opt/adguardhome/work:rw' - - 'adguardhome_conf:/opt/adguardhome/conf:rw' - command: - - "--no-check-update" - - "--web-addr" - - "0.0.0.0:80" - - "--config" - - "/opt/adguardhome/conf/AdGuardHome.yaml" + adguard: + build: adguard + network_mode: host + cap_add: + - NET_ADMIN + volumes: + - "adguardhome_work:/opt/adguardhome/work:rw" + - "adguardhome_conf:/opt/adguardhome/conf:rw" + + # https://github.com/balenablocks/hostname + hostname: + image: balenablocks/hostname:latest@sha256:b923d6ea886ec48125d2c49e431638d2ef6f4adac574b729b4135cfade3ffdce + restart: no + labels: + io.balena.features.supervisor-api: 1 + environment: + SET_HOSTNAME: adguard - # https://github.com/balenablocks/hostname - hostname: - image: balenablocks/hostname:latest@sha256:b923d6ea886ec48125d2c49e431638d2ef6f4adac574b729b4135cfade3ffdce - restart: no - labels: - io.balena.features.supervisor-api: 1 - environment: - SET_HOSTNAME: adguard + # https://hub.docker.com/r/tailscale/tailscale + # https://github.com/tailscale/tailscale/blob/main/cmd/containerboot/main.go + # https://tailscale.com/kb/1282/docker + # https://tailscale.com/kb/1278/tailscaled + # https://tailscale.com/kb/1241/tailscale-up + # https://tailscale.com/kb/1242/tailscale-serve + # https://tailscale.com/kb/1311/tailscale-funnel + tailscale: + build: tailscale + restart: on-failure + environment: + TS_STATE_DIR: /var/lib/tailscale + TS_USERSPACE: false + TS_AUTH_ONCE: false + TS_HOSTNAME: adguard + TS_EXTRA_ARGS: --accept-dns=false --reset + network_mode: host + cap_add: + - NET_ADMIN + - NET_RAW + - SYS_MODULE + labels: + io.balena.features.kernel-modules: 1 + tmpfs: + - /tmp + - /run + volumes: + - tailscale:/var/lib/tailscale diff --git a/tailscale/Dockerfile b/tailscale/Dockerfile new file mode 100644 index 0000000..fd4c422 --- /dev/null +++ b/tailscale/Dockerfile @@ -0,0 +1,15 @@ +# https://hub.docker.com/r/tailscale/tailscale +# https://github.com/tailscale/tailscale/blob/main/cmd/containerboot/main.go +# https://tailscale.com/kb/1282/docker +# https://tailscale.com/kb/1278/tailscaled +# https://tailscale.com/kb/1241/tailscale-up +# https://tailscale.com/kb/1242/tailscale-serve +# https://tailscale.com/kb/1311/tailscale-funnel +FROM tailscale/tailscale:v1.61.11@sha256:06e993635efcfa9574147ed223424cba562f7a5f0785e3b74381477414d57227 + +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +CMD ["/entrypoint.sh"] + +ENV TS_SOCKET /var/run/tailscale/tailscaled.sock diff --git a/tailscale/entrypoint.sh b/tailscale/entrypoint.sh new file mode 100644 index 0000000..eb88ebd --- /dev/null +++ b/tailscale/entrypoint.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env sh + +set -eu + +if [ -z "${TS_AUTH_KEY:-}" ]; then + echo "TS_AUTH_KEY is not set. Exiting." + exit 1 +fi + +# attempt to load required kernel modules +modprobe tun || true +modprobe wireguard || true + +# create tun device if it doesn't exist +mkdir -p /dev/net +[ ! -c /dev/net/tun ] && mknod /dev/net/tun c 10 200 + +exec /usr/local/bin/containerboot