Skip to content

Commit d296363

Browse files
author
Paul C
committed
wolfnet: publish multi-arch Docker image to GHCR
- docker/Dockerfile: switch to TARGETARCH-based COPY so one Dockerfile serves both linux/amd64 and linux/arm64 buildx targets. Context is now the repo root; pre-built binaries live under dist/<arch>/. - docker/entrypoint.sh: rewrite around the real CLI. The previous script called `wolfnet genkey > $KEY_FILE` and `wolfnet pubkey < $KEY_FILE` (genkey writes via --output and pubkey takes no stdin), passed `--config-dir` to `join` (flag doesn't exist), and piped the TOML config through python3 (not in debian-slim). First-run bootstrap is now simply `wolfnet join $TOKEN` or `wolfnet init --address $WOLFNET_ADDRESS`, both of which write the config and key themselves. - .github/workflows/docker-publish.yml: new. Cross-compiles both musl targets, then buildx+QEMU assembles a multi-arch image and pushes to ghcr.io/wolfsoftwaresystemsltd/wolfnet with :latest and :<version> tags. Fixes the 403 that NAS satellite installs were hitting on `docker compose -f docker-compose.satellite.yml up -d` because the image had never been published.
1 parent b4938ec commit d296363

3 files changed

Lines changed: 132 additions & 79 deletions

File tree

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
name: Publish Docker Image
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- 'Cargo.toml'
8+
- 'src/**'
9+
- 'docker/**'
10+
- '.github/workflows/docker-publish.yml'
11+
workflow_dispatch:
12+
13+
jobs:
14+
build-binaries:
15+
strategy:
16+
matrix:
17+
include:
18+
- target: x86_64-unknown-linux-musl
19+
arch: amd64
20+
runner: ubuntu-latest
21+
- target: aarch64-unknown-linux-musl
22+
arch: arm64
23+
runner: ubuntu-latest
24+
25+
runs-on: ${{ matrix.runner }}
26+
steps:
27+
- uses: actions/checkout@v4
28+
29+
- name: Install Rust
30+
uses: dtolnay/rust-toolchain@stable
31+
with:
32+
targets: ${{ matrix.target }}
33+
34+
- name: Install cross
35+
run: cargo install cross --git https://github.com/cross-rs/cross
36+
37+
- name: Build static binaries
38+
run: cross build --release --target ${{ matrix.target }}
39+
40+
- name: Stage binaries
41+
run: |
42+
mkdir -p dist/${{ matrix.arch }}
43+
cp target/${{ matrix.target }}/release/wolfnet dist/${{ matrix.arch }}/wolfnet
44+
cp target/${{ matrix.target }}/release/wolfnetctl dist/${{ matrix.arch }}/wolfnetctl
45+
chmod +x dist/${{ matrix.arch }}/wolfnet dist/${{ matrix.arch }}/wolfnetctl
46+
47+
- name: Upload artifact
48+
uses: actions/upload-artifact@v4
49+
with:
50+
name: binaries-${{ matrix.arch }}
51+
path: dist/${{ matrix.arch }}/
52+
53+
publish:
54+
needs: build-binaries
55+
runs-on: ubuntu-latest
56+
permissions:
57+
contents: read
58+
packages: write
59+
steps:
60+
- uses: actions/checkout@v4
61+
62+
- name: Get version
63+
id: version
64+
run: echo "version=$(grep '^version' Cargo.toml | head -1 | sed 's/.*\"\(.*\)\".*/\1/')" >> "$GITHUB_OUTPUT"
65+
66+
- name: Download amd64 binaries
67+
uses: actions/download-artifact@v4
68+
with:
69+
name: binaries-amd64
70+
path: dist/amd64
71+
72+
- name: Download arm64 binaries
73+
uses: actions/download-artifact@v4
74+
with:
75+
name: binaries-arm64
76+
path: dist/arm64
77+
78+
- name: Ensure binaries are executable
79+
run: chmod +x dist/amd64/* dist/arm64/*
80+
81+
- name: Set up QEMU
82+
uses: docker/setup-qemu-action@v3
83+
84+
- name: Set up Docker Buildx
85+
uses: docker/setup-buildx-action@v3
86+
87+
- name: Log in to GHCR
88+
uses: docker/login-action@v3
89+
with:
90+
registry: ghcr.io
91+
username: ${{ github.actor }}
92+
password: ${{ secrets.GITHUB_TOKEN }}
93+
94+
- name: Build and push multi-arch image
95+
uses: docker/build-push-action@v5
96+
with:
97+
context: .
98+
file: docker/Dockerfile
99+
platforms: linux/amd64,linux/arm64
100+
push: true
101+
tags: |
102+
ghcr.io/wolfsoftwaresystemsltd/wolfnet:latest
103+
ghcr.io/wolfsoftwaresystemsltd/wolfnet:${{ steps.version.outputs.version }}

wolfnet/docker/Dockerfile

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
# WolfNet Docker Container
2-
# Secure mesh VPN — runs in --network host mode with /dev/net/tun
1+
# WolfNet Docker Container — secure mesh VPN
2+
#
3+
# Runs in --network host mode with /dev/net/tun. Pre-built static musl
4+
# binaries are placed under dist/<arch>/ by the publish workflow; this
5+
# Dockerfile copies the right one based on TARGETARCH.
36
#
47
# Usage:
58
# docker run -d --name wolfnet --network host \
@@ -10,6 +13,8 @@
1013

1114
FROM debian:bookworm-slim
1215

16+
ARG TARGETARCH
17+
1318
RUN apt-get update && apt-get install -y --no-install-recommends \
1419
iproute2 \
1520
iptables \
@@ -19,9 +24,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
1924

2025
RUN mkdir -p /etc/wolfnet /var/run/wolfnet
2126

22-
COPY wolfnet /usr/local/bin/wolfnet
23-
COPY wolfnetctl /usr/local/bin/wolfnetctl
24-
COPY entrypoint.sh /entrypoint.sh
27+
COPY dist/${TARGETARCH}/wolfnet /usr/local/bin/wolfnet
28+
COPY dist/${TARGETARCH}/wolfnetctl /usr/local/bin/wolfnetctl
29+
COPY docker/entrypoint.sh /entrypoint.sh
2530

2631
RUN chmod +x /usr/local/bin/wolfnet /usr/local/bin/wolfnetctl /entrypoint.sh
2732

wolfnet/docker/entrypoint.sh

Lines changed: 19 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,35 @@
11
#!/bin/bash
22
set -e
33

4-
# ─── WolfNet Docker Entrypoint ───
5-
# Configures WolfNet from environment variables and starts the daemon.
4+
# ─── WolfNet Docker Entrypoint ────────────────────────────────────────────────
5+
# First-run bootstrap, then exec the daemon. The WolfNet binary creates
6+
# config.toml and the private key itself on `init` / `join`, so this script
7+
# just picks which of those to run on first boot.
68

7-
CONFIG_DIR="/etc/wolfnet"
8-
CONFIG_FILE="$CONFIG_DIR/config.toml"
9-
KEY_FILE="$CONFIG_DIR/private.key"
9+
CONFIG="/etc/wolfnet/config.toml"
10+
CONFIG_DIR="$(dirname "$CONFIG")"
1011
STATUS_DIR="/var/run/wolfnet"
1112

1213
mkdir -p "$CONFIG_DIR" "$STATUS_DIR"
1314

14-
# ─── Check TUN device ───
1515
if [ ! -c /dev/net/tun ]; then
16-
echo "ERROR: /dev/net/tun not found."
17-
echo "Run with: --device /dev/net/tun:/dev/net/tun"
18-
echo "If the TUN module is not loaded, run: modprobe tun"
16+
echo " /dev/net/tun not found inside the container."
17+
echo " Run with: --device /dev/net/tun:/dev/net/tun"
18+
echo " If the module is not loaded on the host: sudo modprobe tun"
1919
exit 1
2020
fi
2121

22-
# ─── Generate keys if missing ───
23-
if [ ! -f "$KEY_FILE" ]; then
24-
echo "Generating WolfNet key pair..."
25-
wolfnet genkey > "$KEY_FILE"
26-
chmod 600 "$KEY_FILE"
27-
echo "Key generated. Public key:"
28-
wolfnet pubkey < "$KEY_FILE"
29-
fi
30-
31-
# ─── Handle join token ───
32-
if [ -n "$WOLFNET_JOIN_TOKEN" ] && [ ! -f "$CONFIG_DIR/.joined" ]; then
33-
echo "Joining WolfNet network with invite token..."
34-
if wolfnet join "$WOLFNET_JOIN_TOKEN" --config-dir "$CONFIG_DIR"; then
35-
touch "$CONFIG_DIR/.joined"
36-
echo "Successfully joined. Check 'docker logs wolfnet' for the reverse token."
22+
if [ ! -f "$CONFIG" ]; then
23+
if [ -n "$WOLFNET_JOIN_TOKEN" ]; then
24+
echo "→ Joining WolfNet using supplied invite token..."
25+
wolfnet --config "$CONFIG" join "$WOLFNET_JOIN_TOKEN"
3726
else
38-
echo "WARNING: Join failed. Starting with existing config if available."
39-
fi
40-
fi
41-
42-
# ─── Generate config from env vars if missing ───
43-
if [ ! -f "$CONFIG_FILE" ]; then
44-
echo "Generating config from environment variables..."
45-
ADDR="${WOLFNET_ADDRESS:-10.0.10.1}"
46-
SUBNET="${WOLFNET_SUBNET:-24}"
47-
PORT="${WOLFNET_PORT:-9600}"
48-
IFACE="${WOLFNET_INTERFACE:-wolfnet0}"
49-
GW="${WOLFNET_GATEWAY:-false}"
50-
DISC="${WOLFNET_DISCOVERY:-true}"
51-
HOSTNAME="${WOLFNET_HOSTNAME:-$(hostname)}"
52-
53-
cat > "$CONFIG_FILE" <<EOF
54-
[node]
55-
address = "$ADDR/$SUBNET"
56-
listen_port = $PORT
57-
interface = "$IFACE"
58-
hostname = "$HOSTNAME"
59-
gateway = $GW
60-
61-
[network]
62-
discovery = $DISC
63-
discovery_port = 9601
64-
EOF
65-
66-
# Add static peers if provided (JSON array)
67-
if [ -n "$WOLFNET_PEERS" ]; then
68-
echo "" >> "$CONFIG_FILE"
69-
echo "$WOLFNET_PEERS" | python3 -c "
70-
import json, sys
71-
peers = json.load(sys.stdin)
72-
for p in peers:
73-
print(f'''
74-
[[peers]]
75-
public_key = \"{p.get('public_key', '')}\"
76-
endpoint = \"{p.get('endpoint', '')}\"
77-
allowed_ips = \"{p.get('allowed_ips', '10.0.10.0/24')}\"
78-
''')
79-
" >> "$CONFIG_FILE" 2>/dev/null || true
27+
ADDRESS="${WOLFNET_ADDRESS:-10.0.10.1}"
28+
echo "→ No config and no join token — initialising new network at $ADDRESS"
29+
echo " (run 'docker exec wolfnet wolfnet invite' to get a token for peers)"
30+
wolfnet --config "$CONFIG" init --address "$ADDRESS"
8031
fi
81-
82-
echo "Config generated at $CONFIG_FILE"
8332
fi
8433

85-
echo "Starting WolfNet daemon..."
86-
echo " Config: $CONFIG_FILE"
87-
echo " Interface: ${WOLFNET_INTERFACE:-wolfnet0}"
88-
echo " Port: ${WOLFNET_PORT:-9600}"
89-
90-
exec wolfnet --config "$CONFIG_FILE"
34+
echo "→ Starting WolfNet daemon..."
35+
exec wolfnet --config "$CONFIG" "$@"

0 commit comments

Comments
 (0)