Client ACLs
Host-managed allow/deny (and tag) for client socket IPs using named type: cidr data sources. Use this when you want different policy on public vs internal listeners without scripting exact-key CSV lookups on every query.
Omit top-level acls: and Conduit admits every client — same behavior as a config with no ACL keys. CIDR table format is under Data sources — CIDR sources; ACL field lists are in Config schema: acls and Config schema: data sources.
How it fits the query path
ACL runs on ingress, before a transaction slot is acquired when the runtime can admit that way:
flowchart TD
Recv[Receive UDP/TCP query] --> T0[ACL Tier 0 — explicit drop only]
T0 -->|drop| DropSilent[Silent drop — no slot; TCP closes]
T0 -->|continue| Parse[Structural parse]
Parse -->|invalid| ParseDrop[Parse reject — no slot]
Parse -->|ok| T1[ACL Tier 1 — full policy]
T1 -->|drop| DropSilent
T1 -->|refuse| Refuse[REFUSED — no slot]
T1 -->|tag / accept / allow| Slot[Acquire slot → pipeline]
| Stage | What runs |
|---|---|
| Tier 0 (pre-parse) | Explicit drop matches only — known-bad nets never pay for parse or a slot. On TCP, Tier 0 closes the session immediately after accept (before reading a query). The TCP handshake still completes; this is not a firewall SYN drop. |
| Tier 1 (post-parse, pre-slot) | Full first-match: drop, refuse, tag, accept, and default_action |
| Request rules | Optional client_cidr selector for special cases with existing rule actions |
Matching uses the UDP/TCP peer address on the socket. EDNS Client Subnet and other forwarded-client identity are out of scope.
Global and per-listener policy
data_sources:
- name: corp_nets
type: cidr
path: data/corp_nets.txt
- name: block_nets
type: cidr
path: data/block_nets.txt
acls:
default_action: deny
rules:
- match: block_nets
action: drop
- match: corp_nets
action: accept
listeners:
threads: 1
listeners:
- address: "0.0.0.0:53"
protocol: udp
name: public
acls:
default_action: deny
rules:
- match: corp_nets
action: accept
- address: "10.0.0.1:53"
protocol: udp
name: internal
# no acls: — inherits global
Listener acls: |
Effect |
|---|---|
| Omitted | Inherit the entire top-level acls: (or admit-all if none) |
| Present | Full replace of global ACL for that listener only — not a merge |
First matching rule wins. If no rule matches, default_action applies: allow admits; deny is a silent drop (no DNS reply). Prefer an explicit refuse rule when clients should see REFUSED.
Actions
action |
Behavior |
|---|---|
drop |
Silent discard; no slot; can run at Tier 0 when it is the first matching rule |
refuse |
DNS REFUSED from the parsed query id; no slot |
tag |
Admit, acquire a slot, set the named tag on the transaction (tag: required) |
accept |
Admit and stop ACL evaluation (name is accept, not allow, so it does not collide with default_action: allow) |
Overlay and export
An overlay is an in-memory config patch applied with conduitctl apply without editing the file on disk; export writes the current effective config back out as YAML. This section covers how ACL policy behaves under both.
Top-level acls: is overlay-eligible: when an overlay patch includes acls:, it replaces the entire top-level ACL policy. To clear back toward admit-all, apply an empty-rules block with default_action: allow (or reload without overlay).
YAML export keeps ACL structure and data-source names / types / paths — it does not inline CIDR file contents.
Per-listener ACL changes need a file reload or a whole-listeners: overlay replace (there is no sparse listener_acls: map).
Observability
- Metrics:
conduit_acl_decisions_total(host gates only; Prom + OTLP parity) - Denial logs: optional
logging.query_accesswith optional sampling
Checking an IP
Use conduitctl acl check to dry-run effective ACL policy for a client address without sending a DNS query. Live mode (default) asks the running process via the control plane so the answer matches the in-memory snapshot and CIDR tables. Pass --file to compile a local YAML the same way as validate. Details and JSON fields: gRPC and conduitctl — acl check.
Related
- Rules and actions —
client_cidrselector - Data sources —
type: cidrviews and file format - Rhai lookups —
lookup_ipin rule scripts - Runtime and concurrency — sync / split_io ingress
- gRPC and conduitctl —
acl check