Create Tailscale egress from the cluster #87
Labels
No labels
blocked
bug
duplicate
effort
high
effort
low
effort
medium
effort
minimal
enhancement
help wanted
invalid
pr
feedback
pr
reviewing
question
status
backlog
status
document
status
done
status
implement
status
pending
status
planning
status
reviewing
waiting
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
tepichord/milner#87
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
tepichord referenced this issue2026-09-22 14:12:03 -07:00
Naina:
Overview
Add the cluster's first egress path: an
egressProxyGroup plus anExternalNameService in a newannianamespace, so in-cluster workloads reachnaina-server.faun-snapper.ts.net:4000athttp://llm-gateway.annia.svc.cluster.local:4000. This is the cluster half only — the relay onnaina-serveris #86, and the Service deliberately lives in the consumer namespace so preview namespaces never inherit a live GPU path (story 14).Technical Approach
Create
gitops/clusters/milner/infrastructure/namespaces/annia.yaml— only if it does not already exist (#25 and the enqueuer issue both need it; first to land creates it, the rest cite it).List
namespaces/annia.yamlingitops/clusters/milner/infrastructure/kustomization.yaml(afternamespaces/postgres.yaml).Append a second ProxyGroup to
gitops/clusters/milner/infrastructure/tailscale.yaml.spec.typeis immutable and the only required field;replicasdefaults to 2, so1must be explicit to matchingress-proxies.1234Create
gitops/clusters/milner/apps/annia/llm-gateway.yaml.externalNamecannot be omitted (the API server rejects an empty one); the literal is the human record of intent, not a value that survives reconciliation.56Create
gitops/clusters/milner/apps/annia/kustomization.yaml(mirrorsapps/rabbitmq/kustomization.yaml).Add
anniatogitops/clusters/milner/apps/kustomization.yaml'sresources. Without this the Service never renders; the existingapps.yamlFlux Kustomization picks the directory up unchanged.18Add
docs/tailscale.md§17, "Cluster egress" — ProxyGroup, Service, the full-MagicDNS-name rule,tailnet-ipas the mutually exclusive alternative, the consumer-namespace placement and story 14's reason, and why egress needs no admin approval. Do not claim failover forreplicas: 1.Implementation Details
Files
gitops/clusters/milner/infrastructure/namespaces/annia.yamlgitops/clusters/milner/infrastructure/kustomization.yamlgitops/clusters/milner/infrastructure/tailscale.yamlegress-proxiesgitops/clusters/milner/apps/annia/llm-gateway.yamlgitops/clusters/milner/apps/annia/kustomization.yamlgitops/clusters/milner/apps/kustomization.yamlanniadocs/tailscale.mdtests/test_gitops.pyDependencies — none beyond what is already installed; operator
v1.102.3supportsegressProxyGroups and thetailnet-fqdnpath.78 No RBAC change (the operator's ClusterRole already coversproxygroups,services,endpointslices)91011, no ACL change (tag:k8s-operatoralready ownstag:k8s).12Constraints
tailscale.com/tailnet-fqdnmust be the full MagicDNS name, and exactly one oftailnet-fqdn/tailnet-ipmay be set.13spec.ports; a missing4000fails silently.1415CLUSTER_SCOPED_KINDS.1617tailscalenamespace, not inannia;annia'sbaselinePSA does not admit them.1920egress-proxies.Out-of-scope notes
OPERATIONS.mdrow (Flux-owned state) and noARCHITECTURE.mdchange, per the issue.7 VaultAuths / 11 VaultStaticSecrets) reads only thesecretsandpreview-systemlayers, so ananniaVaultStaticSecret later would not move it.<proxygroup>-<ordinal>=egress-proxies-0, notmilner-egress-proxies-0— the docs'milner-prefix implies an out-of-bandhostnamePrefix/ProxyClassthat grep finds nowhere in-tree. Verify via ProxyGroup status, not a hard-coded name.docs/tailscale.md§3 pins v1.102.3; 1.102.4 exists but is orthogonal to this issue.Acceptance Criteria
infrastructurelayer renders a second ProxyGroup namedegress-proxies,type: egress,replicas: 1.appslayer rendersService/llm-gatewayin namespaceanniawithtype: ExternalName, both annotations, and port4000.annianamespace exists withpod-security.kubernetes.io/enforce: baseline, and is created only if not already present.pytest tests/test_gitops.pypasses, including the new egress-shape guard.kubectl get proxygroup egress-proxies -o jsonpath='{.status.devices[*].hostname}{\"\n\"}').Service/llm-gatewayreportsTailscaleEgressSvcReady=true, and once #86's relay is in place the probe returns401.docs/tailscale.md§17 documents the four rules and the placement rationale.Testing Plan
A connection-refused or timeout means #86's relay/firewall is not in place; that is not an ACL denial.
Related Issues/PRs
naina-server; this is the cluster half).annianamespace; cite, do not duplicate).docs/preview-environment.md§11h —milner.dev/preview-paths; if annia's Service ever declares it, revisit this placement in the same change.Research Findings
spec.typeis the only required ProxyGroup field;egressis a valid enum andreplicasdefaults to2, so the issue's manifest is complete and valid.proxygroups, coreservices, andendpointslicesfor HA egress, and the proxy pod runs in the privilegedtailscalenamespace.spec.externalNamemust be set — the API server rejects an empty one — and the operator overwrites it with its generated ClusterIP FQDN, as the issue states.tailscale.com/proxy-groupandtailscale.com/tailnet-fqdn(the mutually exclusive alternative istailscale.com/tailnet-ip); the operator adds a finalizer and cleans up only on Service deletion.docs/tailscale.md§2.1 already makestag:k8s-operatorowntag:k8s;autoApprovers.servicesis irrelevant because egress advertises nothing.<proxygroup>-<ordinal>(egress-proxies-0), not themilner--prefixed form the docs imply, so verification should read ProxyGroup status rather than assume the prefix.Pre-Mortem
TailscaleEgressSvcReadychecks only pass once the out-of-scope relay onnaina-serverexists, so the issue stalls open or gets closed on trust. A reviewer then misreads the timeout as an ACL/egress defect and "fixes" working manifests.annianamespace and theKustomization.resourcesentry — "create only if absent" cannot be expressed in a committed git manifest, so #25 or the enqueuer adds the same file or a secondnamespaces/annia.yamlline, and the merge leaves a duplicate resource that breakskustomize buildin CI.TailscaleEgressSvcReady=truenever becomes true despite correct YAML — Readiness depends onnaina-server.faun-snapper.ts.net:4000being resolvable and reachable from the egress proxy pods, which is outside the plan's control. The plan offers no fallback assertion that isolates cluster-side correctness, so a non-ready condition is indistinguishable from a manifest mistake.