Skip to main content

Cloud DNS provider discovery

A DNS Zone Transfer (AXFR) source needs your own authoritative DNS server and the ability to allow zone transfers from the scanner. If your DNS is hosted in the cloud — Cloudflare, Google Cloud DNS, and more providers over time — you have no AXFR. A Cloud DNS Provider source closes that gap: it authenticates to the provider's API, lists a zone's records, and probes each hostname with its own name as SNI, exactly like AXFR — so it finds every distinct certificate serving your names.

How it works

  1. It reads the zone's A records through the provider API (example.com192.0.2.10). AAAA records are counted but not scanned (IPv4-only, same as AXFR). A CNAME is not flattened — except a Cloudflare proxied CNAME, which the edge serves under its own name and is therefore scanned at the edge (see Proxied records).
  2. Records are grouped by IP and port, and each responsive endpoint is probed with every hostname it serves as the SNI — so an SNI-routed frontend (a Cloudflare edge IP, a shared load balancer) yields each distinct certificate.
  3. Results land in the same inventory, findings, and risk scoring as every other source.

Each hostname that a certificate genuinely covers gets its own endpoint row (the name as the SNI) — so you see every name and its certificate, including a real certificate served for many names (e.g. a wildcard, where each subdomain is its own row). The exception is a fall-through: when an IP returns the same certificate for a name that cert does not cover — a reverse-DNS hostname, or an apex a wildcard misses, hitting the server's default/catch-all vhost — that name is not really served there, so its row is merged into the IP's plain-IP (no SNI) row, which carries a "seen via N" tag listing the names. That keeps the name→IP mapping visible without a wall of misleading rows. (The scanner's internal wildcard-probe name is dropped entirely.) On the inventory and certificate pages, the per-name rows for one IP are grouped under a single expandable row so a wildcard serving dozens of names stays compact.

Cloud DNS records point at public IP addresses, so a Cloud DNS Provider source always scans public targets (there is no "private only" toggle for this type).

Proxied (orange-cloud) records

A Cloudflare record with the proxy on ("orange cloud") is served by Cloudflare's edge: visitors connect to a Cloudflare address and see the certificate on the edge, while the address in the record is the origin behind it, which presents its own certificate — often a different one. Both matter, so each proxied name is scanned on both sides and the rows are tagged on the endpoint tables:

  • Edge — the name is resolved with public resolvers (Cloudflare's and Google's — never your internal Discovery DNS resolvers, which may answer the origin for a proxied name) and probed at each Cloudflare edge address it resolves to (normally two) with the name as SNI. This is the certificate the public sees — for a certificate CertAutoPilot uploaded to Cloudflare it is the managed certificate itself, so on the next complete scan the discovered certificate is linked to it as managed (a partial run defers the link).
  • Origin — the address in the record, probed with the name as SNI: what the origin server presents (a wildcard for another domain, a Cloudflare origin certificate, an expired leftover…). Origins that only accept connections from Cloudflare show up as failed rows.

Proxied records on the source selects which side is scanned: Edge and origin (default), Edge only, or Origin only (the behaviour before this option existed). It can be changed on the source's Edit page and takes effect on the next scan. Records that are not proxied are never affected.

Details worth knowing:

  • The edge only terminates HTTPS on Cloudflare's proxied ports (443, 2053, 2083, 2087, 2096, 8443), so a name is scanned at the edge only on the source ports in that set; other ports go to the origin only.
  • Before an address is scanned as edge it is checked against Cloudflare's published IP ranges. An answer outside them (a name that is not really proxied, a paused zone, a stale record) is not scanned at all — the origin row is unaffected — and is named in the scan's job log. If the ranges cannot be fetched, the scan continues with the resolved public addresses (a private answer is still never treated as edge) and says so in the job log.
  • A proxied CNAME or AAAA record is scanned at the edge only — the edge serves the name over IPv4 either way, while its origin is not known / not IPv4.
  • Edge addresses are IPv4 only, like the rest of discovery. The edge refuses a handshake without a server name, so edge addresses get no plain-IP (no SNI) row; every edge row carries a name.
  • The names of a zone are a finite list, so they are not subject to the deep-SNI candidate budget — every proxied name is probed at the edge even though they all share one or two Cloudflare addresses. Names found by SAN crawling from those certificates are still budgeted.
  • If some proxied names could not be resolved (resolver failure or the DNS pool deadline), the run is reported as partial and the disappearance sweep is skipped, exactly as for a zone that failed to list.
  • The tag is per address: a name that is not proxied but shares an origin address with a proxied one is shown under that address's Origin tag.
  • Rows created before this feature keep working; an origin row gains the Origin tag on the next scan that includes the origin (mode Edge and origin or Origin only) while the record is still proxied. Turning the proxy off removes the tag on the next scan. Switching a source between modes marks the rows of the side no longer scanned as disappeared on the next complete scan.

1. Add a connection (admin, one-time)

Cloud DNS connections are managed org-wide by an admin under Settings → Discovery → DNS Provider Connections. A connection stores an API token (encrypted at rest, never shown again) plus an optional list of zones it may enumerate.

For Cloudflare, create an API token with Zone → Read and DNS → Read (a read-only token is enough — discovery never writes). Then:

  1. Settings → Discovery → DNS Provider Connections → Add Connection.
  2. Name it, choose Cloudflare, paste the API token.
  3. Optionally list the zones the token may enumerate (empty = every zone the token can access).
  4. Use Test to confirm the token authenticates.

For Google Cloud DNS, create a service account with the DNS Reader role (roles/dns.reader) on the project whose zones you want to scan, download a JSON key, and:

  1. Settings → Discovery → DNS Provider Connections → Add Connection.
  2. Name it, choose Google Cloud DNS, paste the service-account JSON key.
  3. Optionally set a Project ID to read a different project's zones than the key's own (defaults to the key's project_id), and optionally list the zones to restrict enumeration (empty = every managed zone in the project).
  4. Use Test to confirm the key authenticates and has dns.reader.

The key is read-only (scope ndev.clouddns.readonly) — discovery only lists records and never writes.

2. Add a discovery source

  1. Discovery → Sources → New → type Cloud DNS Provider.
  2. Pick the connection.
  3. Optionally narrow to a subset of zones (empty = every zone the connection allows).
  4. Set the ports to probe (default 443).
  5. Choose what a proxied record is scanned at — edge and origin (default), edge only, or origin only (see Proxied records).
  6. Set a schedule like any other source.

Targets are enumerated from the provider API at scan time, so the source shows no fixed record count until it runs.

Editing zones later

On the source's Edit page you can change ports, the proxied-record mode and schedule as a project admin. Zones are add-only — a zone you added can't be removed (create a new source to narrow). Because adding a zone widens what the org-shared token reads, adding a zone requires an org administrator; a ports/schedule-only edit does not. If an admin later narrows the connection's own zone list, sources are re-clipped to it on the next scan — a zone dropped from the connection is no longer scanned by any source (and the drop is noted in the scan's job log). A source created with no zone list scans every zone the connection allows and can't be narrowed to a specific list on edit (that would drop the other zones' discovered endpoints) — create a new source for a subset.

