(feat) DNS Authority #241

Open
opened 2026-04-05 17:03:18 +02:00 by MrUnknownDE · 0 comments
Owner

Originally created by @mattv8 on 2/17/2026

Community Contribution License Agreement

By creating this pull request, I grant the project maintainers an unlimited,
perpetual license to use, modify, and redistribute these contributions under any terms they
choose, including both the AGPLv3 and the Fossorial Commercial license terms. I
represent that I have the right to grant this license for all contributed content.

Description

This adds DNS Authority and Auth Proxy support to Pangolin. The companion Newt PR is https://github.com/fosrl/newt/pull/236. I opened a discussion about this beforehand. Read more about my motivation for building this feature there.

What this does

Pangolin builds authoritative DNS zone configs and pushes them to Newt over WebSocket. Newt binds port 53 and serves A/NS/SOA responses. The DNS answers use the Site's public IP (not the internal target IP); the idea is that DNS controls which site the user's traffic reaches, and then Traefik/the tunnel handles proxying to the actual backend.

There are two layers, and they can operate independently or together:

Domain-level zones: work with existing wildcard domains. When you enable DNS Authority on a Site and set its public IP, Pangolin finds every wildcard domain that has Resources with Targets on that Site. For each domain it builds a *.baseDomain zone containing all DNS-Authority-enabled Sites as targets. Health is aggregated at the Site level a Site is considered healthy for a domain if any of its Targets across Resources on that domain are healthy. This is the "just turn it on" mode: no per-Resource config needed, and it covers every subdomain under the wildcard automatically.

Per-resource zones: give finer control. Enabling dnsAuthorityEnabled on a specific Resource creates a zone for that Resource's fullDomain (e.g., app.docker.example.com). Unlike domain-level, health is tracked per-Target (not per-Site), and the user can configure a custom TTL (10–86400s) and routing policy (failover, round-robin, or priority). This works with any domain type, not just wildcards.

Zone configs are rebuilt and pushed to each Newt agent: Site DNS Authority toggle, Resource update, Target create/update/delete, health check status change, and Newt reconnect (which gets the full set of zones immediately).

Auth Proxy: Pangolin also pushes auth proxy configs so Newt can enforce SSO at the edge. Newt tries local JWT verification first using an RSA public key from Pangolin (sub-ms), then falls back to the Pangolin session validation API if needed.

How it works

graph LR
    %% Layout Adjustment
    User((User))

    subgraph "Central Control Plane"
        P[Pangolin Dashboard]
        DB[(PostgreSQL)]
        P <--> DB
    end

    subgraph "Site A (Primary Network)"
        direction LR
        N1[Newt Agent A]
        S1[App Service]
        N1 -- "Health Check" --> S1
    end

    subgraph "Site B (Redundant Network)"
        direction LR
        N2[Newt Agent B]
        S2[Redundant App Service]
        N2 -- "Health Check" --> S2
    end

    %% State Sync (Hidden/Top)
    P ==>|"WebSocket Sync"| N1
    P ==>|"WebSocket Sync"| N2

    %% Step 1 & 2: DNS Resolution
    User -- "1. DNS Query" --> N1
    N1 -- "2. IP Resolution" --> User

    %% Step 3 & 4: Traffic Flow
    User -- "3. HTTPS Request" --> N1
    User -.-> |"Failover Request"| N2
    N1 -- "4. Auth Proxy" --> S1
    N2 -- "4. Auth Proxy" --> S2

Schema changes

Migration 1.16.0 (I am assuming this warrants a version up) adds columns to sites (publicIp, dnsAuthorityEnabled, dnsStatus, dnsError) and resources (dnsAuthorityEnabled, dnsAuthorityTtl, dnsAuthorityRoutingPolicy). Both PG and SQLite migrations are included. Everything defaults to off (backwards compatibility).

Backward compatibility

All new behavior is behind dnsAuthorityEnabled toggles on both sites and resources. Existing deployments are unaffected; nothing activates until a user explicitly enables it.

Documentation

