Skip to content

Complete CoreDNS and StepCA Setup on CoreOS with Quadlet

Published: at 03:00 PM

Table of Contents

Open Table of Contents

Overview

This guide provides a complete setup for deploying CoreDNS and Smallstep Certificate Authority (StepCA) on Fedora CoreOS using Podman Quadlet. This approach offers a lightweight, systemd-integrated solution for DNS and PKI infrastructure without the complexity of Kubernetes.

Architecture Overview

graph TB
    subgraph "Fedora CoreOS Host"
        subgraph "systemd Services"
            A[container-coredns.service]
            B[container-stepca.service]
        end

        subgraph "Podman Containers"
            C[CoreDNS Container]
            D[StepCA Container]
        end

        subgraph "Storage"
            E[CoreDNS Config Volume]
            F[StepCA Data Volume]
        end

        subgraph "Network"
            G[Podman Network]
            H[Host Network]
        end
    end

    A --> C
    B --> D
    C --> E
    D --> F
    C --> G
    D --> G
    G --> H

    style A fill:#ffd43b,stroke:#fab005,stroke-width:2px
    style B fill:#ffd43b,stroke:#fab005,stroke-width:2px
    style C fill:#4ecdc4,stroke:#087f5b,stroke-width:2px
    style D fill:#74c0fc,stroke:#1971c2,stroke-width:2px

Initial CoreOS Setup

1. Verify CoreOS Environment

# Check CoreOS version
rpm-ostree status

# Verify Podman installation
podman --version

# Check systemd version
systemctl --version

2. Create Required Directories

# Create directories for configurations
sudo mkdir -p /etc/containers/systemd
sudo mkdir -p /etc/containers/coredns
sudo mkdir -p /etc/containers/stepca

CoreDNS Configuration Files

CoreDNS Corefile

Create /etc/containers/coredns/Corefile:

.:53 {
    hosts {
        192.168.122.16 coredns.invinsense
        192.168.122.76 ca.invinsense
        192.168.122.236 nginx.invinsense
        fallthrough
    }
    forward . 8.8.8.8 9.9.9.9 {
        max_concurrent 1000
    }
    cache 300
    log
    errors
}

invinsense:53 {
    file /etc/coredns/invinsense.db
    log
    errors
}

DNS Zone File

Create /etc/containers/coredns/invinsense.db:

$ORIGIN invinsense.
@    3600 IN    SOA coredns.invinsense. admin.invinsense. (
                2024010101 ; serial
                7200       ; refresh (2 hours)
                3600       ; retry (1 hour)
                1209600    ; expire (2 weeks)
                3600       ; minimum (1 hour)
                )

    3600 IN NS coredns.invinsense.

coredns    IN A     192.168.122.16
ca         IN A     192.168.122.76
nginx      IN A     192.168.122.236
*.apps     IN A     192.168.122.100

StepCA Configuration

StepCA Environment File

Create /etc/containers/stepca.env:

STEPCA_PASSWORD=your_secure_password_here

Secure the file:

sudo chmod 600 /etc/containers/stepca.env
sudo chown root:root /etc/containers/stepca.env

Quadlet Configuration Files

CoreDNS Network Configuration

Create /etc/containers/systemd/dns-ca.network:

[Network]
NetworkName=dns-ca-network
Subnet=10.89.0.0/24
Gateway=10.89.0.1
DNS=10.89.0.10

CoreDNS Volume Configuration

Create /etc/containers/systemd/coredns-config.volume:

[Volume]
VolumeName=coredns-config
CopyFiles=/etc/containers/coredns:/etc/coredns

StepCA Volume Configuration

Create /etc/containers/systemd/stepca-data.volume:

[Volume]
VolumeName=stepca-data

CoreDNS Container Configuration

Create /etc/containers/systemd/coredns.container:

[Unit]
Description=CoreDNS DNS Server
After=network-online.target dns-ca.network.service
Wants=network-online.target dns-ca.network.service

[Container]
Image=docker.io/coredns/coredns:1.11.1
ContainerName=coredns
Network=dns-ca.network
PublishPort=53:53/udp
PublishPort=53:53/tcp
Volume=coredns-config.volume:/etc/coredns:ro,z
Environment=COREDNS_CONFIG=/etc/coredns/Corefile
Exec=-conf /etc/coredns/Corefile

