feat(cloudflared): publish the kube-apiserver at k8s.riotpiao.com #88

Open
rock wants to merge 1 commits from feat/k8s-api-public-tunnel into main
Owner

Why

The apiserver now authenticates against ZITADEL (PR #87, applied and verified), so kubectl can work from outside the LAN without a client certificate. This publishes it through the existing Cloudflare Tunnel.

Two deliberate details

  1. Origin is the in-cluster Service (kubernetes.default.svc.cluster.local:443), not a node IP. That survives a control-plane address change and lets each connector reach a local-healthy apiserver.

  2. originRequest.noTLSVerify: true is required. The apiserver presents a certificate for the cluster CA plus its node addresses, so its SANs cannot match this public hostname. Cloudflare terminates the public TLS with the *.riotpiao.com edge certificate and the connector hop is already inside the cluster. Scoped to this one rule.

Port

Cloudflare Tunnel hostname rules serve on 443 and do not proxy arbitrary ports. kubeconfig must use https://k8s.riotpiao.com with no :6443.

Already done out-of-band

  • DNS CNAME k8s.riotpiao.com → 32d0bf93-….cfargotunnel.com, proxied (created via the Cloudflare API, verified resolving).
  • The hostname is not yet reachable because this ConfigMap change is uncommitted and ArgoCD self-heal reverted the direct apply.

Security — please read

This exposes the Kubernetes API to the internet. Authentication remains ZITADEL OIDC plus client certificates, so an unauthenticated caller gets 401. The residual risk is scanner noise and DoS, not auth bypass.

Recommended follow-up: a Cloudflare Access application in front of k8s.riotpiao.com. Trade-off worth knowing before you choose:

  • Access with a service token is the secure option, but kubectl cannot send arbitrary headers, so it needs either a local cloudflared access proxy or a kubeconfig proxy-url.
  • Access with browser SSO does not fit a CLI client well.

So there is a real convenience/security trade. My read: enable Access if this hostname will be internet-facing long-term; otherwise keep the OIDC requirement alone and accept the scanner traffic.

## Why The apiserver now authenticates against ZITADEL (PR #87, applied and verified), so `kubectl` can work from outside the LAN without a client certificate. This publishes it through the existing Cloudflare Tunnel. ## Two deliberate details 1. **Origin is the in-cluster Service** (`kubernetes.default.svc.cluster.local:443`), not a node IP. That survives a control-plane address change and lets each connector reach a local-healthy apiserver. 2. **`originRequest.noTLSVerify: true` is required.** The apiserver presents a certificate for the cluster CA plus its node addresses, so its SANs cannot match this public hostname. Cloudflare terminates the public TLS with the `*.riotpiao.com` edge certificate and the connector hop is already inside the cluster. Scoped to this one rule. ## Port Cloudflare Tunnel hostname rules serve on **443** and do not proxy arbitrary ports. kubeconfig must use `https://k8s.riotpiao.com` with **no `:6443`**. ## Already done out-of-band - DNS CNAME `k8s.riotpiao.com` → `32d0bf93-….cfargotunnel.com`, proxied (created via the Cloudflare API, verified resolving). - The hostname is not yet reachable because this ConfigMap change is uncommitted and ArgoCD self-heal reverted the direct apply. ## Security — please read This exposes the Kubernetes API to the internet. Authentication remains ZITADEL OIDC plus client certificates, so an unauthenticated caller gets 401. The residual risk is scanner noise and DoS, not auth bypass. **Recommended follow-up: a Cloudflare Access application in front of `k8s.riotpiao.com`.** Trade-off worth knowing before you choose: - Access with a **service token** is the secure option, but `kubectl` cannot send arbitrary headers, so it needs either a local `cloudflared access` proxy or a kubeconfig `proxy-url`. - Access with **browser SSO** does not fit a CLI client well. So there is a real convenience/security trade. My read: enable Access if this hostname will be internet-facing long-term; otherwise keep the OIDC requirement alone and accept the scanner traffic.
rock added 1 commit 2026-10-31 22:10:52 +00:00
Adds a tunnel ingress rule so kubectl works from outside the LAN now that the
apiserver authenticates against ZITADEL.

Two deliberate details:

1. The origin is the in-cluster Service
   (kubernetes.default.svc.cluster.local:443), not a node IP. That survives a
   control-plane address change and lets each connector reach a local-healthy
   apiserver.

2. originRequest.noTLSVerify is required. The apiserver presents a certificate
   for the cluster CA plus its node addresses, so its SANs cannot match this
   public hostname. Cloudflare terminates the public TLS with the *.riotpiao.com
   edge certificate and the connector hop is already inside the cluster, so
   skipping verification on that short hop is the least-bad option. This is
   scoped to this one rule; do not copy it elsewhere.

The hostname is served on 443. Cloudflare Tunnel hostname rules do not proxy
arbitrary ports, so kubeconfig must use https://k8s.riotpiao.com with no :6443.

SECURITY: this exposes the Kubernetes API to the internet. Authentication is
still ZITADEL OIDC plus client certificates, but a Cloudflare Access application
belongs in front of the hostname. That is a separate change and is not included
here.
You are not authorized to merge this pull request.
This pull request can be merged automatically.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/k8s-api-public-tunnel:feat/k8s-api-public-tunnel
git checkout feat/k8s-api-public-tunnel
Sign in to join this conversation.