Skip to content

steigr/nameserver-switcher

Repository files navigation

nameserver-switcher

A DNS proxy server with intelligent routing based on regex pattern matching. It provides both DNS and gRPC interfaces, along with Prometheus metrics and health endpoints.

Code Coverage Maintainability

Features

  • Pattern-based DNS routing: Match incoming DNS requests against configurable regex patterns

  • CNAME-aware routing: Secondary pattern matching on CNAME responses for intelligent resolver selection

  • Multiple resolver support: Route to different DNS resolvers based on pattern matches

  • gRPC interface: Full DNS resolution capabilities via gRPC with runtime configuration updates

  • Prometheus metrics: Comprehensive metrics for monitoring

  • Health endpoints: Kubernetes-compatible health and readiness probes

How It Works

  1. Request Pattern Matching: When a DNS request arrives (via DNS or gRPC), it checks if the query name matches any configured request patterns.

  2. Non-recursive Lookup to Explicit Resolver: If a pattern matches, performs a non-recursive DNS lookup to the configured "explicit resolver".

  3. CNAME Pattern Matching: If the response contains a CNAME record, checks if the CNAME target matches any CNAME patterns.

  4. Recursive Lookup: If a CNAME pattern matches, performs a recursive lookup to the "explicit resolver".

  5. System Fallback: If no CNAME match occurs (but request pattern matched), uses the system resolver. If no request pattern matches at all, uses the system resolver.

Installation

Using Go

go install github.com/steigr/nameserver-switcher/cmd/nameserver-switcher@latest

Using Docker

docker run -p 5353:5353/udp -p 5353:5353/tcp -p 5354:5354 -p 8080:8080 \
  ghcr.io/steigr/nameserver-switcher:latest

Using Helm

helm install nameserver-switcher ./charts/nameserver-switcher \
  --set patterns.request[0]=".*\\.example\\.com$" \
  --set patterns.cname[0]=".*\\.cdn\\.com$" \
  --set resolvers.request="8.8.8.8:53" \
  --set resolvers.explicit="1.1.1.1:53"

Configuration

Command Line Flags

Flag Description Default

--request-patterns

Newline-delimited regex patterns for matching incoming requests

""

--cname-patterns

Newline-delimited regex patterns for matching CNAME responses

""

--request-resolver

DNS server for initial non-recursive lookups

""

--explicit-resolver

DNS server for recursive lookups when CNAME matches

""

--dns-listen-addr

Address to listen for DNS requests

"0.0.0.0"

--dns-port

Port for DNS server

5353

--grpc-listen-addr

Address to listen for gRPC requests

"0.0.0.0"

--grpc-port

Port for gRPC server

5354

--http-listen-addr

Address for HTTP health/metrics server

"0.0.0.0"

--http-port

Port for HTTP server

8080

--debug

Enable debug logging

false

--log-requests

Log all DNS requests

true

--log-responses

Log all DNS responses

true

--log-format

Log output format: text or json

text

Environment Variables

Variable Description

REQUEST_PATTERNS

Newline-delimited regex patterns for request matching

CNAME_PATTERNS

Newline-delimited regex patterns for CNAME matching

REQUEST_RESOLVER

DNS server for non-recursive lookups

EXPLICIT_RESOLVER

DNS server for recursive CNAME lookups

DNS_LISTEN_ADDR

Address for DNS server

DNS_PORT

Port for DNS server

GRPC_LISTEN_ADDR

Address for gRPC server

GRPC_PORT

Port for gRPC server

HTTP_LISTEN_ADDR

Address for HTTP server

HTTP_PORT

Port for HTTP server

DEBUG

Enable debug logging (true/1 to enable)

LOG_REQUESTS

Log DNS requests (false/0 to disable)

LOG_RESPONSES

Log DNS responses (false/0 to disable)

LOG_FORMAT

Log output format: text (default) or json

Logging

The nameserver-switcher provides comprehensive logging capabilities with support for text and JSON output formats.

Log Formats

Text format (default): Human-readable logs with millisecond-precision timestamps.

JSON format: Structured logs for log aggregation systems. Each entry includes a timemillis field with Unix timestamp in milliseconds.

Normal Logging (LOG_REQUESTS=true, LOG_RESPONSES=true)

When enabled (default), logs all DNS requests and responses:

Text format:

2026/01/29 14:32:15.123 [INFO] DNS request received protocol=udp type=A name=example.com. from=127.0.0.1:54321
2026/01/29 14:32:15.135 [INFO] DNS response sent name=example.com. rcode=NOERROR answer_count=1 resolver=system duration_ms=12.345

