# coredns-clawker Binary

Custom CoreDNS build embedding two in-tree plugins. Built as a static Linux binary by `make coredns-binary` → `internal/controlplane/firewall/assets/coredns-clawker`, then embedded into the clawker release via `internal/controlplane/firewall/embed_coredns.go` (`//go:embed`). At runtime, `firewall.Stack` builds the `clawker-coredns:latest` image on demand from that asset and manages its container lifecycle on `clawker-net`.

This binary is **not** invoked from the host CLI. It runs only inside the CoreDNS container managed by the CP firewall stack.

## Files

| File | Purpose |
|------|---------|
| `main.go` | Blank-imports the CoreDNS plugins our Corefile uses + the two in-tree plugins (`otel`, `dnsbpf`). `init()` prepends `otel` then `dnsbpf` to `dnsserver.Directives` so otel sees every final response and dnsbpf still wraps all resolver plugins. `main()` is just `coremain.Run()`. |
| `plugins/otel/` | OTEL log exporter for DNS query events — emits structured `dns.query` log records over OTLP/gRPC + mTLS to the CP-only collector. See `plugins/otel/CLAUDE.md`. |

## Plugin chain ordering

```go
dnsserver.Directives = append([]string{"otel", "dnsbpf"}, dnsserver.Directives...)
```

- `otel` is **first** so it observes the final response after every other plugin (template AAAA NODATA, forward upstream answers, catch-all NXDOMAIN) has produced its rcode + answer set.
- `dnsbpf` is **second** so it still wraps all resolver plugins (`forward`, `hosts`, etc.) and intercepts A answers before they leave the process for the BPF `dns_cache` map.
- Both plugins are also blank-imported from `cmd/coredns-clawker/main.go` so their `init()` (`plugin.Register(...)`) fires before `coremain.Run()` parses the Corefile.

## Stdlib CoreDNS plugins linked

```go
_ "github.com/coredns/coredns/plugin/forward"   // upstream resolution
_ "github.com/coredns/coredns/plugin/health"    // /health endpoint (catch-all zone)
_ "github.com/coredns/coredns/plugin/log"       // stdout logging (docker logs triage only; OpenSearch ingestion is the `otel` plugin)
_ "github.com/coredns/coredns/plugin/reload"    // 2s Corefile reload
_ "github.com/coredns/coredns/plugin/template"  // AAAA NODATA + NXDOMAIN templates
```

`coremain` does NOT transitively import these — CoreDNS's `zplugin.go` blank-imports must be replicated per binary. We only import the plugins the firewall Corefile uses, avoiding the full stdlib import set (Azure, AWS, K8s, etc.).

## Corefile contract

The Corefile is generated by `internal/controlplane/firewall/coredns_config.go` and bind-mounted at `/etc/coredns/Corefile`. Every zone block uses:

```
<zone> {
    otel              # dns.query log record per query → OTLP/gRPC (OpenSearch ingestion path)
    log . "<fmt>"     # stdout log line → docker logs clawker-coredns (triage only; not scraped)
    template IN AAAA . { rcode NOERROR }   # IPv6 NODATA (eBPF blocks connect6)
    dnsbpf            # write A answers into BPF dns_cache
    forward . <upstreams>
}
```

The catch-all `. {}` zone has `otel`, `log`, `template IN ANY . { rcode NXDOMAIN }`, `health :<port>`, and `reload 2s`. There is no `forward` — unlisted domains return NXDOMAIN.

## Build + embed

| Stage | Where |
|-------|-------|
| Build | `make coredns-binary` → `GOOS=linux CGO_ENABLED=0 go build ./cmd/coredns-clawker` → `internal/controlplane/firewall/assets/coredns-clawker` (gitignored) |
| Embed into CLI | `internal/controlplane/firewall/embed_coredns.go` — `//go:embed assets/coredns-clawker` → exported `CoreDNSClawkerBinary []byte` |
| Image build | `firewall.Stack.ensureCorednsImage` (Dockerfile generated in-memory; the embedded binary is the only content of the layer) |
| Runtime | `firewall.Stack.ensureContainer` creates the CoreDNS container on `clawker-net` at a static IP with `CAP_BPF + CAP_SYS_ADMIN` and `/sys/fs/bpf` bind-mounted |

Binary architecture must match the Docker host (arm64 or amd64). Makefile selects `GOARCH` accordingly.

## See also

- `plugins/otel/CLAUDE.md` — OTEL plugin internals (emitter lifecycle, mTLS, env config, test seam)
- `internal/dnsbpf/CLAUDE.md` — dnsbpf plugin (BPF `dns_cache` writer + shared FNV-1a domain hash)
- `internal/controlplane/firewall/CLAUDE.md` — Stack lifecycle, image build, Corefile generation, env wiring (`CLAWKER_COREDNS_OTEL_ENDPOINT`, mTLS cert mounts)