# Security settings
SecurityLabelType=spc_t
ReadOnly=false
NoNewPrivileges=true

# Resource limits
MemoryLimit=512M
CPUQuota=50%

[Service]
Restart=always
RestartSec=30
TimeoutStartSec=900

[Install]
WantedBy=default.target

StepCA Container Configuration

Create /etc/containers/systemd/stepca.container:

[Unit]
Description=Smallstep Certificate Authority
After=network-online.target dns-ca.network.service
Wants=network-online.target dns-ca.network.service

[Container]
Image=docker.io/smallstep/step-ca:0.24.0
ContainerName=stepca
Network=dns-ca.network
PublishPort=443:443
Volume=stepca-data.volume:/home/step:z
EnvironmentFile=/etc/containers/stepca.env
Environment=DOCKER_STEPCA_INIT_NAME=Invinsense CA
Environment=DOCKER_STEPCA_INIT_DNS_NAMES=ca.invinsense,localhost
Environment=DOCKER_STEPCA_INIT_ADDRESS=:443

# Security settings
SecurityLabelType=spc_t
ReadOnly=false
NoNewPrivileges=true

# Resource limits
MemoryLimit=512M
CPUQuota=50%

[Service]
Restart=always
RestartSec=30
TimeoutStartSec=900

[Install]
WantedBy=default.target

Deployment Process

1. Deploy the Services

# Reload systemd to detect new Quadlet files
sudo systemctl daemon-reload

# Start and enable the network
sudo systemctl enable --now dns-ca.network

# Start and enable volumes
sudo systemctl enable --now coredns-config.volume
sudo systemctl enable --now stepca-data.volume

# Start and enable containers
sudo systemctl enable --now coredns.container
sudo systemctl enable --now stepca.container

2. Verify Services

# Check service status
sudo systemctl status coredns.container
sudo systemctl status stepca.container

# Check container status
podman ps

# View logs
sudo journalctl -u coredns.container
sudo journalctl -u stepca.container

Post-Deployment Configuration

Configure System DNS

# Update system DNS to use CoreDNS
sudo nmcli connection modify "System eth0" ipv4.dns "127.0.0.1"
sudo nmcli connection up "System eth0"

# Verify DNS resolution
dig @localhost ca.invinsense
nslookup nginx.invinsense

Initialize StepCA Client

# Get CA fingerprint
sudo podman exec stepca step certificate fingerprint /home/step/certs/root_ca.crt

# Bootstrap the client
step ca bootstrap --ca-url https://ca.invinsense --fingerprint <FINGERPRINT>

# Request a test certificate
step ca certificate test.invinsense test.crt test.key

Advanced Configuration

High Availability Setup

graph TB
    subgraph "HA Architecture"
        subgraph "Node 1"
            A1[CoreDNS Primary]
            B1[StepCA Primary]
        end

        subgraph "Node 2"
            A2[CoreDNS Secondary]
            B2[StepCA Standby]
        end

        subgraph "Node 3"
            A3[CoreDNS Secondary]
            B3[StepCA Standby]
        end

        C[Load Balancer]
        D[Shared Storage]
    end

    C --> A1
    C --> A2
    C --> A3

    B1 --> D
    B2 --> D
    B3 --> D

    style C fill:#ff6b6b,stroke:#c92a2a,stroke-width:2px
    style D fill:#74c0fc,stroke:#1971c2,stroke-width:2px

DNS Performance Tuning

Add to CoreDNS Corefile:

.:53 {
    # Performance optimizations
    bufsize 1232
    cache {
        success 9984 300
        denial 9984 5
    }

    # Health checks
    health {
        lameduck 5s
    }
    ready

    # Prometheus metrics
    prometheus :9153
}

Certificate Auto-Renewal Script

Create /usr/local/bin/cert-renewal.sh:

#!/bin/bash
set -euo pipefail

CERT_DIR="/etc/ssl/certs"
KEY_DIR="/etc/ssl/private"
DOMAINS=("nginx.invinsense" "app1.invinsense" "app2.invinsense")

for domain in "${DOMAINS[@]}"; do
    echo "Checking certificate for $domain..."

    if step certificate needs-renewal "$CERT_DIR/$domain.crt"; then
        echo "Renewing certificate for $domain..."
        step ca renew "$CERT_DIR/$domain.crt" "$KEY_DIR/$domain.key" --force

        # Reload services that use this certificate
        systemctl reload nginx || true
    fi
