Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: add Dockerfile and Docker image build action #5

Merged
merged 11 commits into from
Nov 8, 2024
11 changes: 11 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.DS_Store
.idea
*.log
tmp/

.direnv/

dist-newstyle/
result
result-*
out/
116 changes: 116 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
name: Docker

on:
pull_request:
branches:
- main
push:
branches:
- main
tags:
- "v*"

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
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:
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: Extract metadata (tags, labels) for Docker
id: meta
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,format=long

- name: Build and push Docker image
id: push
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
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ tmp/
dist-newstyle/
result
result-*
out/
21 changes: 21 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 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
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 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 /bin/
CMD ["tailscale-manager"]
42 changes: 35 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -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] |
Expand All @@ -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
Expand All @@ -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",
Expand Down Expand Up @@ -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 <CONFIG_FILE>

COPY entrypoint.sh /usr/local/bin/entrypoint
CMD ["entrypoint"]
```

```sh
# entrypoint.sh
#!/bin/sh
tailscale-manager <CONFIG_FILE> --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";

Expand Down
Loading