Skip to content

Commit

Permalink
feat: support for relay dns
Browse files Browse the repository at this point in the history
  • Loading branch information
mguptahub authored Oct 27, 2024
1 parent 4b69dab commit 268a902
Show file tree
Hide file tree
Showing 12 changed files with 628 additions and 48 deletions.
71 changes: 44 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,30 @@ chmod +x nanodns-darwin-arm64
| Variable | Description | Default | Example |
|----------|-------------|---------|---------|
| DNS_PORT | UDP port for DNS server | 53 | 5353 |
| DNS_RELAY_SERVERS | Comma-separated upstream DNS servers | - | 8.8.8.8:53,1.1.1.1:53 |
| A_xxx | A Record Details | - | - |
| CNAME_xxx | CNAME Record Details | - | - |
| MX_xxx | MX Record Details | - | - |
| TXT_xxx | TXT Record Details | - | - |

### DNS Resolution Strategy

NanoDNS follows this resolution order:

1. Check configured local records first
2. If no local record found and relay is enabled, forward to upstream DNS servers
3. Return first successful response from relay servers

### Record Format

All records use the `|` character as a separator. The general format is:
```

```txt
RECORD_TYPE_NUMBER=domain|value[|ttl]
```

### A Records

```
A_REC1=domain|ip|ttl
A_REC2=domain|service:servicename|ttl
Expand All @@ -115,6 +126,7 @@ A_REC2=api.example.com|service:webapp
```

### CNAME Records

```
CNAME_REC1=domain|target|ttl
```
Expand All @@ -124,6 +136,7 @@ CNAME_REC1=www.example.com|app.example.com|3600
```

### MX Records

```
MX_REC1=domain|priority|mailserver|ttl
```
Expand All @@ -134,6 +147,7 @@ MX_REC2=example.com|20|mail2.example.com
```

### TXT Records

```
TXT_REC1=domain|"text value"|ttl
```
Expand All @@ -143,36 +157,40 @@ TXT_REC1=example.com|v=spf1 include:_spf.example.com ~all|3600
TXT_REC2=_dmarc.example.com|v=DMARC1; p=reject; rua=mailto:dmarc@example.com
```


## Docker Usage

### Using Docker Run

```bash
docker run -d \
--name nanodns \
-p 5353:5353/udp \
-e DNS_PORT=5353 \
-e DNS_RELAY_SERVERS=8.8.8.8:53,1.1.1.1:53 \ # Optional relay configuration
-e "A_REC1=app.example.com|192.168.1.10|300" \
-e "A_REC2=api.example.com|service:webapp" \
-e "TXT_REC1=example.com|v=spf1 include:_spf.example.com ~all" \
ghcr.io/mguptahub/nanodns:latest
```

### Using Docker Compose

```yaml
name: nanodns
services:
dns:
image: ghcr.io/mguptahub/nanodns:latest
environment:
# DNS Server Configuration
- DNS_PORT=5353 # Optional, defaults to 53
# A Records
- DNS_RELAY_SERVERS=8.8.8.8:53,1.1.1.1:53 # Optional relay servers

# Local Records
- A_REC1=app.example.com|service:webapp
- A_REC2=api.example.com|192.168.1.10|300
# TXT Records
- TXT_REC1=example.com|v=spf1 include:_spf.example.com ~all
ports:
- "${DNS_PORT:-5353}:${DNS_PORT:-5353}/udp" # Uses DNS_PORT if set, otherwise 5353
- "${DNS_PORT:-5353}:${DNS_PORT:-5353}/udp"
networks:
- app_network

Expand All @@ -182,13 +200,15 @@ networks:
```
### Kubernetes
For detailed instructions on deploying NanoDNS in Kubernetes, see our [Kubernetes Deployment Guide](kubernetes/README.md).
## Running Without Docker Compose
```bash
# Set environment variables
export DNS_PORT=5353
export DNS_RELAY_SERVERS=8.8.8.8:53,1.1.1.1:53
export A_REC1=app.example.com|192.168.1.10
export TXT_REC1=example.com|v=spf1 include:_spf.example.com ~all

Expand All @@ -199,16 +219,15 @@ export TXT_REC1=example.com|v=spf1 include:_spf.example.com ~all
## Testing Records

```bash
# Test using custom port
# Test local records
dig @localhost -p 5353 app.example.com A

