From 9696cbfd649634f2482a8ff8a2dc35ab6ab7bc02 Mon Sep 17 00:00:00 2001 From: "NAGY Akos (external)" Date: Thu, 16 Oct 2025 14:06:39 +0300 Subject: [PATCH] init --- README.md | 86 ++++++++++++++++++++++++++++++++++++++ backends/backend1/Corefile | 15 +++++++ backends/backend2/Corefile | 12 ++++++ backends/backend3/Corefile | 12 ++++++ backends/backend4/Corefile | 13 ++++++ dnsmasq/dnsmasq.conf | 37 ++++++++++++++++ docker-compose.yaml | 66 +++++++++++++++++++++++++++++ 7 files changed, 241 insertions(+) create mode 100644 README.md create mode 100644 backends/backend1/Corefile create mode 100644 backends/backend2/Corefile create mode 100644 backends/backend3/Corefile create mode 100644 backends/backend4/Corefile create mode 100644 dnsmasq/dnsmasq.conf create mode 100644 docker-compose.yaml diff --git a/README.md b/README.md new file mode 100644 index 0000000..caa7dfc --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +## DNSMasq divergence testbed + +This project spins up one `dnsmasq` in front of four backend DNS servers (CoreDNS). Each backend returns a different A record for the same name so you can observe how `dnsmasq` behaves when upstreams disagree. + +### Topology + +- **dnsmasq**: listens on host UDP/TCP `5353`, forwards to backends at `192.168.243.11-14` +- **backend1..backend4 (CoreDNS)**: each serves a different answer for `test.local`: + - backend1 → `10.0.0.1` + - backend2 → `10.0.0.2` + - backend3 → `10.0.0.3` + - backend4 → `10.0.0.4` + +Custom bridge network: `192.168.243.0/24` with static IPs for reproducibility. + +### Files + +- `docker-compose.yaml`: services and fixed IP networking +- `dnsmasq/dnsmasq.conf`: forwards to all four backends, logging enabled, caching enabled +- `backends/backend*/Corefile`: CoreDNS configs returning distinct answers + +### Run + +```bash +docker compose -f /home/akos/docker/dnsmasq/docker-compose.yaml up -d +``` + +Wait a few seconds until all containers are healthy. + +### Test from the host + +Query via `dnsmasq` on port 5353: + +```bash +dig @127.0.0.1 -p 5353 test.local A +short +``` + +Run several times to observe responses and `dnsmasq` caching behavior. You should see one of: `10.0.0.1`, `10.0.0.2`, `10.0.0.3`, `10.0.0.4`. + +### Test from within the dnsmasq container (optional) + +The `andyshinn/dnsmasq` image is Alpine-based; install `dig` temporarily: + +```bash +docker exec -it dnsmasq sh -c "apk add --no-cache bind-tools >/dev/null && dig @127.0.0.1 test.local A +short" +``` + +### Inspect logs + +`dnsmasq` query logging is enabled: + +```bash +docker logs -f dnsmasq +``` + +### Adjusting behavior + +To explore how `dnsmasq` handles disagreement: + +- **Disable cache** (no stored answers): set `cache-size=0` in `dnsmasq/dnsmasq.conf`, then recreate the service. +- **Force first-server order**: add `strict-order` to `dnsmasq/dnsmasq.conf` so servers are queried in listed order. +- **Query all upstreams**: add `all-servers` so `dnsmasq` queries every upstream in parallel and picks the first reply. + +Apply changes by recreating the service: + +```bash +docker compose -f /home/akos/docker/dnsmasq/docker-compose.yaml up -d --force-recreate dnsmasq +``` + +### Resetting the cache + +```bash +docker restart dnsmasq +``` + +### Clean up + +```bash +docker compose -f /home/akos/docker/dnsmasq/docker-compose.yaml down -v +``` + +### Notes + +- The backends are simple CoreDNS instances using the `hosts` plugin for `test.local`; unknown names forward to public resolvers. +- The compose file exposes `53/udp` and `53/tcp` on host port `5353` to avoid clashing with any local resolver. + diff --git a/backends/backend1/Corefile b/backends/backend1/Corefile new file mode 100644 index 0000000..f5d362f --- /dev/null +++ b/backends/backend1/Corefile @@ -0,0 +1,15 @@ +. { + errors + log + health + ready + hosts { + 10.0.0.1 test.local + 10.0.0.11 test + 10.0.0.2 test2.local + 10.0.0.22 test2 + fallthrough + } + forward . 8.8.8.8 +} + diff --git a/backends/backend2/Corefile b/backends/backend2/Corefile new file mode 100644 index 0000000..14ff157 --- /dev/null +++ b/backends/backend2/Corefile @@ -0,0 +1,12 @@ +. { + errors + log + health + ready + hosts { + 10.0.0.2 test.local + fallthrough + } + forward . 8.8.4.4 +} + diff --git a/backends/backend3/Corefile b/backends/backend3/Corefile new file mode 100644 index 0000000..9a25aea --- /dev/null +++ b/backends/backend3/Corefile @@ -0,0 +1,12 @@ +. { + errors + log + health + ready + hosts { + 10.0.0.3 test.local + fallthrough + } + forward . 1.1.1.1 +} + diff --git a/backends/backend4/Corefile b/backends/backend4/Corefile new file mode 100644 index 0000000..a8e94cd --- /dev/null +++ b/backends/backend4/Corefile @@ -0,0 +1,13 @@ +. { + errors + log + health + ready + hosts { + 10.0.0.4 test.local + 10.0.0.99 test9.frequentis.frq + fallthrough + } + forward . 9.9.9.9 +} + diff --git a/dnsmasq/dnsmasq.conf b/dnsmasq/dnsmasq.conf new file mode 100644 index 0000000..ce84dff --- /dev/null +++ b/dnsmasq/dnsmasq.conf @@ -0,0 +1,37 @@ +# Log queries for visibility +log-queries +log-facility=/var/log/dnsmasq.log + +# Do not read /etc/resolv.conf +no-resolv + +# Upstream DNS servers (CoreDNS backends) +server=192.168.243.11 +server=192.168.243.12 +server=192.168.243.13 +server=192.168.243.14 + +# Never forward plain names +domain-needed + +# Never forward reverse lookups for private ranges +bogus-priv + +# Listen on all interfaces +interface=eth0 +bind-interfaces + +# Set cache size (optional) +cache-size=1000 + + +# Upstream selection behavior (uncomment one or both as needed) +# Query upstreams strictly in the listed order (no randomization) +#strict-order + +# Query all upstream servers in parallel; use the first reply +#all-servers + +# Disable cache entirely (for testing) +#cache-size=0 + diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..9d42a42 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,66 @@ +services: + dnsmasq: + image: 4km3/dnsmasq:2.86-r0 + container_name: dnsmasq + command: ["-k"] + volumes: + - ./dnsmasq/dnsmasq.conf:/etc/dnsmasq.conf:ro + ports: + - "5353:53/udp" + - "5353:53/tcp" + networks: + dnsnet: + ipv4_address: 192.168.243.10 + depends_on: + - backend1 + - backend2 + - backend3 + - backend4 + + backend1: + image: coredns/coredns:latest + container_name: backend1 + command: ["-conf", "/etc/coredns/Corefile"] + volumes: + - ./backends/backend1/Corefile:/etc/coredns/Corefile:ro + networks: + dnsnet: + ipv4_address: 192.168.243.11 + + backend2: + image: coredns/coredns:latest + container_name: backend2 + command: ["-conf", "/etc/coredns/Corefile"] + volumes: + - ./backends/backend2/Corefile:/etc/coredns/Corefile:ro + networks: + dnsnet: + ipv4_address: 192.168.243.12 + + backend3: + image: coredns/coredns:latest + container_name: backend3 + command: ["-conf", "/etc/coredns/Corefile"] + volumes: + - ./backends/backend3/Corefile:/etc/coredns/Corefile:ro + networks: + dnsnet: + ipv4_address: 192.168.243.13 + + backend4: + image: coredns/coredns:latest + container_name: backend4 + command: ["-conf", "/etc/coredns/Corefile"] + volumes: + - ./backends/backend4/Corefile:/etc/coredns/Corefile:ro + networks: + dnsnet: + ipv4_address: 192.168.243.14 + +networks: + dnsnet: + driver: bridge + ipam: + config: + - subnet: 192.168.243.0/24 +