Tailscale: MagicDNS Detail

Level 3 (Detail) — Naming contracts, .ts.net domain, workload DNS resolution.

Concept

Tailscale MagicDNS assigns .ts.net hostnames to every device on the tailnet. The tailnet for TazLab is magellanic-gondola.ts.net, so every device gets a FQDN like <device-name>.magellanic-gondola.ts.net.

Current Device Names

DeviceMagicDNS FQDNTags
lushycorp-vaultlushycorp-vault.magellanic-gondola.ts.nettag:tazlab-vault, tag:vault-api
tazlab-k8s-control-plane-01tazlab-k8s-control-plane-01.magellanic-gondola.ts.nettag:tazlab-k8s
TazPod (operator)ephemeral, no stable nametag:tazpod

Naming Evolution

PhaseNameTypeStatus
Originallushycorp-vm.ts.tazlab.netMagicDNS (device name)Superseded
Custom aliaslushycorp-api.ts.tazlab.netRuntime TLS configDebt (TD-020), retired
Currentlushycorp-vaultMagicDNS (device name)Active
Current FQDNlushycorp-vault.magellanic-gondola.ts.netMagicDNSActive

Custom Alias Problem

The alias lushycorp-api.ts.tazlab.net was defined only in the Vault runtime TLS configuration, not in Tailscale DNS. This means:

  • It is not lifecycle-stable: after a destroy/recreate, the tailnet IP changes and the alias must be manually updated
  • It is not Tailscale-owned: the .ts.tazlab.net subdomain is not registered in the global DNS hierarchy
  • It requires bespoke DNS glue to work, adding complexity

The preferred direction is to converge onto lushycorp-vault.magellanic-gondola.ts.net which IS lifecycle-stable and Tailscale-managed.

Workload DNS Resolution (Current — ExternalName Egress Proxy)

Since 15-tailscale-operator-hardening (2026-05-09), the architecture uses the native Tailscale Kubernetes Operator DNS path, replacing the earlier relay DaemonSet.

Architecture: ExternalName + DNSConfig

The Operator provides DNS resolution for MagicDNS names via:

  1. ExternalName Service — target nodes like Vault are declared as ExternalName services with annotation tailscale.com/tailnet-fqdn
  2. Egress Proxy — for each ExternalName, the Operator creates a dedicated egress proxy pod in the tailscale namespace
  3. DNSConfig — the Operator’s nameserver auto-registers A records mapping MagicDNS names to proxy pod IPs
  4. CoreDNS forwarding — user-managed CoreDNS (10.96.0.10) forwards ts.net to the nameserver’s ClusterIP 10.108.193.103
pod → CoreDNS (10.96.0.10)
  → forward ts.net → nameserver Service (10.108.193.103)
    → nameserver pod (DNSConfig CRD)
      → egress proxy pod (connected to tailnet)
        → target: lushycorp-vault.magellanic-gondola.ts.net

CoreDNS Hardening on Talos

Talos v1.12+ uses Server-Side Apply — the built-in CoreDNS controller overwrites any manual ConfigMap changes. Implemented “Disable & Replace”:

  1. cluster.coreDNS.disabled: true in Talos config
  2. machine.kubelet.clusterDNS: ["10.96.0.10"] on all nodes
  3. User-managed CoreDNS stack (SA, RBAC, ConfigMap, Deployment, Service) deployed via Terraform inlineManifest

Previous Approach (decommissioned)

Before 15-tailscale-operator-hardening, a hostNetwork CoreDNS relay DaemonSet on port 5353 used a static hosts plugin mapping (lushycorp-vault → 100.82.13.87). Replaced because the ExternalName egress proxy approach is the enterprise-correct Operator-native solution — no static mappings, no relay management, no Corefile patches.

Verified Behavior (2026-05-09)

  • Pod using standard cluster DNS resolves lushycorp-vault.magellanic-gondola.ts.net to egress proxy pod IP
  • Traffic flows through the proxy into the tailnet, reaching Vault securely
  • No relay DaemonSet, no static host mappings, no kubectl patches in create.sh

See Also