# Newt (Pangolin) — noble This is the **primary** automation path for **public** hostnames to workloads in this cluster (it **replaces** in-cluster ExternalDNS). [Newt](https://github.com/fosrl/newt) is the on-prem agent that connects your cluster to a **Pangolin** site (WireGuard tunnel). The [Fossorial Helm chart](https://github.com/fosrl/helm-charts) deploys one or more instances. **Secrets:** Never commit endpoint, Newt ID, or Newt secret in **plain** YAML. If credentials were pasted into chat or CI logs, **rotate them** in Pangolin and recreate the Kubernetes Secret. ## 1. Create the Secret Keys must match `values.yaml` (`PANGOLIN_ENDPOINT`, `NEWT_ID`, `NEWT_SECRET`). ### Option A — SOPS (safe for GitOps) Encrypt a normal **`Secret`** with [Mozilla SOPS](https://github.com/getsops/sops) and **age** (see **`clusters/noble/secrets/README.md`** and **`.sops.yaml`**). The repo includes an encrypted example at **`clusters/noble/secrets/newt-pangolin-auth.secret.yaml`** — edit with `sops` after exporting **`SOPS_AGE_KEY_FILE`** to your **`age-key.txt`**, or create a new file and encrypt it. ```bash export SOPS_AGE_KEY_FILE=/absolute/path/to/home-server/age-key.txt sops clusters/noble/secrets/newt-pangolin-auth.secret.yaml # then: sops -d clusters/noble/secrets/newt-pangolin-auth.secret.yaml | kubectl apply -f - ``` **Ansible** (`noble.yml`) applies all **`clusters/noble/secrets/*.yaml`** automatically when **`age-key.txt`** exists at the repo root. ### Option B — Imperative Secret (not in git) ```bash kubectl apply -f clusters/noble/bootstrap/newt/namespace.yaml kubectl -n newt create secret generic newt-pangolin-auth \ --from-literal=PANGOLIN_ENDPOINT='https://pangolin.pcenicni.dev' \ --from-literal=NEWT_ID='YOUR_NEWT_ID' \ --from-literal=NEWT_SECRET='YOUR_NEWT_SECRET' ``` Use the Pangolin UI or [Integration API](https://docs.pangolin.net/manage/common-api-routes) (`pick-site-defaults` + `create site`) to obtain a Newt ID and secret for a new site if you are not reusing an existing pair. ## 2. Install the chart ```bash helm repo add fossorial https://charts.fossorial.io helm repo update helm upgrade --install newt fossorial/newt \ --namespace newt \ --version 1.5.0 \ -f clusters/noble/bootstrap/newt/values.yaml \ --wait ``` ## 3. DNS: CNAME at your DNS host + Pangolin API for routes Pangolin does not replace your public DNS provider. Typical flow: 1. **Link a domain** in Pangolin (organization **Domains**). For **CNAME**-style domains, Pangolin shows the hostname you must **CNAME** to at Cloudflare / your registrar (see [Domains](https://docs.pangolin.net/manage/common-api-routes#list-domains)). 2. **Create public HTTP resources** (and **targets** to your Newt **site**) via the [Integration API](https://docs.pangolin.net/manage/integration-api) — same flows as the UI. Swagger: `https:///v1/docs` (self-hosted: enable `enable_integration_api` and route `api.example.com` → integration port per [docs](https://docs.pangolin.net/self-host/advanced/integration-api)). Minimal patterns (Bearer token = org or root API key): ```bash export API_BASE='https://api.example.com/v1' # your Pangolin Integration API base export ORG_ID='your-org-id' export TOKEN='your-integration-api-key' # Domains already linked to the org (use domainId when creating a resource) curl -sS -H "Authorization: Bearer ${TOKEN}" \ "${API_BASE}/org/${ORG_ID}/domains" # Create an HTTP resource on a domain (FQDN = subdomain + base domain for NS/wildcard domains) curl -sS -X PUT -H "Authorization: Bearer ${TOKEN}" -H 'Content-Type: application/json' \ "${API_BASE}/org/${ORG_ID}/resource" \ -d '{ "name": "Example app", "http": true, "domainId": "YOUR_DOMAIN_ID", "protocol": "tcp", "subdomain": "my-app" }' # Point the resource at your Newt site backend (siteId from list sites / create site; ip:port inside the tunnel) curl -sS -X PUT -H "Authorization: Bearer ${TOKEN}" -H 'Content-Type: application/json' \ "${API_BASE}/resource/RESOURCE_ID/target" \ -d '{ "siteId": YOUR_SITE_ID, "ip": "10.x.x.x", "port": 443, "method": "http" }' ``` Exact JSON fields and IDs differ by domain type (**ns** vs **cname** vs **wildcard**); see [Common API routes](https://docs.pangolin.net/manage/common-api-routes) and Swagger. ### Authentik on a public name Use **`noble_authentik_ingress_extra_hosts`** (see **`ansible/roles/noble_authentik/README.md`**) so the Authentik Ingress (and **cert-manager** SANs) include your public FQDN, then create the Pangolin **HTTP** resource + **target** to the same Traefik **:443** endpoint as other apps. One Newt site can carry many hostnames. ### What to put in Pangolin (resource + target) 1. **Public hostname** — the FQDN users type in the browser (must match **`noble_authentik_ingress_extra_hosts`** and your **CNAME** at the DNS host Pangolin documents for that domain). 2. **Site** — the Pangolin **site** that owns your **Newt** pair (same **`NEWT_ID`** / **`NEWT_SECRET`** as the cluster Secret). In the UI: **Sites** → pick the site connected to this cluster. 3. **Target `ip`** — an address **reachable from inside the tunnel** to **Traefik HTTPS**. On noble this is usually the Traefik **LoadBalancer** IP (repo pins **`192.168.50.211`** in **`clusters/noble/bootstrap/traefik/values.yaml`**). Confirm live: `kubectl -n traefik get svc -l app.kubernetes.io/name=traefik -o wide` Use **`EXTERNAL-IP`** (or **`LOAD_BALANCER_IP`** from the Service status) as **`ip`**. If Newt runs **in** the cluster, that MetalLB/LAN VIP is correct; if you run Newt elsewhere, use whatever L3 path reaches Traefik from that host. 4. **Target `port`** — **`443`** (TLS to Traefik; SNI carries the public hostname). 5. **Target `method`** — **`http`** in the Integration API examples above (TLS is still terminated at Traefik; Pangolin’s field names follow their docs). Discovery in Pangolin’s UI: **Domains** (see required CNAME) → **Resources** → **Add** HTTP resource for the subdomain/FQDN → **Targets** / **Backends** → attach **site** + **ip:port**. Official flow: [Domains](https://docs.pangolin.net/manage/common-api-routes#list-domains), [Integration API](https://docs.pangolin.net/manage/integration-api), and your deployment’s **Swagger** at **`https:///v1/docs`** when enabled. ## LAN vs internet - **LAN / VPN:** point **`*.apps.noble.lab.pcenicni.dev`** at the Traefik **LoadBalancer** (**`192.168.50.211`**) with local or split-horizon DNS if you want direct in-lab access. - **Internet-facing:** use Pangolin **resources** + **targets** to the Newt **site**; public names rely on **CNAME** records at your DNS provider per Pangolin’s domain setup, not on ExternalDNS in the cluster.