done

Monitoring Configuration

# prometheus-config.yaml for monitoring
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: "coredns"
    static_configs:
      - targets: ["localhost:9153"]

  - job_name: "stepca"
    static_configs:
      - targets: ["localhost:9000"]

Security Hardening

SELinux Configuration

# Create custom SELinux policy for containers
cat > coredns-stepca.te << 'EOF'
module coredns-stepca 1.0;

require {
    type container_t;
    type dns_port_t;
    type http_port_t;
    class tcp_socket name_bind;
    class udp_socket name_bind;
}

#============= container_t ==============
allow container_t dns_port_t:tcp_socket name_bind;
allow container_t dns_port_t:udp_socket name_bind;
allow container_t http_port_t:tcp_socket name_bind;
EOF

# Compile and install the policy
checkmodule -M -m -o coredns-stepca.mod coredns-stepca.te
semodule_package -o coredns-stepca.pp -m coredns-stepca.mod
sudo semodule -i coredns-stepca.pp

Firewall Rules

# Configure firewall
sudo firewall-cmd --add-service=dns --permanent
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reload

Maintenance Operations

Backup Procedures

#!/bin/bash
# backup-dns-ca.sh

BACKUP_DIR="/var/backups/dns-ca"
DATE=$(date +%Y%m%d_%H%M%S)

# Create backup directory
mkdir -p "$BACKUP_DIR"

# Backup CoreDNS configuration
podman volume export coredns-config > "$BACKUP_DIR/coredns-config-$DATE.tar"

# Backup StepCA data
podman volume export stepca-data > "$BACKUP_DIR/stepca-data-$DATE.tar"

# Backup Quadlet configurations
tar -czf "$BACKUP_DIR/quadlet-configs-$DATE.tar.gz" /etc/containers/systemd/

# Keep only last 7 days of backups
find "$BACKUP_DIR" -name "*.tar*" -mtime +7 -delete

Update Procedures

# Update container images
sudo podman pull docker.io/coredns/coredns:latest
sudo podman pull docker.io/smallstep/step-ca:latest

# Update Quadlet files with new image tags
sudo sed -i 's/coredns:1.11.1/coredns:latest/g' /etc/containers/systemd/coredns.container
sudo sed -i 's/step-ca:0.24.0/step-ca:latest/g' /etc/containers/systemd/stepca.container

# Restart services
sudo systemctl restart coredns.container
sudo systemctl restart stepca.container

Troubleshooting Guide

Common Issues and Solutions

  1. Container Fails to Start

    # Check for port conflicts
    sudo ss -tlnp | grep -E ':53|:443'
    
    # Verify SELinux context
    ls -Z /etc/containers/systemd/
    
  2. DNS Resolution Failures

    # Test CoreDNS directly
    dig @localhost invinsense
    
    # Check CoreDNS logs
    sudo podman logs coredns
    
  3. Certificate Issues

    # Verify StepCA is initialized
    sudo podman exec stepca step ca health
    
    # Check certificate chain
    openssl s_client -connect ca.invinsense:443 -showcerts
    

Debug Commands

# Enable debug logging for CoreDNS
sudo podman exec coredns kill -USR1 1

# View detailed container inspect
sudo podman inspect coredns stepca

# Check systemd unit status
systemctl status container-*.service

Performance Metrics

graph LR
    subgraph "Performance Monitoring"
        A[CoreDNS Metrics] --> B[Query Rate]
        A --> C[Cache Hit Ratio]
        A --> D[Response Time]

        E[StepCA Metrics] --> F[Certificate Issued]
        E --> G[Request Latency]
        E --> H[Error Rate]
    end

    style A fill:#4ecdc4,stroke:#087f5b,stroke-width:2px
    style E fill:#74c0fc,stroke:#1971c2,stroke-width:2px

Conclusion

This comprehensive setup provides a production-ready DNS and PKI infrastructure on Fedora CoreOS using Podman Quadlet. The configuration offers:

Key advantages:

By following this guide, you can deploy a robust DNS and certificate infrastructure that’s both secure and maintainable, perfect for internal networks and development environments.