From 7fdff9a0359ad11d99f7e29df2623babfe793cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Djema=C3=AF?= <53857555+SamyDjemai@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:34:36 +0100 Subject: [PATCH 01/11] ci: add Dockerfile --- .dockerignore | 11 +++++++++++ .gitignore | 1 + Dockerfile | 16 ++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..138f099 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.DS_Store +.idea +*.log +tmp/ + +.direnv/ + +dist-newstyle/ +result +result-* +out/ diff --git a/.gitignore b/.gitignore index a240499..138f099 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ tmp/ dist-newstyle/ result result-* +out/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fa17c02 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM haskell:9.6-slim-bullseye AS builder + +# Copy source code +WORKDIR /app +COPY . . + +# Build static binary, code from https://hasufell.github.io/posts/2024-04-21-static-linking.html +RUN cabal update +RUN cabal build -j --enable-executable-static exe:tailscale-manager +RUN mkdir out/ && cp $(cabal -v0 list-bin exe:tailscale-manager) out/ + +# Copy the binary to a new image, to keep the final image lightweight +FROM alpine:3.20 AS runtime + +COPY --from=builder /app/out/tailscale-manager / +CMD ["/tailscale-manager"] From fd75c7397a985b38b5a2527765ddd42d87989890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Djema=C3=AF?= <53857555+SamyDjemai@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:42:08 +0100 Subject: [PATCH 02/11] ci: add docker-build github action --- .github/workflows/docker-build.yml | 52 ++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 .github/workflows/docker-build.yml diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..ffbe376 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,52 @@ +name: Create and publish a Docker image + +on: + push: + branches: ["ci/dockerfile"] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + attestations: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image + id: push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v1 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}} + subject-digest: ${{ steps.push.outputs.digest }} + push-to-registry: true From 3ad8da3bb64db570f52a5686f8ec9ffc9a03fdf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Djema=C3=AF?= <53857555+SamyDjemai@users.noreply.github.com> Date: Thu, 7 Nov 2024 19:01:12 +0100 Subject: [PATCH 03/11] ci: improve docker image tags --- .github/workflows/docker-build.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index ffbe376..9cc1782 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -1,8 +1,14 @@ name: Create and publish a Docker image on: + pull_request: + branches: + - main push: - branches: ["ci/dockerfile"] + branches: + - main + tags: + - "v*" env: REGISTRY: ghcr.io @@ -34,6 +40,12 @@ jobs: uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha - name: Build and push Docker image id: push @@ -47,6 +59,6 @@ jobs: - name: Generate artifact attestation uses: actions/attest-build-provenance@v1 with: - subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}} + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} subject-digest: ${{ steps.push.outputs.digest }} push-to-registry: true From bc8ce243e5eacc03ade03d58b19716a6e17c5587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Djema=C3=AF?= <53857555+SamyDjemai@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:11:51 +0100 Subject: [PATCH 04/11] ci: rework Dockerfile to cache dependencies --- .github/workflows/docker-build.yml | 2 ++ Dockerfile | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 9cc1782..7f783e5 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -52,6 +52,8 @@ jobs: uses: docker/build-push-action@v6 with: context: . + cache-from: type=gha + cache-to: type=gha,mode=max push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/Dockerfile b/Dockerfile index fa17c02..b179830 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,14 @@ FROM haskell:9.6-slim-bullseye AS builder -# Copy source code +# Install dependencies first, to cache them WORKDIR /app +COPY tailscale-manager.cabal . +RUN cabal update && cabal build -j --only-dependencies + +# Copy source code COPY . . # Build static binary, code from https://hasufell.github.io/posts/2024-04-21-static-linking.html -RUN cabal update RUN cabal build -j --enable-executable-static exe:tailscale-manager RUN mkdir out/ && cp $(cabal -v0 list-bin exe:tailscale-manager) out/ From f3563a627e985c9c7fcb7818772f4900b8ed11af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Djema=C3=AF?= <53857555+SamyDjemai@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:15:16 +0100 Subject: [PATCH 05/11] ci: set up docker buildx in workflow --- .github/workflows/docker-build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 7f783e5..eb41696 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -28,6 +28,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Log in to the Container registry uses: docker/login-action@v3 with: From 75a8caf9f4afd5151e4bce958a33913a2d1dc066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Djema=C3=AF?= <53857555+SamyDjemai@users.noreply.github.com> Date: Fri, 8 Nov 2024 14:27:08 +0100 Subject: [PATCH 06/11] ci: use long commit sha as docker image tag --- .github/workflows/docker-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index eb41696..605fbbd 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -48,7 +48,7 @@ jobs: type=ref,event=pr type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} - type=sha + type=sha,format=long - name: Build and push Docker image id: push From 2537aacad9b5e024bc434865f6210da9b7f7e7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Djema=C3=AF?= <53857555+SamyDjemai@users.noreply.github.com> Date: Fri, 8 Nov 2024 15:35:05 +0100 Subject: [PATCH 07/11] ci: use ghc-musl builder docker image --- Dockerfile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index b179830..0b90f70 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,6 @@ -FROM haskell:9.6-slim-bullseye AS builder +# We can't use the official haskell image, because it's based on Debian, +# which uses glibc, and we need musl to build a static binary. +FROM benz0li/ghc-musl:9.6 AS builder # Install dependencies first, to cache them WORKDIR /app @@ -15,5 +17,5 @@ RUN mkdir out/ && cp $(cabal -v0 list-bin exe:tailscale-manager) out/ # Copy the binary to a new image, to keep the final image lightweight FROM alpine:3.20 AS runtime -COPY --from=builder /app/out/tailscale-manager / -CMD ["/tailscale-manager"] +COPY --from=builder /app/out/tailscale-manager /bin/ +CMD ["tailscale-manager"] From d8e55e4a31ab932cbf643be438bc1de8eb6c87ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Djema=C3=AF?= <53857555+SamyDjemai@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:03:33 +0100 Subject: [PATCH 08/11] ci: rename docker workflow, fix doc typo --- .github/workflows/docker-build.yml | 4 ++-- README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 605fbbd..990ad5e 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -1,4 +1,4 @@ -name: Create and publish a Docker image +name: Docker build and push on: pull_request: @@ -15,7 +15,7 @@ env: IMAGE_NAME: ${{ github.repository }} jobs: - build-and-push-image: + docker-build-and-push: runs-on: ubuntu-latest permissions: diff --git a/README.md b/README.md index 31cf1fd..eed93a7 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,11 @@ normally support App Connectors. Here is a sample config file, in JSON format: -``` json +```json { "routes": [ "172.16.0.0/22", - "192.168.0.0/24", + "192.168.0.0/24" ], "hostRoutes": [ "github.com", From fd108e684b4300a154d196a15f375b55d413c53f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Djema=C3=AF?= <53857555+SamyDjemai@users.noreply.github.com> Date: Fri, 8 Nov 2024 17:16:26 +0100 Subject: [PATCH 09/11] docs: add Docker image doc to README.md --- README.md | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index eed93a7..19fbeed 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # Tailscale routes manager **tailscale-manager** dynamically manages Tailscale subnet route advertisements -based on user-configurable discovery sources. It runs alongside tailscaled on +based on user-configurable discovery sources. It runs alongside tailscaled on the node(s) where you want to advertise routes. ## Supported discovery methods | config keyword | example | description | -|:------------------------|:------------------------------|:---------------------------| +| :---------------------- | :---------------------------- | :------------------------- | | `routes` | `["192.168.0.0/24"]` | Static routes | | `hostRoutes` | `["private-app.example.com"]` | DNS hostname lookup | | `awsManagedPrefixLists` | `["pl-02761f4a40454a3c9"]` | [AWS Managed Prefix Lists] | @@ -16,7 +16,7 @@ the node(s) where you want to advertise routes. `hostRoutes` can be used to emulate [Tailscale App Connectors] by advertising a set of individual IP address routes that are kept in sync with DNS lookups of a -set of hostnames. This is most useful when using [Headscale], which doesn't +set of hostnames. This is most useful when using [Headscale], which doesn't normally support App Connectors. [Tailscale App Connectors]: https://tailscale.com/kb/1281/app-connectors @@ -104,11 +104,39 @@ Available options: -h,--help Show this help text ``` +## Docker image + +A Docker image is built and pushed to GitHub Container Registry on each version, commit, and pull request. The image is built based on Alpine images and contains a statically-linked `tailscale-manager` binary. + +You can use it to build your own custom Tailscale Docker images using the following Dockerfile and `entrypoint.sh` script. + +```dockerfile +# Dockerfile +FROM ghcr.io/singlestore-labs/tailscale-manager AS tailscale-manager +FROM tailscale/tailscale AS tailscale + +COPY --from=tailscale-manager /bin/tailscale-manager /bin/tailscale-manager + +COPY config.json + +COPY entrypoint.sh /usr/local/bin/entrypoint +CMD ["entrypoint"] +``` + +```sh +# entrypoint.sh +#!/bin/sh +tailscale-manager --interval 300 & +containerboot +``` + +This will allow you to use Tailscale as a Docker container while having `tailscale-manager` running in the background, periodically updating routes. + ## NixOS module -If you use NixOS, this repository provides a flake with a NixOS module to install and run tailscale-manager as a systemd service. You can incorporate it into your flake.nix like so: +If you use NixOS, this repository provides a flake with a NixOS module to install and run tailscale-manager as a systemd service. You can incorporate it into your flake.nix like so: -``` nix +```nix { description = "my nixos config"; From 4d1d7919e8142900502f2ab8e9b690c58cd67ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Djema=C3=AF?= <53857555+SamyDjemai@users.noreply.github.com> Date: Fri, 8 Nov 2024 17:54:40 +0100 Subject: [PATCH 10/11] ci: add arm64 docker builds --- .github/workflows/docker-build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 990ad5e..0b68b09 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -28,6 +28,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -55,6 +58,7 @@ jobs: uses: docker/build-push-action@v6 with: context: . + platforms: linux/amd64,linux/arm64 cache-from: type=gha cache-to: type=gha,mode=max push: true From 9a1e5c9d52bd318454b5e9f11c2311ce8ca05735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Djema=C3=AF?= <53857555+SamyDjemai@users.noreply.github.com> Date: Fri, 8 Nov 2024 18:39:25 +0100 Subject: [PATCH 11/11] ci: use native arm64 runner and cache build layers --- .github/workflows/docker-build.yml | 47 ++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 0b68b09..199f09d 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -1,4 +1,4 @@ -name: Docker build and push +name: Docker on: pull_request: @@ -15,7 +15,50 @@ env: IMAGE_NAME: ${{ github.repository }} jobs: - docker-build-and-push: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + platform: + - linux/amd64 + - linux/arm64 + + permissions: + contents: read + packages: write + attestations: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and cache Docker image + id: push + uses: docker/build-push-action@v6 + with: + context: . + platforms: ${{ matrix.platform }} + cache-from: type=gha + cache-to: type=gha,mode=max + push: false + + push: + needs: build runs-on: ubuntu-latest permissions: