diff --git a/.env.example b/.env.example index f3ce3f3..892bef9 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ # Copy to .env, set values, and never commit .env. -# Used by docker-compose-macmini.yml and docker-compose-logos.yml for tunnel HTTP Basic auth. +# Used by archive/docker-compose-macmini.yml and archive/docker-compose-logos.yml for tunnel HTTP Basic auth. TUNNEL_AUTH_USER= TUNNEL_AUTH_PASS= diff --git a/README.md b/README.md index 12be2ea..7007b17 100644 --- a/README.md +++ b/README.md @@ -134,10 +134,12 @@ Set `TUNNEL_AUTH_USER` / `TUNNEL_AUTH_PASS` via an env file; never commit passwo For WebDAV stacks (macmini, logos) and any client using HTTP Basic Auth at the tunnel: copy `.env.example` to `.env`, set `TUNNEL_AUTH_USER` and `TUNNEL_AUTH_PASS`, and **never commit `.env`**. Compose files reference `${TUNNEL_AUTH_USER}` and `${TUNNEL_AUTH_PASS}`; Docker Compose reads `.env` from the project directory automatically. -#### WebDAV stacks (macmini / logos) +#### WebDAV stacks (macmini / logos) — in `archive/` -- **Macmini:** `docker compose -f docker-compose-macmini.yml up -d --build` — ensure `.env` has tunnel auth set. -- **Logos:** `docker compose -f docker-compose-logos.yml -p logos up -d --build` — same `.env` or separate; images/SVG-only uploads. +- **Macmini:** from repo root: `docker compose -f archive/docker-compose-macmini.yml up -d --build` — ensure `.env` has tunnel auth set. +- **Logos:** `docker compose -f archive/docker-compose-logos.yml -p logos up -d --build` — same `.env` or separate; images/SVG-only uploads. + +See `archive/README.md` for other archived files (Woodpecker CI, reference Traefik YAML, old root binaries). ## Generated Traefik Config @@ -164,7 +166,7 @@ http: ## Building ```bash -# Build both binaries locally (commit and push these for remote deployment) +# Build both binaries locally go build -o tunnel-server ./cmd/server/ go build -o tunnel-client ./cmd/client/ @@ -173,9 +175,11 @@ docker compose build # server image docker build --target client -t tunnel-client . # client image ``` +Release to GitHub + GHCR (multi-arch): `./ship.sh "your message"` (requires `gh` with `write:packages` and git remote `github`). + ## Binaries and systemd (bare metal) -The repo ships compiled `tunnel-server` and `tunnel-client` for remote hosts that run without Docker. Use the included systemd units under `systemd/`. +Build `tunnel-server` / `tunnel-client` with `go build` (or use `./ship.sh`, which writes `dist/`). Use the included systemd units under `systemd/`. **Keys:** Do not reuse the host’s SSH keys or share one key between hosts or tunnels. Generate a dedicated ed25519 key per tunnel (or per host). Add that key’s **public** half to the **tunnel** user’s `authorized_keys` on **ingress.nixc.us** (the reverse tunnel server). @@ -207,10 +211,10 @@ sudo systemctl daemon-reload && sudo systemctl enable --now tunnel-client ### Client on a remote host (clone or copy) -1. Install the binary (from repo clone or raw URL above; repo has `client`/`server`): +1. Install the binary (from repo clone, `go build`, `dist/tunnel-client` after `./ship.sh`, or raw URL): ```bash - sudo cp client /usr/local/bin/tunnel-client && sudo chmod +x /usr/local/bin/tunnel-client + sudo cp dist/tunnel-client /usr/local/bin/tunnel-client && sudo chmod +x /usr/local/bin/tunnel-client ``` 2. Copy the systemd unit and create env file: diff --git a/archive/README.md b/archive/README.md new file mode 100644 index 0000000..29a287e --- /dev/null +++ b/archive/README.md @@ -0,0 +1,14 @@ +# Archive (review / optional stacks) + +Items here are **not** required for the core tunnel server and client (`cmd/server`, `cmd/client`). + +| Item | Purpose | +|------|---------| +| `cmd-webdav/` | WebDAV app source (logos / macmini stacks) | +| `webdav/` | Dockerfile + entrypoint for that app | +| `docker-compose-macmini.yml` | Mac mini WebDAV + tunnel (run from repo root: `-f archive/docker-compose-macmini.yml`) | +| `docker-compose-logos.yml` | Logos WebDAV + tunnel | +| `woodpecker.yml` | CI pipeline (was `.woodpecker.yml` at repo root) | +| `traefik-reference.yml` | Sample Traefik stack | +| `test-index.html` | Used by `docker-compose.test.yml` in repo root | +| `client`, `server` | Old committed macOS binaries (prefer `go build` or Docker images) | diff --git a/client b/archive/client similarity index 100% rename from client rename to archive/client diff --git a/cmd/webdav/filter.go b/archive/cmd-webdav/filter.go similarity index 100% rename from cmd/webdav/filter.go rename to archive/cmd-webdav/filter.go diff --git a/cmd/webdav/main.go b/archive/cmd-webdav/main.go similarity index 100% rename from cmd/webdav/main.go rename to archive/cmd-webdav/main.go diff --git a/docker-compose-logos.yml b/archive/docker-compose-logos.yml similarity index 85% rename from docker-compose-logos.yml rename to archive/docker-compose-logos.yml index 0d9850a..230350c 100644 --- a/docker-compose-logos.yml +++ b/archive/docker-compose-logos.yml @@ -2,7 +2,7 @@ # Server (ingress) must be configured for this domain and key. # # Auth: set TUNNEL_AUTH_USER and TUNNEL_AUTH_PASS in .env (see .env.example); never commit .env. -# Deploy (separate from macmini): docker compose -f docker-compose-logos.yml -p logos up -d --build +# Deploy (separate from macmini): docker compose -f archive/docker-compose-logos.yml -p logos up -d --build # # Connect in Finder: Go → Connect to Server → https://logos.nixc.us # Only image and SVG files can be uploaded (company logos). @@ -10,8 +10,8 @@ services: webdav: build: - context: . - dockerfile: webdav/Dockerfile + context: .. + dockerfile: archive/webdav/Dockerfile restart: always environment: WEBDAV_IMAGES_ONLY: "1" diff --git a/docker-compose-macmini.yml b/archive/docker-compose-macmini.yml similarity index 95% rename from docker-compose-macmini.yml rename to archive/docker-compose-macmini.yml index 8d846d6..09b3c77 100644 --- a/docker-compose-macmini.yml +++ b/archive/docker-compose-macmini.yml @@ -12,8 +12,8 @@ services: webdav: build: - context: . - dockerfile: webdav/Dockerfile + context: .. + dockerfile: archive/webdav/Dockerfile restart: always volumes: - ${HOME}/dev/piconfigurator/bin:/data diff --git a/server b/archive/server similarity index 100% rename from server rename to archive/server diff --git a/test-index.html b/archive/test-index.html similarity index 100% rename from test-index.html rename to archive/test-index.html diff --git a/traefik-reference.yml b/archive/traefik-reference.yml similarity index 100% rename from traefik-reference.yml rename to archive/traefik-reference.yml diff --git a/webdav/Dockerfile b/archive/webdav/Dockerfile similarity index 59% rename from webdav/Dockerfile rename to archive/webdav/Dockerfile index 5bff958..da16ab9 100644 --- a/webdav/Dockerfile +++ b/archive/webdav/Dockerfile @@ -1,14 +1,14 @@ -# Build from repo root so go.mod and cmd/webdav are available (build context: .). +# Build context must be the repository root (see archive/docker-compose-*.yml). FROM golang:1.24-alpine AS build WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN go build -o /webdav ./cmd/webdav +RUN go build -o /webdav ./archive/cmd-webdav FROM alpine:3.20 RUN apk add --no-cache ca-certificates -COPY webdav/entrypoint.sh /entrypoint.sh +COPY archive/webdav/entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh COPY --from=build /webdav /webdav EXPOSE 80 diff --git a/webdav/entrypoint.sh b/archive/webdav/entrypoint.sh similarity index 100% rename from webdav/entrypoint.sh rename to archive/webdav/entrypoint.sh diff --git a/.woodpecker.yml b/archive/woodpecker.yml similarity index 100% rename from .woodpecker.yml rename to archive/woodpecker.yml diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 6b5c205..86f64ed 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -6,7 +6,7 @@ services: image: nginx:alpine restart: always volumes: - - ./test-index.html:/usr/share/nginx/html/index.html:ro + - ./archive/test-index.html:/usr/share/nginx/html/index.html:ro expose: - "80" diff --git a/go.mod b/go.mod index a980bdb..8278c1f 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ go 1.24.0 toolchain go1.24.13 -require golang.org/x/crypto v0.48.0 - require ( - golang.org/x/net v0.50.0 // indirect - golang.org/x/sys v0.41.0 // indirect + golang.org/x/crypto v0.48.0 + golang.org/x/net v0.50.0 ) + +require golang.org/x/sys v0.41.0 // indirect diff --git a/go.sum b/go.sum index 31319b3..655e4db 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,8 @@ -golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= -golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= -golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= diff --git a/internal/buildinfo/buildinfo.go b/internal/buildinfo/buildinfo.go new file mode 100644 index 0000000..bb9b592 --- /dev/null +++ b/internal/buildinfo/buildinfo.go @@ -0,0 +1,5 @@ +// Package buildinfo holds values set at link time (-ldflags -X). +package buildinfo + +// Commit is the git SHA baked in by Docker / ship.sh. +var Commit = "unknown" diff --git a/ship.sh b/ship.sh new file mode 100755 index 0000000..2571a3b --- /dev/null +++ b/ship.sh @@ -0,0 +1,121 @@ +#!/usr/bin/env bash +# Reliable release: commit → local binaries → multi-arch images → GHCR → git push. +# Only input: commit message. Edit CONSTANTS to change registry/platforms. +# +# Usage: ./ship.sh "your message" + +set -euo pipefail + +if [[ $# -lt 1 ]] || [[ -z "${1// }" ]]; then + echo "usage: $0 " >&2 + exit 1 +fi +COMMIT_MSG="$*" + +ROOT="$(cd "$(dirname "$0")" && pwd)" +cd "$ROOT" + +# --- CONSTANTS --- +GITHUB_OWNER="Leopere" +GITHUB_REPO="better-argo-tunnels" +REGISTRY_BASE="ghcr.io/leopere/better-argo-tunnels" +TAG_SERVER="production" +TAG_CLIENT="client-production" +PLATFORMS="linux/amd64,linux/arm64,linux/arm/v7" +GIT_REMOTE="github" +# --- end --- + +for cmd in gh docker git go; do + command -v "$cmd" >/dev/null 2>&1 || { + echo "error: need $cmd in PATH" >&2 + exit 1 + } +done + +BRANCH="$(git branch --show-current)" +if [[ -z "$BRANCH" ]]; then + echo "error: detached HEAD" >&2 + exit 1 +fi + +echo "==> git add -A" +git add -A +if git diff --cached --quiet; then + echo "error: nothing to commit" >&2 + exit 1 +fi + +echo "==> git commit" +git commit -m "$COMMIT_MSG" + +GIT_COMMIT="$(git rev-parse HEAD)" +BA=(--build-arg "GIT_COMMIT=$GIT_COMMIT") +LDFLAGS="-s -w -X github.com/nixc/reverse-ssh-traefik/internal/buildinfo.Commit=${GIT_COMMIT}" + +echo "==> go build → dist/" +mkdir -p dist +CGO_ENABLED=0 go build -ldflags="$LDFLAGS" -o dist/tunnel-server ./cmd/server/ +CGO_ENABLED=0 go build -ldflags="$LDFLAGS" -o dist/tunnel-client ./cmd/client/ + +pick_builder() { + if docker buildx inspect orbstack >/dev/null 2>&1; then + echo "orbstack" + else + echo "default" + fi +} + +platform_slug() { + local p="${1// /}" + p="${p#linux/}" + echo "${p//\//-}" +} + +push_multi() { + local target="$1" + local final_tag="$2" + local builder="$3" + local base="${final_tag%:*}" + local name="${final_tag##*:}" + local arch_refs=() + + IFS=',' read -ra plats <<< "$PLATFORMS" + for p in "${plats[@]}"; do + p="${p// /}" + [[ -z "$p" ]] && continue + local slug + slug="$(platform_slug "$p")" + local arch_tag="${base}:${name}-${slug}" + echo "==> docker buildx $target $p → $arch_tag" + docker buildx build \ + "${BA[@]}" \ + --builder "$builder" \ + --platform "$p" \ + --target "$target" \ + -t "$arch_tag" \ + --push \ + "$ROOT" + arch_refs+=("$arch_tag") + done + docker buildx imagetools create -t "$final_tag" "${arch_refs[@]}" +} + +BUILDER="$(pick_builder)" +SERVER_TAG="${REGISTRY_BASE}:${TAG_SERVER}" +CLIENT_TAG="${REGISTRY_BASE}:${TAG_CLIENT}" + +echo "==> ghcr.io login (needs gh token with write:packages)" +echo "$(gh auth token)" | docker login ghcr.io -u "$GITHUB_OWNER" --password-stdin + +push_multi server "$SERVER_TAG" "$BUILDER" +push_multi client "$CLIENT_TAG" "$BUILDER" + +if ! git remote get-url "$GIT_REMOTE" >/dev/null 2>&1; then + echo "error: git remote '$GIT_REMOTE' missing" >&2 + exit 1 +fi +echo "==> git push $GIT_REMOTE $BRANCH" +git push "$GIT_REMOTE" "$BRANCH" + +echo "==> ok commit=$GIT_COMMIT" +echo " $SERVER_TAG $CLIENT_TAG"