traefik: demo/app upstream to web is plain http over the public IP; use the tailnet #243

Closed
opened 2026-10-16 17:24:42 -04:00 by jfraeys · 1 comment
Owner

Found 2026-10-16 while answering where Traefik and nginx sit. Improvement, not an incident.

Today

roles/traefik/templates/base.yml.j2: the webapps service (used by demo.jfraeys.com and app.jfraeys.com) forwards to
{{ web_apps_scheme }}://{{ hostvars[groups['web_hosts'][0]]['public_ipv4'] }}:{{ web_apps_port }}, with web_apps_scheme: http and
web_apps_port: 80 (inventory/group_vars/all.yml). So Traefik on services talks plain HTTP to nginx on web over the web node's
public IP.

Problems

  1. The demo/app traffic between the two nodes is unencrypted and crosses public addresses.
  2. The upstream is a raw IP taken from the generated inventory. At the OVH cutover it must follow the web node; in the rehearsal
    it already points at ovh-web (which has no nginx yet, so demo and app return 502 there until the app is deployed).

Not a problem (corrects an earlier remark)

Web's port 80 cannot be closed to the public as a result: the web node serves jfraeys.com / www itself on 80/443 (those records
point at it directly through Cloudflare). The gain is encryption and stability, not a smaller exposed surface.

Options

  • Upstream over the tailnet: the web node's tailnet IP, or the MagicDNS name web.ts.jfraeys.com (issues #241/#242). The name follows the
    Headscale node name, so the cutover name swap moves it automatically. Catch: servers join with --accept-dns=false and Traefik runs in
    Docker, so a name does not resolve there today; the tailnet IP can come from tailscale ip -4 on the web host (a fact gathered by the role).
  • Keep http over the tailnet (WireGuard already encrypts it) and keep nginx's own TLS only for Cloudflare-facing traffic.
  • Check the Headscale ACL allows tag:services to reach tag:web:80.

Done when

webapps points at the web node over the tailnet, demo and app still work, and the runbook's cutover steps no longer need to mention this upstream.

Found 2026-10-16 while answering where Traefik and nginx sit. Improvement, not an incident. ## Today `roles/traefik/templates/base.yml.j2`: the `webapps` service (used by `demo.jfraeys.com` and `app.jfraeys.com`) forwards to `{{ web_apps_scheme }}://{{ hostvars[groups['web_hosts'][0]]['public_ipv4'] }}:{{ web_apps_port }}`, with `web_apps_scheme: http` and `web_apps_port: 80` (`inventory/group_vars/all.yml`). So Traefik on `services` talks plain HTTP to nginx on `web` over the web node's **public** IP. ## Problems 1. The demo/app traffic between the two nodes is unencrypted and crosses public addresses. 2. The upstream is a raw IP taken from the generated inventory. At the OVH cutover it must follow the web node; in the rehearsal it already points at `ovh-web` (which has no nginx yet, so demo and app return 502 there until the app is deployed). ## Not a problem (corrects an earlier remark) Web's port 80 cannot be closed to the public as a result: the web node serves `jfraeys.com` / `www` itself on 80/443 (those records point at it directly through Cloudflare). The gain is encryption and stability, not a smaller exposed surface. ## Options - Upstream over the tailnet: the web node's tailnet IP, or the MagicDNS name `web.ts.jfraeys.com` (issues #241/#242). The name follows the Headscale node name, so the cutover name swap moves it automatically. Catch: servers join with `--accept-dns=false` and Traefik runs in Docker, so a name does not resolve there today; the tailnet IP can come from `tailscale ip -4` on the web host (a fact gathered by the role). - Keep http over the tailnet (WireGuard already encrypts it) and keep nginx's own TLS only for Cloudflare-facing traffic. - Check the Headscale ACL allows `tag:services` to reach `tag:web:80`. ## Done when `webapps` points at the web node over the tailnet, demo and app still work, and the runbook's cutover steps no longer need to mention this upstream.
Author
Owner

Implemented in #244 (merged) and verified after the deploy: Traefik's webapps upstream is http://100.64.0.1:80 (the web node's tailnet address); demo.jfraeys.com and app.jfraeys.com answer as before; web's nginx log shows source 100.64.0.3 and no new requests from the services node's public address. Fallback to the public address and web_apps_use_tailnet: false remain available.

Implemented in #244 (merged) and verified after the deploy: Traefik's webapps upstream is http://100.64.0.1:80 (the web node's tailnet address); demo.jfraeys.com and app.jfraeys.com answer as before; web's nginx log shows source 100.64.0.3 and no new requests from the services node's public address. Fallback to the public address and web_apps_use_tailnet: false remain available.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jfraeys/infra#243
No description provided.