This feature will need docs at https://docs.pangolin.net/manage/dns-authority (placeholder links are in the code). I'm happy to write those once (if) the approach is agreed upon.

How to test?

Option 1: Full local test stack

There's a complete end-to-end test stack in mattv8/pangolin-testing that spins up PostgreSQL, Pangolin, Gerbil, two Newt instances, two backends, and a test client on a Docker bridge network.

git clone https://github.com/mattv8/pangolin-testing testing
cd testing/
sudo modprobe wireguard    # required for Gerbil
docker compose down -v && docker compose up -d
docker compose ps          # wait for all services healthy
bash scripts/bootstrap.sh  # creates admin, org, sites, resource, targets

The bootstrap script enables DNS Authority, creates two sites with public IPs, and verifies resolution:

dig @localhost -p 5353 app.test.dev A +short       # 172.28.0.10
dig @localhost -p 5354 app.test.dev A +short       # 172.28.0.10
dig @localhost -p 5353 anything.test.dev A +short  # 172.28.0.10 (wildcard)

Failover test:

docker compose stop backend
dig @localhost -p 5353 app.test.dev A +short       # secondary IP
docker compose start backend

Auth proxy test:

curl -sI http://localhost:8080/ | grep Location    # 302 redirect to login

See the testing README for the full architecture diagram and service map.

Option 2: Drop-in to an existing stack

Pangolin swap the image in your docker-compose.yml:

image: hub.docker.visnovsky.us/library/pangolin:dns-authority-dev
docker compose pull pangolin && docker compose up -d pangolin

Newt install the pre-compiled binary:

sudo cp /usr/local/bin/newt /usr/local/bin/newt.official
curl -fsSL https://raw.githubusercontent.com/mattv8/pangolin-testing/main/scripts/get-newt.sh | bash
sudo systemctl restart newt

Rollback:

sudo cp /usr/local/bin/newt.official /usr/local/bin/newt && sudo systemctl restart newt

Then enable DNS Authority on a site (set public IP), and optionally on individual resources, through the UI.