# Test CNAME record
dig @localhost -p 5353 www.example.com CNAME
# Test relay resolution (for non-local domains)
dig @localhost -p 5353 google.com A

# Test MX record
# Test other record types
dig @localhost -p 5353 www.example.com CNAME
dig @localhost -p 5353 example.com MX

# Test TXT record
dig @localhost -p 5353 example.com TXT
```

Expand All @@ -222,6 +241,12 @@ dig @localhost -p 5353 example.com TXT
- Use a port number above 1024 to avoid requiring root privileges
- Set `DNS_PORT=5353` or another high-numbered port

3. DNS Relay Issues:
- Verify upstream DNS servers are accessible
- Check network connectivity to relay servers
- Ensure correct format in DNS_RELAY_SERVERS (comma-separated, with ports)
- Monitor logs for relay errors

## Issues and Support

### Opening New Issues
Expand All @@ -237,7 +262,14 @@ Before opening a new issue:
- Expected vs actual behavior
- Error messages if any

### Join as a Contributor
## Community

- Star the repository to show support
- Watch for updates and new releases
- Join discussions in issues and PRs
- Share your use cases and feedback

## Join as a Contributor

We welcome contributions! Here's how to get started:

Expand All @@ -249,21 +281,6 @@ We welcome contributions! Here's how to get started:
- PR process
- Release workflow

### Community

- Star the repository to show support
- Watch for updates and new releases
- Join discussions in issues and PRs
- Share your use cases and feedback

## Contributing

Please see [CONTRIBUTING.md](CONTRIBUTING.md) for:
- Development setup
- How to create PRs
- Code style guidelines
- Release process


## License and Usage Terms

Expand Down
8 changes: 7 additions & 1 deletion cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ func main() {
// Load records from environment variables
records := dns.LoadRecords()

// Get relay configuration
relayConfig := config.GetRelayConfig()
if relayConfig.Enabled {
log.Printf("DNS relay enabled, using nameservers: %v", relayConfig.Nameservers)
}

// Create DNS handler
handler := dns.NewHandler(records)
handler, _ := dns.NewHandler(records, relayConfig) // Fixed: Pass RelayConfig directly
externaldns.HandleFunc(".", handler.ServeDNS)

// Configure server
Expand Down
2 changes: 2 additions & 0 deletions docs/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ docker run -d \
--name nanodns \
-p 5353:5353/udp \
-e DNS_PORT=5353 \
-e DNS_RELAY_SERVERS=8.8.8.8:53,1.1.1.1:53 \
-e "A_REC1=app.example.com|192.168.1.10|300" \
-e "TXT_REC1=example.com|v=spf1 include:_spf.example.com ~all" \
ghcr.io/mguptahub/nanodns:latest
Expand All @@ -20,6 +21,7 @@ services:
image: ghcr.io/mguptahub/nanodns:latest
environment:
- DNS_PORT=5353 # Optional, defaults to 53
- DNS_RELAY_SERVERS=8.8.8.8:53,1.1.1.1:53
# A Records
- A_REC1=app.example.com|service:webapp
- A_REC2=api.example.com|192.168.1.10|300
Expand Down
15 changes: 7 additions & 8 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -388,18 +388,17 @@ <h2 id="features">Features</h2>
<div class="feature-card">
<div class="feature-icon"><i class="fas fa-cogs"></i></div>
<h3>Easy Configuration</h3>
<p>Configure DNS records using simple environment variables. Supports A, CNAME, MX, and TXT records.</p>
<p>Configure DNS records using simple environment variables. Supports A, CNAME, MX, and TXT records with optional relay DNS.</p>
</div>
<div class="feature-card">
<div class="feature-icon"><i class="fab fa-docker"></i></div>
<h3>Docker Integration</h3>
<p>Seamless integration with Docker and Kubernetes environments. Automatic service discovery and
resolution.</p>
<div class="feature-icon"><i class="fas fa-route"></i></div>
<h3>Relay DNS</h3>
<p>Forward unmatched queries to upstream DNS servers. Support for multiple fallback servers with automatic failover.</p>
</div>
<div class="feature-card">
<div class="feature-icon"><i class="fas fa-tachometer-alt"></i></div>
<h3>Lightweight</h3>
<p>Minimal resource footprint. Perfect for development and production environments.</p>
<div class="feature-icon"><i class="fab fa-docker"></i></div>
<h3>Docker Integration</h3>
<p>Seamless integration with Docker and Kubernetes environments. Automatic service discovery and resolution.</p>
</div>
</section>

Expand Down
1 change: 1 addition & 0 deletions docs/kubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ metadata:
data:
# DNS Server Configuration
DNS_PORT: "53"
DNS_RELAY_SERVERS: 8.8.8.8:53,1.1.1.1:53

# A Records
A_REC1: "app.example.com|service:frontend.default.svc.cluster.local"
Expand Down
52 changes: 50 additions & 2 deletions internal/dns/handler.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
package dns

import (
"fmt"
"log"
"net"
"strings"

"github.com/mguptahub/nanodns/pkg/config"
"github.com/miekg/dns"
)

type Handler struct {
records map[string][]DNSRecord
relay *RelayClient
}

func NewHandler(records map[string][]DNSRecord) *Handler {
func NewHandler(records map[string][]DNSRecord, relayConfig config.RelayConfig) (*Handler, error) {
var relay *RelayClient
if relayConfig.Enabled {
// relay, _ = NewRelayClient(relayConfig)
var err error
relay, err = NewRelayClient(relayConfig)
if err != nil {
return nil, fmt.Errorf("failed to initialize relay client: %w", err)
}
}

return &Handler{
records: records,
}
relay: relay,
}, nil
}

func (h *Handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
Expand All @@ -26,13 +40,47 @@ func (h *Handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
for _, q := range r.Question {
log.Printf("Query for %s (type: %v)", q.Name, dns.TypeToString[q.Qtype])

// Try local records first
if recs, exists := h.records[q.Name]; exists {
for _, rec := range recs {
if answer := h.createAnswer(q, rec); answer != nil {
m.Answer = append(m.Answer, answer)
}
}
}

// If no local records found and relay is enabled, try relay
if len(m.Answer) == 0 && h.relay != nil {
log.Printf("No local records found for %s, attempting relay", q.Name)

// Create a new message for just this question
relayReq := new(dns.Msg)
relayReq.SetQuestion(q.Name, q.Qtype)
relayReq.RecursionDesired = true

relayResp, err := h.relay.Relay(relayReq)
if err != nil {
log.Printf("Relay failed: %v", err)
// Continue with next question without breaking the loop
break
}

// Validate relay response
if relayResp.Rcode != dns.RcodeSuccess {
log.Printf("Relay returned non-success code: %v", dns.RcodeToString[relayResp.Rcode])
continue
}

// Add answers from relay response
m.Answer = append(m.Answer, relayResp.Answer...)
m.Ns = append(m.Ns, relayResp.Ns...)
m.Extra = append(m.Extra, relayResp.Extra...)

// If we got answers from relay, we're not authoritative
if len(relayResp.Answer) > 0 {
m.Authoritative = false
}
}
}

w.WriteMsg(m)
Expand Down
Loading

0 comments on commit 268a902

Please sign in to comment.