Apply Agent DNS to any agent runtime
Six steps. No SDK, sidecar or proxy.
Follow it once per agent. The agent gets a policy it cannot change, an inventory of what it reaches, a hold on anything new, and an event per decision.
Why the resolver is the control point
Every action an agent takes starts with a name lookup
Model calls, tool calls, package installs and data fetches all begin with a DNS query. The resolver sees every destination before the connection exists.
Policy is enforced outside the agent
The resolver is not part of the agent process. No prompt injection, poisoned tool description or operator flag can change what it answers.
You get an inventory, a hold and an audit trail
Each policy records every destination its agent reached, holds destinations it has never seen, and writes one event per decision to the SIEM you already run.
The six steps
Create one policy per agent role
A policy owns its allow list, block list, first-seen baseline and events. Give the agent process one policy and its tool sandbox a second, stricter one. Bind each policy to a site (static egress IP) or a virtual site (a DoH address, no IP registration). Record the resolver addresses and the block page address from each site.
hermes-gateway process: model provider, messaging platforms
hermes-sandbox shell commands, package installs, browser
openhands-runtime the runtime container
coding-agents-ci CI runners: package registries and the repository hostAssign the resolver
Point each component at its policy. The agent needs no SDK, sidecar or proxy. The runtime pages carry the exact file for each component.
VM or bare metal /etc/systemd/resolved.conf.d/securd.conf
Container you start docker run --dns, compose dns:
Containers the agent starts /etc/docker/daemon.json "dns": [...]
Kubernetes dnsConfig on the pod, or a CoreDNS forward
Workstation, CI, serverless DoH virtual site through a local DoH proxy or an MDM DoH profileRestrict DNS egress to the assigned resolvers
Drop port 53 and 853 to any other destination and block the DoH provider category in the policy. A model that meets a blocked name will try another resolver; this turns that attempt into a firewall log line.
table inet securd {
chain output {
type filter hook output priority 0; policy accept;
ip daddr { <primary resolver IP>, <secondary resolver IP> } udp dport 53 accept
ip daddr { <primary resolver IP>, <secondary resolver IP> } tcp dport 53 accept
udp dport 53 counter drop
tcp dport 53 counter drop
tcp dport 853 counter drop
}
}Learn, then enforce
Import the scope templates for the model provider and tools in use. Set Greywall Mode to Learn Only and give the agent seven days of representative work. Review Traffic Logs filtered on Greywall Event: New Query and approve what the agent needs. Then set Default Action to Deny and Greywall Mode to Enforce.
# First-seen destinations for one site in the last day (JWT or dashboard:read key)
curl -s "https://control.securd.com/api/v1/gateway/dashboard/logs?tf=now-1d/d&dns_q.event=new&site_name=hermes-sandbox&per_page=1000" \
-H "Authorization: Bearer $TOKEN" -H "X-Tenant-UUID: $TENANT" \
| jq -r '.data[] | .dns_q.name' | sort | uniq -c | sort -rn
# Enforce with a 24 hour hold (JWT or policies:write key); the change publishes automatically
curl -s -X PUT https://control.securd.com/api/v1/gateway/policies/$POLICY_GUID \
-H "Authorization: Bearer $TOKEN" -H "X-Tenant-UUID: $TENANT" -H "Content-Type: application/json" \
-d '{ "default": "deny", "greylist": { "mode": "enabled", "holdtime": 86400 } }'Verify from inside the agent
Run these from the agent host and from inside a sandbox task. A held name answers with the block page address, an outside resolver is unreachable, and an approved destination still resolves.
getent hosts first-seen-$(date +%s).example # expected: <block page address>
dig +time=2 +tries=1 @8.8.8.8 example.com # expected: connection timed out
getent hosts dns.google # expected: <block page address> (DoH provider category)
getent hosts api.openai.com # expected: a public addressForward events
Send events over syslog, export CEF or JSON, pull through the API, or subscribe to the signed webhooks. Alert on a spike of New Query events on one site and on any firewall drop from step 3. The integrations pages carry the search for each SIEM.
index=securd dns_q.event="new"
| eval site=coalesce('server.mobile_name','site_name')
| bucket _time span=15m
| stats dc(dns_q.name) as new_names by _time site
| where new_names > 20Runtimes with a guide
| Runtime | Components | Resolver set by |
|---|---|---|
| Hermes Agent | Gateway host, docker or ssh terminal backend, compose deployment | Host resolver, Docker daemon default, compose dns |
| OpenClaw | Gateway host, Docker sandbox | Host resolver, Docker daemon default |
| OpenHands | App container, runtime containers | docker run --dns, Docker daemon default or docker_runtime_kwargs |
| Claude Code, Codex CLI, Gemini CLI | Devcontainers, CI runners, managed workstations, Gemini docker sandbox | runArgs --dns, DoH virtual site, MDM DoH profile, SANDBOX_FLAGS |
Not covered
- Vendor-hosted sandboxes with no resolver control: Hermes on Modal, Daytona or Vercel Sandbox; OpenClaw on Daytona. Use the docker or ssh backend where DNS governance is required.
- Hosted agents that run on the vendor's infrastructure (browser agents and assistants that never execute on your network).
- Connections to an IP address the agent already holds. Agent DNS governs destinations reached by name; the egress firewall governs addresses and ports, and it stays.
Frameworks you build on (LangChain, CrewAI, the OpenAI and Anthropic SDKs, Vercel AI, Spring AI, Semantic Kernel) are in the developer library with the guard that raises a typed exception on a held destination.
Evaluate Agent DNS with your own agent traffic
Apply the standard to one agent in Learn Only and review what it reached for.