*Originally created by @mattv8 on 2/17/2026* ## Community Contribution License Agreement By creating this pull request, I grant the project maintainers an unlimited, perpetual license to use, modify, and redistribute these contributions under any terms they choose, including both the AGPLv3 and the Fossorial Commercial license terms. I represent that I have the right to grant this license for all contributed content. ## Description This adds DNS Authority and Auth Proxy support to Pangolin. The companion Newt PR is https://github.com/fosrl/newt/pull/236. I opened [a discussion](https://github.com/orgs/fosrl/discussions/2423) about this beforehand. Read more about my motivation for building this feature there. ### What this does Pangolin builds authoritative DNS zone configs and pushes them to Newt over WebSocket. Newt binds port 53 and serves A/NS/SOA responses. The DNS answers use the **Site's public IP** (not the internal target IP); the idea is that DNS controls which site the user's traffic reaches, and then Traefik/the tunnel handles proxying to the actual backend. There are two layers, and they can operate independently or together: **Domain-level zones:** work with existing wildcard domains. When you enable DNS Authority on a Site and set its public IP, Pangolin finds every wildcard domain that has Resources with Targets on that Site. For each domain it builds a `*.baseDomain` zone containing all DNS-Authority-enabled Sites as targets. Health is aggregated at the Site level a Site is considered healthy for a domain if *any* of its Targets across Resources on that domain are healthy. This is the "just turn it on" mode: no per-Resource config needed, and it covers every subdomain under the wildcard automatically. **Per-resource zones:** give finer control. Enabling `dnsAuthorityEnabled` on a specific Resource creates a zone for that Resource's `fullDomain` (e.g., `app.docker.example.com`). Unlike domain-level, health is tracked per-Target (not per-Site), and the user can configure a custom TTL (10–86400s) and routing policy (failover, round-robin, or priority). This works with any domain type, not just wildcards. Zone configs are rebuilt and pushed to each Newt agent: Site DNS Authority toggle, Resource update, Target create/update/delete, health check status change, and Newt reconnect (which gets the full set of zones immediately). **Auth Proxy:** Pangolin also pushes auth proxy configs so Newt can enforce SSO at the edge. Newt tries local JWT verification first using an RSA public key from Pangolin (sub-ms), then falls back to the Pangolin session validation API if needed. ### How it works ```mermaid graph LR %% Layout Adjustment User((User)) subgraph "Central Control Plane" P[Pangolin Dashboard] DB[(PostgreSQL)] P <--> DB end subgraph "Site A (Primary Network)" direction LR N1[Newt Agent A] S1[App Service] N1 -- "Health Check" --> S1 end subgraph "Site B (Redundant Network)" direction LR N2[Newt Agent B] S2[Redundant App Service] N2 -- "Health Check" --> S2 end %% State Sync (Hidden/Top) P ==>|"WebSocket Sync"| N1 P ==>|"WebSocket Sync"| N2 %% Step 1 & 2: DNS Resolution User -- "1. DNS Query" --> N1 N1 -- "2. IP Resolution" --> User %% Step 3 & 4: Traffic Flow User -- "3. HTTPS Request" --> N1 User -.-> |"Failover Request"| N2 N1 -- "4. Auth Proxy" --> S1 N2 -- "4. Auth Proxy" --> S2 ``` ### Schema changes Migration `1.16.0` (I am assuming this warrants a version up) adds columns to `sites` (`publicIp`, `dnsAuthorityEnabled`, `dnsStatus`, `dnsError`) and `resources` (`dnsAuthorityEnabled`, `dnsAuthorityTtl`, `dnsAuthorityRoutingPolicy`). Both PG and SQLite migrations are included. Everything defaults to `off` (backwards compatibility). ### Backward compatibility All new behavior is behind `dnsAuthorityEnabled` toggles on both sites and resources. Existing deployments are unaffected; nothing activates until a user explicitly enables it. ### Documentation This feature will need docs at https://docs.pangolin.net/manage/dns-authority (placeholder links are in the code). I'm happy to write those once (if) the approach is agreed upon. ## How to test? ### Option 1: Full local test stack There's a complete end-to-end test stack in [`mattv8/pangolin-testing`](https://github.com/mattv8/pangolin-testing) that spins up PostgreSQL, Pangolin, Gerbil, two Newt instances, two backends, and a test client on a Docker bridge network. ```bash git clone https://github.com/mattv8/pangolin-testing testing cd testing/ sudo modprobe wireguard # required for Gerbil docker compose down -v && docker compose up -d docker compose ps # wait for all services healthy bash scripts/bootstrap.sh # creates admin, org, sites, resource, targets ``` The bootstrap script enables DNS Authority, creates two sites with public IPs, and verifies resolution: ```bash dig @localhost -p 5353 app.test.dev A +short # 172.28.0.10 dig @localhost -p 5354 app.test.dev A +short # 172.28.0.10 dig @localhost -p 5353 anything.test.dev A +short # 172.28.0.10 (wildcard) ``` Failover test: ```bash docker compose stop backend dig @localhost -p 5353 app.test.dev A +short # secondary IP docker compose start backend ``` Auth proxy test: ```bash curl -sI http://localhost:8080/ | grep Location # 302 redirect to login ``` See the [testing README](https://github.com/mattv8/pangolin-testing/blob/main/README.md) for the full architecture diagram and service map. ### Option 2: Drop-in to an existing stack **Pangolin** swap the image in your `docker-compose.yml`: ```yaml image: hub.docker.visnovsky.us/library/pangolin:dns-authority-dev ``` ```bash docker compose pull pangolin && docker compose up -d pangolin ``` **Newt** install the pre-compiled binary: ```bash sudo cp /usr/local/bin/newt /usr/local/bin/newt.official curl -fsSL https://raw.githubusercontent.com/mattv8/pangolin-testing/main/scripts/get-newt.sh | bash sudo systemctl restart newt ``` Rollback: ```bash sudo cp /usr/local/bin/newt.official /usr/local/bin/newt && sudo systemctl restart newt ``` Then enable DNS Authority on a site (set public IP), and optionally on individual resources, through the UI.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github/pangolin#241