Security

  • The credential (Cloudflare token or Google service-account key) is envelope-encrypted (KEK) and is never returned by the API.
  • Discovery uses the credential read-only — it only lists records, never writes.
  • Outbound calls to the provider API go through the SSRF-protected dialer (link-local, cloud-metadata and DNS-rebinding blocked); the credential is never re-sent across a redirect.
  • For Google Cloud DNS the OAuth token endpoint is pinned to Google's fixed URL — a service-account key's own token_uri is ignored — so a crafted key can't redirect CertAutoPilot's token request at another host.
  • Egress during the scan is pinned to exactly the IPs the records resolved to — the record addresses plus, for proxied names, the edge addresses they resolved to (checked against Cloudflare's published ranges).

Limitations

  • A records (and Cloudflare proxied CNAMEs, at the edge) in this version; IPv6 and other CNAMEs are counted, not scanned.
  • Cloudflare and Google Cloud DNS are supported; the design is provider-agnostic, so more can be added without changing how sources or connections work.
  • A partial listing (some zones failed) still scans what it could and skips the disappearance sweep for that run, so a transient API error never falsely marks a still-live endpoint as gone. Each failed zone's reason (permission, rate limit, transport error) is written to the scan's job log, so you can see why a zone was skipped — the same trace an AXFR source leaves for a refused transfer.