From a2fd688865deaa9bcec87e4487274620e29e9594 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sun, 25 Jan 2026 11:20:04 -0800 Subject: [PATCH 1/4] Add Caddy layer4 support for Forgejo SSH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add layer4 TCP proxy configuration to Caddyfile template - Configure SSH service on port 2222 → localhost:2200 (Forgejo) - Switch HTTPS port from 8443 (testing) to 443 (production) - Requires Caddy rebuilt with github.com/mholt/caddy-l4 plugin This enables git+ssh access via forge.ops.eblu.me:2222, accessible from tailnet clients, docker containers, and k8s pods alike. Co-Authored-By: Claude Opus 4.5 --- ansible/roles/caddy/defaults/main.yml | 11 ++++++++--- ansible/roles/caddy/templates/Caddyfile.j2 | 13 +++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ansible/roles/caddy/defaults/main.yml b/ansible/roles/caddy/defaults/main.yml index 3e1ad0c..8490b54 100644 --- a/ansible/roles/caddy/defaults/main.yml +++ b/ansible/roles/caddy/defaults/main.yml @@ -15,9 +15,8 @@ caddy_gandi_token_file: /Users/erichblume/.config/caddy/gandi-token # Domain configuration caddy_domain: ops.eblu.me -# Listen on Tailscale interface only (port 443) -# Use 8443 during testing to avoid conflicts with Tailscale serve -caddy_https_port: 8443 +# HTTPS port (443 is standard) +caddy_https_port: 443 # Services to proxy # Format: { name: "service", host: "hostname", backend: "url" } @@ -35,3 +34,9 @@ caddy_services: # - name: grafana # host: "grafana.{{ caddy_domain }}" # backend: "http://minikube-ip:nodeport" + +# SSH services (Layer 4 TCP proxy) +# Format: { port: external_port, backend: "host:port" } +caddy_ssh_services: + - port: 2222 + backend: "localhost:2200" # Forgejo SSH diff --git a/ansible/roles/caddy/templates/Caddyfile.j2 b/ansible/roles/caddy/templates/Caddyfile.j2 index 455b49c..36c1c8d 100644 --- a/ansible/roles/caddy/templates/Caddyfile.j2 +++ b/ansible/roles/caddy/templates/Caddyfile.j2 @@ -7,6 +7,19 @@ { # Global options admin off + +{% if caddy_ssh_services %} + # Layer 4 (TCP) routing for SSH services + layer4 { +{% for ssh_svc in caddy_ssh_services %} + :{{ ssh_svc.port }} { + route { + proxy {{ ssh_svc.backend }} + } + } +{% endfor %} + } +{% endif %} } # Wildcard certificate for all services -- 2.50.1 (Apple Git-155) From 1c5fcd15d8156e1d4958e16e07fdf1362caeb713 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sun, 25 Jan 2026 11:32:14 -0800 Subject: [PATCH 2/4] Update Forgejo config to use new ops.eblu.me domain - Change domain from forge.tail8d86e.ts.net to forge.ops.eblu.me - Update SSH_PORT from 22 to 2222 (external port via Caddy L4) Co-Authored-By: Claude Opus 4.5 --- ansible/roles/forgejo/defaults/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/forgejo/defaults/main.yml b/ansible/roles/forgejo/defaults/main.yml index 23396e8..95ffda8 100644 --- a/ansible/roles/forgejo/defaults/main.yml +++ b/ansible/roles/forgejo/defaults/main.yml @@ -18,7 +18,7 @@ forgejo_log_path: "{{ forgejo_work_path }}/log" # Server settings forgejo_http_addr: 0.0.0.0 forgejo_http_port: 3001 -forgejo_domain: forge.tail8d86e.ts.net +forgejo_domain: forge.ops.eblu.me forgejo_ssh_domain: "{{ forgejo_domain }}" forgejo_root_url: "https://{{ forgejo_domain }}/" forgejo_offline_mode: true @@ -27,7 +27,7 @@ forgejo_offline_mode: true forgejo_disable_ssh: false forgejo_start_ssh_server: true forgejo_builtin_ssh_user: forgejo -forgejo_ssh_port: 22 +forgejo_ssh_port: 2222 forgejo_ssh_listen_port: 2200 forgejo_lfs_start_server: true -- 2.50.1 (Apple Git-155) From 8be959929c6b3b70354db5342c9a8bc03801111b Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sun, 25 Jan 2026 11:35:05 -0800 Subject: [PATCH 3/4] Update all forge references to use ops.eblu.me domain - Update CLAUDE.md mirror location - Update ansible managed header to use new SSH URL with port 2222 - Update Brewfile comment - Update alloy build instructions - Update mise tasks (pr-comments, indri-runner-logs, indri-services-check, container-tag-and-release) - Update nettest connectivity script - Mark tailscale-operator egress-forge as deprecated (pods can now reach forge directly via Caddy) Co-Authored-By: Claude Opus 4.5 --- Brewfile | 2 +- CLAUDE.md | 2 +- ansible/group_vars/all.yml | 2 +- ansible/roles/alloy/defaults/main.yml | 2 +- argocd/manifests/tailscale-operator/README.md | 4 ++-- argocd/manifests/tailscale-operator/egress-forge.yaml | 9 ++++++--- containers/nettest/test-connectivity.sh | 4 ++-- mise-tasks/container-tag-and-release | 2 +- mise-tasks/indri-runner-logs | 2 +- mise-tasks/indri-services-check | 2 +- mise-tasks/pr-comments | 2 +- 11 files changed, 18 insertions(+), 15 deletions(-) diff --git a/Brewfile b/Brewfile index 64592c8..5aa1402 100644 --- a/Brewfile +++ b/Brewfile @@ -2,5 +2,5 @@ brew "actionlint" # GitHub/Forgejo Actions workflow linter brew "argocd" # ArgoCD CLI for GitOps management brew "bat" # Syntax-highlighted file concatenation -brew "tea" # Gitea/Forgejo CLI for forge.tail8d86e.ts.net +brew "tea" # Gitea/Forgejo CLI for forge.ops.eblu.me brew "podman" # Container CLI (uses VM on macOS, for building/pushing images) diff --git a/CLAUDE.md b/CLAUDE.md index e5c0a50..839cced 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -143,7 +143,7 @@ mise run container-release runner v1.0.0 # Tag and trigger build workflow ## Third-Party Projects When a task requires cloning or using a third-party git repository (e.g., for building from source), **ask the user to mirror it on forge first**, then clone from the mirror: -- Mirror location: `https://forge.tail8d86e.ts.net/eblume/.git` +- Mirror location: `https://forge.ops.eblu.me/eblume/.git` - Clone to: `~/code/3rd//` This avoids external dependencies and ensures the project is available even if the upstream is unreachable. diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml index 4559aef..a9f303d 100644 --- a/ansible/group_vars/all.yml +++ b/ansible/group_vars/all.yml @@ -1,2 +1,2 @@ --- -ansible_managed: "Managed by ansible - do not edit. Source: ssh://forgejo@forge.tail8d86e.ts.net/eblume/blumeops.git" +ansible_managed: "Managed by ansible - do not edit. Source: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git" diff --git a/ansible/roles/alloy/defaults/main.yml b/ansible/roles/alloy/defaults/main.yml index afb14e7..4f0b777 100644 --- a/ansible/roles/alloy/defaults/main.yml +++ b/ansible/roles/alloy/defaults/main.yml @@ -10,7 +10,7 @@ # Build on dev machine (gilbert), then copy to indri: # # 1. Clone from forge mirror: -# git clone ssh://forgejo@forge.tail8d86e.ts.net/eblume/alloy.git ~/code/3rd/alloy +# git clone ssh://forgejo@forge.ops.eblu.me:2222/eblume/alloy.git ~/code/3rd/alloy # # 2. Set up build tools via mise: # cd ~/code/3rd/alloy && mise use go@1.25 node yarn diff --git a/argocd/manifests/tailscale-operator/README.md b/argocd/manifests/tailscale-operator/README.md index 44c5089..ee8ce1e 100644 --- a/argocd/manifests/tailscale-operator/README.md +++ b/argocd/manifests/tailscale-operator/README.md @@ -86,5 +86,5 @@ kubectl logs -n tailscale -l app.kubernetes.io/name=operator annotations: tailscale.com/proxy-class: "default" ``` -- The egress proxy for forge targets `indri.tail8d86e.ts.net` directly (not `forge.tail8d86e.ts.net`) - because Tailscale Serve hostnames are virtual and only work via the Tailscale client. +- The egress proxy for forge is **deprecated**. Forge is now accessible via Caddy at + `forge.ops.eblu.me` (HTTPS) and `forge.ops.eblu.me:2222` (SSH), which pods can reach directly. diff --git a/argocd/manifests/tailscale-operator/egress-forge.yaml b/argocd/manifests/tailscale-operator/egress-forge.yaml index 8705eea..4dc982b 100644 --- a/argocd/manifests/tailscale-operator/egress-forge.yaml +++ b/argocd/manifests/tailscale-operator/egress-forge.yaml @@ -1,7 +1,10 @@ -# Egress proxy to expose Forgejo (forge) to the cluster -# Forge runs on indri:3001, exposed via Tailscale Serve as forge.tail8d86e.ts.net -# We target indri directly since egress can't reach Tailscale Serve hostnames +# DEPRECATED: This egress proxy is no longer needed. +# Forge is now accessible via Caddy at forge.ops.eblu.me (HTTPS) and +# forge.ops.eblu.me:2222 (SSH), which pods can reach directly. # +# Keeping this file for reference during migration. Remove once verified. +# +# Original purpose: Egress proxy to expose Forgejo (forge) to the cluster # See: https://tailscale.com/kb/1438/kubernetes-operator-cluster-egress --- apiVersion: v1 diff --git a/containers/nettest/test-connectivity.sh b/containers/nettest/test-connectivity.sh index d1fb6e4..e97f417 100644 --- a/containers/nettest/test-connectivity.sh +++ b/containers/nettest/test-connectivity.sh @@ -14,8 +14,8 @@ echo "Hostname: $(hostname)" echo "" # Test targets -FORGE_HOST="forge.tail8d86e.ts.net" -REGISTRY_HOST="registry.tail8d86e.ts.net" +FORGE_HOST="forge.ops.eblu.me" +REGISTRY_HOST="registry.ops.eblu.me" test_dns() { local host="$1" diff --git a/mise-tasks/container-tag-and-release b/mise-tasks/container-tag-and-release index 6f37d4e..2b3653e 100755 --- a/mise-tasks/container-tag-and-release +++ b/mise-tasks/container-tag-and-release @@ -71,4 +71,4 @@ echo "The workflow will now build and push:" echo " registry.tail8d86e.ts.net/$IMAGE:$VERSION" echo "" echo "Monitor the build at:" -echo " https://forge.tail8d86e.ts.net/eblume/blumeops/actions" +echo " https://forge.ops.eblu.me/eblume/blumeops/actions" diff --git a/mise-tasks/indri-runner-logs b/mise-tasks/indri-runner-logs index 0240405..a61454a 100755 --- a/mise-tasks/indri-runner-logs +++ b/mise-tasks/indri-runner-logs @@ -12,7 +12,7 @@ if [[ -z "$RUN_ID" ]]; then echo "Only works for runs executed by the indri-host-runner." echo "" echo "Recent runs:" - curl -sf "https://forge.tail8d86e.ts.net/api/v1/repos/eblume/blumeops/actions/tasks" | \ + curl -sf "https://forge.ops.eblu.me/api/v1/repos/eblume/blumeops/actions/tasks" | \ jq -r '.workflow_runs[:10] | .[] | " \(.id)\t\(.status)\t\(.workflow_id)\t\(.display_title | .[0:50])"' exit 1 fi diff --git a/mise-tasks/indri-services-check b/mise-tasks/indri-services-check index a6b6944..f89d506 100755 --- a/mise-tasks/indri-services-check +++ b/mise-tasks/indri-services-check @@ -70,7 +70,7 @@ check_http "Prometheus" "https://prometheus.tail8d86e.ts.net/-/healthy" check_http "Loki" "https://loki.tail8d86e.ts.net/ready" check_http "Grafana" "https://grafana.tail8d86e.ts.net/api/health" check_http "ArgoCD" "https://argocd.tail8d86e.ts.net/healthz" -check_http "Forgejo" "https://forge.tail8d86e.ts.net/" +check_http "Forgejo" "https://forge.ops.eblu.me/" check_http "Zot Registry" "https://registry.tail8d86e.ts.net/v2/_catalog" check_http "Kiwix" "https://kiwix.tail8d86e.ts.net/" check_http "Miniflux" "https://feed.tail8d86e.ts.net/healthcheck" diff --git a/mise-tasks/pr-comments b/mise-tasks/pr-comments index be8d25b..933f432 100755 --- a/mise-tasks/pr-comments +++ b/mise-tasks/pr-comments @@ -20,7 +20,7 @@ import httpx from rich.console import Console from rich.text import Text -FORGE_API_BASE = "https://forge.tail8d86e.ts.net/api/v1" +FORGE_API_BASE = "https://forge.ops.eblu.me/api/v1" REPO_OWNER = "eblume" REPO_NAME = "blumeops" -- 2.50.1 (Apple Git-155) From f7cc30850bedc648cb8f2cea4e35af65f2d90c16 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sun, 25 Jan 2026 11:36:46 -0800 Subject: [PATCH 4/4] Remove forge from tailscale_serve role Forge has been migrated to Caddy at forge.ops.eblu.me. Registry remains on tailscale serve until migrated. Co-Authored-By: Claude Opus 4.5 --- ansible/roles/tailscale_serve/defaults/main.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/ansible/roles/tailscale_serve/defaults/main.yml b/ansible/roles/tailscale_serve/defaults/main.yml index 6e7ab1d..d2cf58a 100644 --- a/ansible/roles/tailscale_serve/defaults/main.yml +++ b/ansible/roles/tailscale_serve/defaults/main.yml @@ -1,16 +1,11 @@ --- # Tailscale serve configuration for this host # Each service maps a Tailscale service name to local endpoints +# +# NOTE: forge has been migrated to Caddy (forge.ops.eblu.me) +# Registry will be migrated next, then this role can be retired. tailscale_serve_services: - - name: svc:forge - https: - port: 443 - upstream: http://localhost:3001 - tcp: - port: 22 - upstream: tcp://localhost:2200 - - name: svc:registry https: port: 443 -- 2.50.1 (Apple Git-155)