Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.git
.gitignore
README.md
LICENSE
docs/
examples/
.github/
.pre-commit-config.yaml
.goreleaser.yml
*.md
32 changes: 32 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Build stage
FROM golang:1.24-alpine AS builder

WORKDIR /app

# Copy go mod files
COPY go.mod go.sum ./

# Download dependencies
RUN go mod download

# Copy source code
COPY . .

# Build the application
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o sinkzone .

# Final stage
FROM alpine:latest

RUN apk --no-cache add ca-certificates

WORKDIR /root/

# Copy the binary from builder stage
COPY --from=builder /app/sinkzone .

# Expose ports for DNS resolver and API
EXPOSE 5353 8080

# Run the resolver with API and DNS ports
CMD ["./sinkzone", "resolver", "--api-port", "8080", "--port", "5353"]
98 changes: 98 additions & 0 deletions README-Docker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Sinkzone with Unbound Docker Setup

A Docker Compose setup for Sinkzone with Unbound as the upstream DNS resolver.

## Quick Start

1. **Create environment file:**
```bash
cat > .env << 'EOF'
DOCKER_NETWORK_SUBNET=172.30.0.0/16
SINKZONE_IP=172.30.0.1
UNBOUND_IP=172.30.0.2
SINKZONE_UPSTREAM_NAMESERVERS=unbound
EOF
```

2. **Start services:**
```bash
docker compose up -d
```

3. **Test DNS resolution:**
```bash
dig @127.0.0.1 -p 5353 google.com
```

## Usage

### DNS Resolution
- **Sinkzone**: `dig @127.0.0.1 -p 5353 google.com`
- **Direct Unbound**: `dig @127.0.0.1 -p 5335 google.com`

### API Access
```bash
# Health check
curl http://localhost:8080/health

# View DNS queries
curl http://localhost:8080/api/queries

# Enable focus mode
curl -X POST http://localhost:8080/api/focus \
-H "Content-Type: application/json" \
-d '{"enabled": true, "duration": "1h"}'
```

### Sinkzone Commands via Docker Exec

```bash
# Terminal UI
docker compose exec -it sinkzone ./sinkzone tui --api-url http://localhost:8080

# Monitor DNS requests
docker compose exec -it sinkzone ./sinkzone monitor --api-url http://localhost:8080

# Add domain to allowlist
docker compose exec -it sinkzone ./sinkzone allowlist add google.com

# Remove domain from allowlist
docker compose exec -it sinkzone ./sinkzone allowlist remove google.com

# List allowlist
docker compose exec -it sinkzone ./sinkzone allowlist list

# Enable focus mode
docker compose exec -it sinkzone ./sinkzone focus start

# Disable focus mode
docker compose exec -it sinkzone ./sinkzone focus --disable

# Check status
docker compose exec -it sinkzone ./sinkzone status
```

## Ports

- **5353**: Sinkzone DNS server
- **8080**: Sinkzone API server
- **5335**: Unbound DNS server (direct access)

## Architecture

```
Client → Sinkzone (5353) → Unbound (172.30.0.2) → Internet
```

## Troubleshooting

```bash
# View logs
docker compose logs -f

# Restart services
docker compose restart

# Rebuild and restart
docker compose up -d --build
```
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,22 @@ go build -o sinkzone .

---

<details>
<summary><b>👉 Docker Setup</b></summary>

For Docker-based deployment with Unbound as the upstream DNS resolver, see [README-Docker.md](README-Docker.md) for complete instructions.

```bash
# Quick start with Docker
git clone https://github.com/berbyte/sinkzone.git
cd sinkzone
docker compose up -d
```

</details>

---


## Documentation

Expand Down
49 changes: 49 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
networks:
dns_net:
driver: bridge
ipam:
config:
- subnet: ${DOCKER_NETWORK_SUBNET:-172.30.0.0/16} # Custom subnet for the Docker network

services:
sinkzone:
build: .
container_name: sinkzone
restart: unless-stopped
ports:
# DNS Ports
- "5353:5353/tcp"
- "5353:5353/udp"
# For production use, set to port 53
#- "53:53/tcp"
#- "53:53/udp"
# API Port
- "8080:8080/tcp"
environment:
# Set sinkzone to use unbound as upstream DNS
SINKZONE_UPSTREAM_NAMESERVERS: "unbound"
cap_add:
- NET_ADMIN # Required for network configuration
depends_on:
- unbound
networks:
dns_net:

unbound:
container_name: unbound
image: mvance/unbound:latest # Official Unbound container
command: >
sh -c "if [ ! -f /opt/unbound/etc/unbound/unbound.conf ]; then \
echo 'server:' > /opt/unbound/etc/unbound/unbound.conf && \
echo ' interface: 0.0.0.0' >> /opt/unbound/etc/unbound/unbound.conf && \
echo ' access-control: 172.30.0.0/16 allow' >> /opt/unbound/etc/unbound/unbound.conf; \
fi; \
touch /opt/unbound/etc/unbound/a-records.conf /opt/unbound/etc/unbound/srv-records.conf /opt/unbound/etc/unbound/forward-records.conf && \
exec unbound -d"
ports:
- "5335:53/tcp"
- "5335:53/udp"
restart: unless-stopped
networks:
dns_net:
12 changes: 12 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ func Load() (*Config, error) {
return nil, fmt.Errorf("failed to save default config: %w", err)
}
}

// Check for environment variable override
if envNameservers := os.Getenv("SINKZONE_UPSTREAM_NAMESERVERS"); envNameservers != "" {
// Split by comma if multiple nameservers are provided
nameservers := strings.Split(envNameservers, ",")
// Trim whitespace from each nameserver
for i, ns := range nameservers {
nameservers[i] = strings.TrimSpace(ns)
}
cfg.UpstreamNameservers = nameservers
}

return cfg, nil
}

Expand Down
Loading