JSON format:

{"timemillis":1738161135123,"time":"2026-01-29T14:32:15.123Z","level":"INFO","message":"DNS request received","fields":{"protocol":"udp","type":"A","name":"example.com.","from":"127.0.0.1:54321"}}
{"timemillis":1738161135135,"time":"2026-01-29T14:32:15.135Z","level":"INFO","message":"DNS response sent","fields":{"name":"example.com.","rcode":"NOERROR","answer_count":1,"resolver":"system","duration_ms":12.345}}

Debug Logging (DEBUG=true)

When debug mode is enabled, provides detailed information about pattern matching and resolution:

Text format:

2026/01/29 14:32:15.136 [DEBUG] REQUEST_PATTERN matched pattern=.*\.example\.com$ request=test.example.com
2026/01/29 14:32:15.137 [DEBUG] CNAME_PATTERN matched pattern=.*\.cdn\.* cname=test.cdn.provider.net
2026/01/29 14:32:15.138 [DEBUG] Queried nameserver resolver=explicit

JSON format:

{"timemillis":1738161135136,"time":"2026-01-29T14:32:15.136Z","level":"DEBUG","message":"REQUEST_PATTERN matched","fields":{"pattern":".*\\.example\\.com$","request":"test.example.com"}}
{"timemillis":1738161135137,"time":"2026-01-29T14:32:15.137Z","level":"DEBUG","message":"CNAME_PATTERN matched","fields":{"pattern":".*\\.cdn\\..*","cname":"test.cdn.provider.net"}}

Logging Configuration Examples

Minimal logging (errors only):

nameserver-switcher --log-requests=false --log-responses=false

Full debug logging (text format):

nameserver-switcher --debug --log-requests --log-responses

Production JSON logging:

nameserver-switcher --log-format=json --log-requests --log-responses

Environment variable configuration:

export DEBUG=true
export LOG_REQUESTS=true
export LOG_RESPONSES=true
export LOG_FORMAT=json
nameserver-switcher

For detailed logging documentation, see Logging Documentation.

Usage Examples

Basic Usage

nameserver-switcher \
  --request-patterns=".*\.example\.com$" \
  --cname-patterns=".*\.cdn\.provider\.net$" \
  --request-resolver="8.8.8.8:53" \
  --explicit-resolver="1.1.1.1:53"

Using Environment Variables

export REQUEST_PATTERNS=".*\.example\.com$
.*\.test\.org$"
export CNAME_PATTERNS=".*\.cdn\.com$"
export REQUEST_RESOLVER="8.8.8.8:53"
export EXPLICIT_RESOLVER="1.1.1.1:53"

nameserver-switcher

Query via dig

# DNS query
dig @localhost -p 5353 www.example.com A

Query via gRPC (using grpcurl)

# Install grpcurl if needed: brew install grpcurl

# Resolve a domain
grpcurl -plaintext -d '{"name": "www.example.com", "type": "A"}' \
  localhost:5354 api.v1.NameserverSwitcherService/Resolve

# Get current configuration
grpcurl -plaintext localhost:5354 api.v1.NameserverSwitcherService/GetConfig

# Update request patterns
grpcurl -plaintext -d '{"patterns": [".*\\.newdomain\\.com$"]}' \
  localhost:5354 api.v1.NameserverSwitcherService/UpdateRequestPatterns

# Get statistics
grpcurl -plaintext localhost:5354 api.v1.NameserverSwitcherService/GetStats

Endpoints

HTTP Endpoints

Endpoint Description

/healthz

Health check (returns 200 if healthy)

/readyz

Readiness check (returns 200 if ready)

/livez

Liveness check (always returns 200)

/metrics

Prometheus metrics

Prometheus Metrics

Metric Type Description

nameserver_switcher_requests_total

Counter

Total DNS requests by protocol and type

nameserver_switcher_request_duration_seconds

Histogram

Request processing duration by resolver

nameserver_switcher_resolver_used_total

Counter

Resolver usage count

nameserver_switcher_pattern_matches_total

Counter

Request pattern match count

nameserver_switcher_cname_matches_total

Counter

CNAME pattern match count

nameserver_switcher_errors_total

Counter

Error count by type

nameserver_switcher_active_connections

Gauge

Current active connections

nameserver_switcher_dns_response_codes_total

Counter

DNS response codes

Development

Build

make build

Run Tests

make test

Run with Coverage

make test-coverage

Build Docker Image

make docker

Lint Helm Chart

make helm-lint

License

MIT License

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages