Deploying CoreDNS and StepCA with Kubernetes Manifests Using Quadlet on CoreOS
Fedora CoreOS provides an excellent foundation for running containerized workloads, especially when combined with Podman and systemd. While Kubernetes is a popular choice for container orchestration, it can be complex to set up and manage. Quadlet offers a compelling alternative by allowing you to deploy Kubernetes-style manifests directly through systemd on CoreOS, combining the declarative power of Kubernetes manifests with the simplicity of systemd service management.
This guide demonstrates how to deploy two critical infrastructure components—CoreDNS for DNS management and StepCA for certificate authority services—using Kubernetes manifests orchestrated through Quadlet on Fedora CoreOS.
Understanding the Architecture
Before diving into implementation, let’s understand the overall architecture:
graph TD A[CoreOS Host] --> B[systemd] B --> C[Quadlet] C --> D[Podman] D --> E[CoreDNS Kubernetes Deployment] D --> F[StepCA Kubernetes Deployment]
E --> G[DNS Service] F --> H[Certificate Authority]
I[Client Systems] --> G I --> H
G --> J[(Zone Files)] H --> K[(Certificate Store)]
This architecture enables:
- Declarative Configuration: Using Kubernetes manifests for service definition
- SystemD Integration: Managing container deployments through systemd
- No Kubernetes Required: Running Kubernetes-style resources without a full cluster
- Simplified Management: Standard systemd commands for controlling services
- Domain Name Resolution: Internal DNS with custom zones
- Certificate Infrastructure: Self-hosted PKI for secure communications
Prerequisites
Before beginning, ensure you have:
- Fedora CoreOS 35 or newer
- Root access to the system
- Basic understanding of systemd, Kubernetes manifests, and Podman
- The
kubectl
command-line tool installed
# Check CoreOS versionrpm-ostree status
# Verify kubectl availability or install itwhich kubectl || rpm-ostree install kubectl
# Check Podman version (should be 4.0+)podman --version
Implementation Steps
1. Creating the Configuration Directory
First, let’s create the directory where our Quadlet files will reside:
# Create the systemd directory for Quadlet filessudo mkdir -p /etc/containers/systemd
2. Preparing StepCA Password
For StepCA, we need to generate a secure password and encode it for use in the Kubernetes Secret:
# Generate a secure random passwordSTEPCA_PASSWORD=$(openssl rand -base64 32)
# Base64 encode it for use in Kubernetes SecretSTEPCA_PASSWORD_BASE64=$(echo -n "$STEPCA_PASSWORD" | base64)
# Store the plain password securely for referenceecho "$STEPCA_PASSWORD" | sudo tee /etc/containers/stepca.passwordsudo chmod 600 /etc/containers/stepca.password
# Output the encoded password for use in our manifestecho "Encoded password for use in manifest: $STEPCA_PASSWORD_BASE64"
3. Creating the CoreDNS Deployment Quadlet
Now, let’s create the Quadlet file for CoreDNS:
# Create the CoreDNS Quadlet filesudo tee /etc/containers/systemd/dns-deployment.kube > /dev/null << 'EOF'## CoreDNS Kubernetes Deployment via Quadlet#[Unit]Description=CoreDNS Kubernetes DeploymentAfter=network-online.targetWants=network-online.target
[Kube]# Namespace for DNS servicesNamespace=dns
# The manifest for CoreDNSYaml='''apiVersion: v1kind: Namespacemetadata: name: dns---apiVersion: v1kind: ConfigMapmetadata: name: coredns-config namespace: dnsdata: Corefile: | .:53 { forward . 8.8.8.8 log errors } invinsense:53 { file /etc/coredns/invinsense.db log errors } invinsense.db: | $ORIGIN invinsense. @ 3600 IN SOA coredns.invinsense. admin.invinsense. ( 2023062001 ; serial 7200 ; refresh 3600 ; retry 1209600 ; expire 3600 ; minimum ) @ 3600 IN NS coredns.invinsense. coredns IN A 192.168.122.16 ca IN A 192.168.122.76---apiVersion: apps/v1kind: Deploymentmetadata: name: coredns namespace: dnsspec: replicas: 1 selector: matchLabels: app: coredns template: metadata: labels: app: coredns spec: containers: - name: coredns image: docker.io/coredns/coredns:1.11.1 args: ["-conf", "/etc/coredns/Corefile"] ports: - containerPort: 53 name: dns protocol: UDP - containerPort: 53 name: dns-tcp protocol: TCP volumeMounts: - name: config-volume mountPath: /etc/coredns volumes: - name: config-volume configMap: name: coredns-config---apiVersion: v1kind: Servicemetadata: name: coredns namespace: dnsspec: selector: app: coredns ports: - name: dns port: 53 protocol: UDP - name: dns-tcp port: 53 protocol: TCP type: LoadBalancer'''
[Install]WantedBy=default.targetEOF
4. Creating the StepCA Deployment Quadlet
Next, we’ll create the Quadlet file for StepCA. Note that we need to substitute the actual base64-encoded password:
# Create the StepCA Quadlet file with password substitutionsudo tee /etc/containers/systemd/pki-deployment.kube > /dev/null << EOF## StepCA Kubernetes Deployment via Quadlet#[Unit]Description=StepCA Kubernetes DeploymentAfter=network-online.targetWants=network-online.target
[Kube]# Namespace for PKI servicesNamespace=pki
# The manifest for StepCAYaml='''apiVersion: v1kind: Namespacemetadata: name: pki---apiVersion: v1kind: Secretmetadata: name: stepca-secret namespace: pkitype: Opaquedata: password: ${STEPCA_PASSWORD_BASE64}---apiVersion: v1kind: PersistentVolumeClaimmetadata: name: stepca-data namespace: pkispec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi---apiVersion: apps/v1kind: Deploymentmetadata: name: stepca namespace: pkispec: replicas: 1 selector: matchLabels: app: stepca template: metadata: labels: app: stepca spec: containers: - name: stepca image: smallstep/step-ca:0.24.0 ports: - containerPort: 443 env: - name: STEPCA_INIT_NAME value: "Invinsense CA" - name: STEPCA_INIT_DNS_NAMES value: "ca.invinsense" - name: STEPCA_INIT_ADDRESS value: ":443" - name: STEPCA_PASSWORD valueFrom: secretKeyRef: name: stepca-secret key: password volumeMounts: - name: stepca-data mountPath: /home/step volumes: - name: stepca-data persistentVolumeClaim: claimName: stepca-data---apiVersion: v1kind: Servicemetadata: name: stepca namespace: pkispec: selector: app: stepca ports: - port: 443 targetPort: 443 type: LoadBalancer'''
[Install]WantedBy=default.targetEOF
5. Setting Correct Permissions
Ensure the Quadlet files have appropriate permissions:
# Set correct permissions for Quadlet filessudo chmod 644 /etc/containers/systemd/*.kube
6. Enabling and Starting Services
Now, let’s enable and start the services through systemd:
# Reload systemd to detect new Quadlet filessudo systemctl daemon-reload
# Enable and start CoreDNSsudo systemctl enable --now kube-dns-deployment
# Enable and start StepCAsudo systemctl enable --now kube-pki-deployment
7. Verifying Service Deployment
Let’s verify that our services are running correctly:
# Check systemd services statussudo systemctl status kube-dns-deploymentsudo systemctl status kube-pki-deployment
# Check Kubernetes resources using kubectlkubectl get all -n dnskubectl get all -n pki
# Check the podskubectl get pods -n dnskubectl get pods -n pki
8. Configuring DNS Resolution
To use our CoreDNS server for name resolution:
# Get CoreDNS service IPCOREDNS_IP=$(kubectl get svc -n dns coredns -o jsonpath='{.status.loadBalancer.ingress[0].ip}')echo "CoreDNS service IP: $COREDNS_IP"
# Configure systemd-resolvedsudo mkdir -p /etc/systemd/resolved.conf.d/sudo tee /etc/systemd/resolved.conf.d/dns.conf > /dev/null << EOF[Resolve]DNS=$COREDNS_IPDomains=~invinsenseEOF
# Restart systemd-resolvedsudo systemctl restart systemd-resolved
9. Initializing StepCA Client
To use certificates from our StepCA installation:
# Install step CLI if neededsudo rpm-ostree install step-cli
# Get StepCA service IPSTEPCA_IP=$(kubectl get svc -n pki stepca -o jsonpath='{.status.loadBalancer.ingress[0].ip}')echo "StepCA service IP: $STEPCA_IP"
# Get CA fingerprintCA_POD=$(kubectl get pods -n pki -l app=stepca -o jsonpath='{.items[0].metadata.name}')CA_FINGERPRINT=$(kubectl exec -n pki $CA_POD -- step certificate fingerprint /home/step/certs/root_ca.crt)echo "CA Fingerprint: $CA_FINGERPRINT"
# Bootstrap with CAstep ca bootstrap --ca-url https://$STEPCA_IP --fingerprint $CA_FINGERPRINT
Testing the Infrastructure
Now that we have deployed both CoreDNS and StepCA, let’s verify they’re working properly:
Testing DNS Resolution
# Test DNS resolution through CoreDNSdig @$COREDNS_IP ca.invinsensedig @$COREDNS_IP coredns.invinsense
# Test external domain resolutiondig @$COREDNS_IP google.com
Testing Certificate Issuance
# Request a certificate for a test domainstep ca certificate test.invinsense test.crt test.key
# Verify the certificatestep certificate verify test.crt --roots ~/.step/certs/root_ca.crt
Advanced Configuration
Modifying CoreDNS Zones
To add new DNS records:
-
Edit the Quadlet file:
Terminal window sudo vim /etc/containers/systemd/dns-deployment.kube -
Add your new entries to the zone file in the ConfigMap:
invinsense.db: |$ORIGIN invinsense.# ...existing entries...newservice IN A 192.168.122.100 -
Apply the changes:
Terminal window sudo systemctl daemon-reloadsudo systemctl restart kube-dns-deployment
Adding StepCA Provisioners
To create additional certificate provisioners:
-
Connect to the StepCA pod:
Terminal window CA_POD=$(kubectl get pods -n pki -l app=stepca -o jsonpath='{.items[0].metadata.name}')kubectl exec -it -n pki $CA_POD -- /bin/sh -
Add a new provisioner:
Terminal window step ca provisioner add deploy --type JWK -
Exit the pod:
Terminal window exit
Maintenance Procedures
Viewing Service Logs
# CoreDNS logskubectl logs -n dns -l app=coredns
# StepCA logskubectl logs -n pki -l app=stepca
Updating Services
To update the container images:
# Update CoreDNSkubectl set image deployment/coredns -n dns coredns=docker.io/coredns/coredns:1.11.2
# Update StepCAkubectl set image deployment/stepca -n pki stepca=smallstep/step-ca:0.25.0
Backing Up Critical Data
# Create a backup directorysudo mkdir -p /var/backups/quadlet
# Backup Kubernetes resourceskubectl get all -n dns -o yaml | sudo tee /var/backups/quadlet/dns-backup.yamlkubectl get all -n pki -o yaml | sudo tee /var/backups/quadlet/pki-backup.yaml
# Backup StepCA dataCA_POD=$(kubectl get pods -n pki -l app=stepca -o jsonpath='{.items[0].metadata.name}')kubectl cp -n pki $CA_POD:/home/step /var/backups/quadlet/stepca-data
# Backup configurationssudo tar -czf /var/backups/quadlet/k8s-quadlet-backup.tar.gz \ /etc/containers/systemd/*.kube \ /etc/containers/stepca.password
Troubleshooting Common Issues
DNS Resolution Issues
If DNS resolution isn’t working:
-
Check if CoreDNS is running:
Terminal window kubectl get pods -n dns -
Verify CoreDNS configuration:
Terminal window kubectl describe configmap -n dns coredns-config -
Check DNS connectivity:
Terminal window podman run --rm alpine nslookup -type=a coredns.invinsense $COREDNS_IP
Certificate Issuance Problems
If certificate issuance fails:
-
Check StepCA pod status:
Terminal window kubectl get pods -n pki -
Examine StepCA logs:
Terminal window kubectl logs -n pki -l app=stepca -
Verify connectivity to StepCA:
Terminal window curl -k https://$STEPCA_IP/health
Quadlet Service Failures
If the Quadlet services aren’t starting:
-
Check the systemd service status:
Terminal window sudo systemctl status kube-dns-deployment -
Examine journal logs:
Terminal window sudo journalctl -u kube-dns-deployment -
Validate the Quadlet file syntax:
Terminal window podman kube --dry-run play /etc/containers/systemd/dns-deployment.kube
Security Considerations
When implementing this infrastructure, consider the following security aspects:
- Restrict DNS Access: Configure firewall rules to limit DNS access to your network
- Secure CA Keys: StepCA private keys are critical security assets
- Regular Backups: Back up the StepCA data regularly
- Certificate Rotation: Set up automatic certificate rotation
- Access Controls: Implement RBAC for StepCA access
- Network Segmentation: Place your infrastructure on a separate network
- TLS for CoreDNS: Consider enabling DNS over TLS for sensitive queries
Conclusion
Deploying CoreDNS and StepCA using Kubernetes manifests with Quadlet provides a powerful way to bring Kubernetes-like declarative infrastructure to Fedora CoreOS without the overhead of a full Kubernetes cluster. This approach gives you:
- Simplified management through standard systemd commands
- Declarative configuration using familiar Kubernetes manifests
- Infrastructure as code practices without complex orchestration
- Critical infrastructure services for DNS and certificate management
- Persistent configuration that survives system reboots
By integrating DNS and certificate management, you create a foundation for secure, service-oriented architecture within your environment. The Quadlet approach elegantly bridges the gap between traditional system administration and modern Kubernetes-based configuration, offering the best of both worlds.