diff --git a/builds/alpine-unbound/Dockerfile b/builds/alpine-unbound/Dockerfile new file mode 100644 index 0000000..6daf119 --- /dev/null +++ b/builds/alpine-unbound/Dockerfile @@ -0,0 +1,58 @@ +# Alpine Unbound DNS Server - Lightweight Edition +# Multi-architecture support: amd64, arm64, arm/v7 +FROM alpine:3.19 + +LABEL maintainer="homelab" +LABEL description="Lightweight Unbound DNS resolver" + +# Set timezone (can be overridden at runtime) +ENV TZ=UTC + +# Install only essential packages for Unbound DNS +RUN apk add --no-cache \ + # Core DNS + unbound \ + # DNS utilities for debugging + drill \ + bind-tools \ + # Basic networking tools + curl \ + ca-certificates \ + # Timezone data + tzdata \ + # Process management (lightweight alternative to supervisor) + tini \ + && rm -rf /var/cache/apk/* + +# Create necessary directories +RUN mkdir -p /etc/unbound/unbound.conf.d \ + /var/lib/unbound \ + /var/log/unbound \ + /config + +# Download root hints for DNSSEC validation +RUN curl -sSL https://www.internic.net/domain/named.root -o /etc/unbound/root.hints + +# Set proper permissions +RUN chown -R unbound:unbound /var/lib/unbound /var/log/unbound + +# Copy default configuration +COPY unbound.conf /etc/unbound/unbound.conf + +# Create healthcheck script +RUN echo '#!/bin/sh' > /usr/local/bin/healthcheck.sh && \ + echo 'drill @127.0.0.1 -p 5335 google.com > /dev/null 2>&1' >> /usr/local/bin/healthcheck.sh && \ + chmod +x /usr/local/bin/healthcheck.sh + +# Expose DNS port +EXPOSE 5335/tcp 5335/udp + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD /usr/local/bin/healthcheck.sh + +# Use tini as init system for proper signal handling +ENTRYPOINT ["/sbin/tini", "--"] + +# Run unbound in foreground +CMD ["unbound", "-d", "-c", "/etc/unbound/unbound.conf"] diff --git a/builds/alpine-unbound/Dockerfile.amd64 b/builds/alpine-unbound/Dockerfile.amd64 new file mode 100644 index 0000000..b0f2fbf --- /dev/null +++ b/builds/alpine-unbound/Dockerfile.amd64 @@ -0,0 +1,58 @@ +# Alpine Unbound DNS Server - x86_64/AMD64 Edition +FROM --platform=linux/amd64 alpine:3.19 + +LABEL maintainer="homelab" +LABEL description="Lightweight Unbound DNS resolver for x86_64" +LABEL architecture="amd64" + +# Set timezone (can be overridden at runtime) +ENV TZ=UTC + +# Install only essential packages for Unbound DNS +RUN apk add --no-cache \ + # Core DNS + unbound \ + # DNS utilities for debugging + drill \ + bind-tools \ + # Basic networking tools + curl \ + ca-certificates \ + # Timezone data + tzdata \ + # Process management (lightweight alternative to supervisor) + tini \ + && rm -rf /var/cache/apk/* + +# Create necessary directories +RUN mkdir -p /etc/unbound/unbound.conf.d \ + /var/lib/unbound \ + /var/log/unbound \ + /config + +# Download root hints for DNSSEC validation +RUN curl -sSL https://www.internic.net/domain/named.root -o /etc/unbound/root.hints + +# Set proper permissions +RUN chown -R unbound:unbound /var/lib/unbound /var/log/unbound + +# Copy default configuration +COPY unbound.conf /etc/unbound/unbound.conf + +# Create healthcheck script +RUN echo '#!/bin/sh' > /usr/local/bin/healthcheck.sh && \ + echo 'drill @127.0.0.1 -p 5335 google.com > /dev/null 2>&1' >> /usr/local/bin/healthcheck.sh && \ + chmod +x /usr/local/bin/healthcheck.sh + +# Expose DNS port +EXPOSE 5335/tcp 5335/udp + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD /usr/local/bin/healthcheck.sh + +# Use tini as init system for proper signal handling +ENTRYPOINT ["/sbin/tini", "--"] + +# Run unbound in foreground +CMD ["unbound", "-d", "-c", "/etc/unbound/unbound.conf"] diff --git a/builds/alpine-unbound/Dockerfile.arm64 b/builds/alpine-unbound/Dockerfile.arm64 new file mode 100644 index 0000000..febe833 --- /dev/null +++ b/builds/alpine-unbound/Dockerfile.arm64 @@ -0,0 +1,58 @@ +# Alpine Unbound DNS Server - ARM64/aarch64 Edition +FROM --platform=linux/arm64 alpine:3.19 + +LABEL maintainer="homelab" +LABEL description="Lightweight Unbound DNS resolver for ARM64" +LABEL architecture="arm64" + +# Set timezone (can be overridden at runtime) +ENV TZ=UTC + +# Install only essential packages for Unbound DNS +RUN apk add --no-cache \ + # Core DNS + unbound \ + # DNS utilities for debugging + drill \ + bind-tools \ + # Basic networking tools + curl \ + ca-certificates \ + # Timezone data + tzdata \ + # Process management (lightweight alternative to supervisor) + tini \ + && rm -rf /var/cache/apk/* + +# Create necessary directories +RUN mkdir -p /etc/unbound/unbound.conf.d \ + /var/lib/unbound \ + /var/log/unbound \ + /config + +# Download root hints for DNSSEC validation +RUN curl -sSL https://www.internic.net/domain/named.root -o /etc/unbound/root.hints + +# Set proper permissions +RUN chown -R unbound:unbound /var/lib/unbound /var/log/unbound + +# Copy default configuration +COPY unbound.conf /etc/unbound/unbound.conf + +# Create healthcheck script +RUN echo '#!/bin/sh' > /usr/local/bin/healthcheck.sh && \ + echo 'drill @127.0.0.1 -p 5335 google.com > /dev/null 2>&1' >> /usr/local/bin/healthcheck.sh && \ + chmod +x /usr/local/bin/healthcheck.sh + +# Expose DNS port +EXPOSE 5335/tcp 5335/udp + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD /usr/local/bin/healthcheck.sh + +# Use tini as init system for proper signal handling +ENTRYPOINT ["/sbin/tini", "--"] + +# Run unbound in foreground +CMD ["unbound", "-d", "-c", "/etc/unbound/unbound.conf"] diff --git a/builds/alpine-unbound/README.md b/builds/alpine-unbound/README.md new file mode 100644 index 0000000..ac3becc --- /dev/null +++ b/builds/alpine-unbound/README.md @@ -0,0 +1,129 @@ +# Alpine Unbound DNS Server + +A lightweight Alpine-based Unbound DNS resolver container, optimized for homelab use. + +## Features + +- ðŸŠķ **Lightweight**: ~50MB image size (vs ~500MB+ for Ubuntu-based) +- 🔒 **DNSSEC Validation**: Full DNSSEC support with automatic root trust anchor updates +- 🏗ïļ **Multi-Architecture**: Native support for x86_64 and ARM64 (Raspberry Pi, Apple Silicon) +- âĪïļ **Health Checks**: Built-in health monitoring +- 🔧 **Hardened Config**: Security best practices out of the box +- 📊 **Resource Efficient**: Memory limits of 128MB (typically uses ~30-50MB) + +## Quick Start + +### Using Docker Compose + +```bash +docker-compose up -d +``` + +### Building Manually + +```bash +# For x86_64/AMD64 +docker build -f Dockerfile.amd64 -t alpine-unbound:amd64 . + +# For ARM64 (Raspberry Pi 4/5, Apple Silicon) +docker build -f Dockerfile.arm64 -t alpine-unbound:arm64 . + +# Multi-arch build (if using buildx) +./build.sh multiarch +``` + +## Architecture-Specific Dockerfiles + +| File | Architecture | Use Case | +|------|-------------|----------| +| `Dockerfile` | Auto-detect | General use, multi-arch builds | +| `Dockerfile.amd64` | x86_64 | Intel/AMD servers, desktop PCs | +| `Dockerfile.arm64` | ARM64 | Raspberry Pi 4/5, Apple Silicon, AWS Graviton | + +## Configuration + +The default configuration (`unbound.conf`) includes: + +- Listening on port **5335** (to avoid conflicts with other DNS services) +- Access allowed from all RFC1918 private networks +- DNSSEC validation enabled +- Query name minimization for privacy +- Aggressive caching for performance +- Security hardening options + +### Custom Configuration + +Mount your own config files: + +```yaml +volumes: + - ./my-unbound.conf:/etc/unbound/unbound.conf:ro + - ./conf.d/:/etc/unbound/unbound.conf.d/:ro +``` + +## Integration with Pi-hole + +Use as upstream DNS for Pi-hole: + +```yaml +# In Pi-hole's docker-compose.yml +environment: + - PIHOLE_DNS_=172.17.0.1#5335 +``` + +## Comparison with Ubuntu Version + +| Aspect | Alpine | Ubuntu | +|--------|--------|--------| +| Image Size | ~50MB | ~500MB+ | +| Memory Usage | ~30-50MB | ~100-200MB | +| SSH Access | ❌ | ✅ | +| Dev Tools | ❌ | ✅ | +| Shell | ash | zsh (oh-my-zsh) | +| Init System | tini | supervisord | +| Startup Time | <1s | ~5s | + +Choose **Alpine** for: Production DNS, resource-constrained devices, simple deployments. + +Choose **Ubuntu** for: Development, debugging, when you need SSH access or interactive shell. + +## Ports + +| Port | Protocol | Description | +|------|----------|-------------| +| 5335 | TCP/UDP | Unbound DNS | + +## Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `TZ` | `UTC` | Timezone | + +## Health Check + +The container includes automatic health checks using `drill`: + +```bash +drill @127.0.0.1 -p 5335 google.com +``` + +## Troubleshooting + +### Check if Unbound is running +```bash +docker exec alpine_unbound drill @127.0.0.1 -p 5335 google.com +``` + +### View logs +```bash +docker logs alpine_unbound +``` + +### Test DNSSEC validation +```bash +docker exec alpine_unbound drill -D sigok.verteiltesysteme.net @127.0.0.1 -p 5335 +``` + +## License + +MIT diff --git a/builds/alpine-unbound/build.sh b/builds/alpine-unbound/build.sh new file mode 100755 index 0000000..d4be2bb --- /dev/null +++ b/builds/alpine-unbound/build.sh @@ -0,0 +1,134 @@ +#!/bin/bash +# Build script for Alpine Unbound DNS containers +# Supports building for multiple architectures + +set -e + +IMAGE_NAME="${IMAGE_NAME:-alpine-unbound}" +REGISTRY="${REGISTRY:-}" +VERSION="${VERSION:-latest}" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +print_status() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Build for specific architecture +build_single() { + local arch=$1 + local dockerfile="Dockerfile.${arch}" + local tag="${IMAGE_NAME}:${VERSION}-${arch}" + + if [[ -n "$REGISTRY" ]]; then + tag="${REGISTRY}/${tag}" + fi + + print_status "Building for ${arch}..." + docker build -f "$dockerfile" -t "$tag" . + print_status "Successfully built: $tag" +} + +# Build multi-arch image using buildx +build_multiarch() { + local tag="${IMAGE_NAME}:${VERSION}" + + if [[ -n "$REGISTRY" ]]; then + tag="${REGISTRY}/${tag}" + fi + + print_status "Building multi-architecture image..." + + # Check if buildx is available + if ! docker buildx version &>/dev/null; then + print_error "Docker buildx is not available. Please install it first." + exit 1 + fi + + # Create builder if not exists + if ! docker buildx inspect multiarch-builder &>/dev/null; then + print_status "Creating buildx builder..." + docker buildx create --name multiarch-builder --driver docker-container --use + else + docker buildx use multiarch-builder + fi + + # Build and push (or load for local use) + if [[ -n "$REGISTRY" ]] && [[ "$PUSH" == "true" ]]; then + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + -t "$tag" \ + --push \ + . + else + print_warning "Building for local use only. Use PUSH=true REGISTRY=your-registry to push." + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + -t "$tag" \ + --load \ + . + fi + + print_status "Successfully built: $tag" +} + +# Show help +show_help() { + echo "Usage: $0 [COMMAND] [OPTIONS]" + echo "" + echo "Commands:" + echo " amd64 Build for x86_64/AMD64 architecture" + echo " arm64 Build for ARM64/aarch64 architecture" + echo " multiarch Build for all architectures (requires buildx)" + echo " all Build separate images for each architecture" + echo " help Show this help message" + echo "" + echo "Environment variables:" + echo " IMAGE_NAME Image name (default: alpine-unbound)" + echo " VERSION Image version tag (default: latest)" + echo " REGISTRY Docker registry to use (optional)" + echo " PUSH Set to 'true' to push multiarch images" + echo "" + echo "Examples:" + echo " $0 amd64 # Build for x86_64" + echo " $0 arm64 # Build for ARM64" + echo " $0 all # Build for all architectures" + echo " REGISTRY=ghcr.io/user PUSH=true $0 multiarch # Build and push multiarch" +} + +# Main +case "${1:-help}" in + amd64) + build_single "amd64" + ;; + arm64) + build_single "arm64" + ;; + multiarch) + build_multiarch + ;; + all) + build_single "amd64" + build_single "arm64" + ;; + help|--help|-h) + show_help + ;; + *) + print_error "Unknown command: $1" + show_help + exit 1 + ;; +esac diff --git a/builds/alpine-unbound/docker-compose.yml b/builds/alpine-unbound/docker-compose.yml new file mode 100644 index 0000000..6f2bd6a --- /dev/null +++ b/builds/alpine-unbound/docker-compose.yml @@ -0,0 +1,42 @@ +version: "3.9" + +services: + alpine-unbound: + build: + context: . + dockerfile: Dockerfile + image: alpine-unbound:latest + container_name: alpine_unbound + restart: unless-stopped + environment: + - TZ=America/New_York + volumes: + - unbound_config:/etc/unbound/unbound.conf.d + - unbound_data:/var/lib/unbound + ports: + - "5335:5335/tcp" + - "5335:5335/udp" + networks: + - dns_net + healthcheck: + test: [ "CMD", "/usr/local/bin/healthcheck.sh" ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + deploy: + resources: + limits: + memory: 128M + reservations: + memory: 32M + +networks: + dns_net: + driver: bridge + +volumes: + unbound_config: + driver: local + unbound_data: + driver: local diff --git a/builds/alpine-unbound/unbound.conf b/builds/alpine-unbound/unbound.conf new file mode 100644 index 0000000..55f9689 --- /dev/null +++ b/builds/alpine-unbound/unbound.conf @@ -0,0 +1,74 @@ +# Unbound configuration for Alpine container +# Optimized for recursive DNS resolution with DNSSEC + +server: + # Basic settings + verbosity: 1 + num-threads: 2 + + # Interface binding + interface: 0.0.0.0 + port: 5335 + do-ip4: yes + do-ip6: yes + do-udp: yes + do-tcp: yes + + # Access control - allow all private networks + access-control: 127.0.0.0/8 allow + access-control: 10.0.0.0/8 allow + access-control: 172.16.0.0/12 allow + access-control: 192.168.0.0/16 allow + access-control: ::1/128 allow + access-control: fd00::/8 allow + + # Root hints for DNSSEC + root-hints: /etc/unbound/root.hints + + # Trust anchor for DNSSEC validation + auto-trust-anchor-file: /var/lib/unbound/root.key + + # Hide identity and version + hide-identity: yes + hide-version: yes + + # Harden settings + harden-glue: yes + harden-dnssec-stripped: yes + harden-referral-path: yes + use-caps-for-id: yes + + # Prefetch settings for performance + prefetch: yes + prefetch-key: yes + + # Cache settings + cache-min-ttl: 3600 + cache-max-ttl: 86400 + msg-cache-size: 50m + rrset-cache-size: 100m + key-cache-size: 50m + neg-cache-size: 10m + + # Privacy settings + qname-minimisation: yes + aggressive-nsec: yes + + # Logging + logfile: "" + log-queries: no + log-replies: no + log-local-actions: no + log-servfail: yes + + # User/group (Alpine uses 'unbound' user) + username: "unbound" + directory: "/etc/unbound" + chroot: "" + + # PID file + pidfile: "/var/run/unbound.pid" + +# Remote control (disabled for security) +remote-control: + control-enable: no