https://github.com/fdeantoni/knot-step-acme
An ACME setup using knot and step-ca
https://github.com/fdeantoni/knot-step-acme
acme-dns knot-dns step-ca
Last synced: 10 months ago
JSON representation
An ACME setup using knot and step-ca
- Host: GitHub
- URL: https://github.com/fdeantoni/knot-step-acme
- Owner: fdeantoni
- License: apache-2.0
- Created: 2025-08-13T17:37:38.000Z (10 months ago)
- Default Branch: main
- Last Pushed: 2025-08-13T22:07:15.000Z (10 months ago)
- Last Synced: 2025-08-13T22:20:00.301Z (10 months ago)
- Topics: acme-dns, knot-dns, step-ca
- Language: Shell
- Homepage:
- Size: 13.7 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Knot DNS + Step CA ACME Development Environment
A Docker Compose setup that provides a local DNS server (Knot DNS) with ACME certificate authority (Step CA) for development and testing.
## Overview
This project sets up:
- **Knot DNS Server**: Authoritative DNS server for the `.test` domain
- **Step CA**: ACME-enabled certificate authority for issuing TLS certificates
- **Custom Network**: Isolated Docker network for service communication
## Architecture
```text
┌─────────────────┐ ┌─────────────────┐
│ Knot DNS │ │ Step CA │
│ 10.0.0.10:53 │◄───┤ 10.0.0.11:9000 │
│ │ │ │
│ • test. zone │ │ • ACME endpoint │
│ • ca.test → CA │ │ • ca.test cert │
└─────────────────┘ └─────────────────┘
```
## Quick Start
1. **Start the services:**
```bash
docker-compose up -d
# or rebuild everything with:
# docker compose up --build --force-recreate -d
```
2. **Wait for services to be healthy:**
```bash
docker-compose ps
```
3. **Test DNS resolution:**
```bash
dig @localhost -p 9053 ca.test A
```
4. **Access Step CA:**
- ACME directory: `https://ca.test:9000/acme/acme/directory`
- Root certificate: `https://ca.test:9000/roots.pem`
## Services
### Knot DNS (`knot`)
- **Port**: `9053` (mapped from container port 53)
- **IP**: `10.0.0.10` (internal network)
- **Zone file**: [`knot/test.zone`](knot/test.zone)
- **Config**: [`knot/knot.conf`](knot/knot.conf)
**DNS Records:**
- `ca.test.` → `10.0.0.11` (Step CA)
- `ns1.test.` → `10.0.0.10` (DNS server)
### Step CA (`step-ca`)
- **Port**: `9000`
- **IP**: `10.0.0.11` (internal network)
- **Domain**: `ca.test`
- **ACME Endpoint**: `/acme/acme/directory`
### Step Secrets (`step-secrets`)
- One-time service that generates CA password
- Creates `/home/step/secrets/password` with random password
## Configuration
### Default DNS Configuration
The Knot DNS server is configured in [`knot/knot.conf`](knot/knot.conf) with:
- Authority for `test.` domain
- HMAC-SHA256 key for dynamic updates automatically generated
- Forwarding to upstream DNS (1.1.1.1, 9.9.9.9) for other domains
### Default Step CA Configuration
- Automatically initialized on first run
- ACME provisioner enabled
- Certificate for `ca.test` domain
- Remote management enabled
### Enable Remote Access
By default everything runs on its own Docker network with DNS mapped to 0.0.0.0:9053 and step-ca mapped to 0.0.0.0:9000 so these are accessble on the hosts LAN IP. However, to make Knot DNS return the correct LAN IP address when queried for `ca.test`, you will need to reconfigure the test.zone file. You can do this as follows:
```bash
# First extract the tsig.key from Knot
./extract-tsig.sh
# Now switch to using the LAN IP for ca.test
./enable-remote.sh
```
### Configuring K3D
If you will run your K3D cluster on the same machine, you can tell it to use the knot and step-ca service:
```bash
k3d cluster create my-cluster \
--k3s-arg "--cluster-dns=10.0.0.10@server:*" \
--k3s-arg "--cluster-domain=cluster.local@server:*" \
--network knot-step-acme_lab \
--wait
```
If your K3D is running on another machine, you can instead tell K3D's CoreDNS to use your remote Knot instance instead. You can do so as follows:
```bash
# Create a config file pointing .test to your Knot instance
DNS_SERVER=""
cat > coredns_custom.yaml < -s
# Example:
./add-subdomain.sh -k ./tsig.key -s myapp
```
This creates: *.myapp.test → in Knot.
4) Test
```bash
dig @localhost -p 9053 app..test A
```
### Using with curl/ACME clients
1. **Get the tsig.key and CA root certificate:**
```bash
./extract-tsig.sh
```
2. **Use with certbot:**
```bash
docker run --rm -it \
--network knot-step-acme_lab \
--dns 10.0.0.10 \
-v $(pwd)/dev_root_ca.pem:/etc/certs/dev_root_ca.pem \
-e REQUESTS_CA_BUNDLE="/etc/certs/dev_root_ca.pem" \
certbot/certbot certonly \
--manual \
--server https://ca.test:9000/acme/acme/directory \
--preferred-challenges dns \
--work-dir /tmp --logs-dir /tmp --config-dir /etc/letsencrypt \
-d "example.test"
```
3. **Use nsupdate with TSIG key:**
```bash
# Create nsupdate script
cat > update.txt << EOF
server localhost 9053
zone test.
update add _acme-challenge.example.test. 60 IN TXT "your-acme-challenge-token"
send
quit
EOF
# Apply the update
nsupdate -k ./tsig.key update.txt
# Clean up
rm update.txt
```
### DNS Testing
```bash
# Test DNS resolution
dig @localhost -p 9053 test SOA
dig @localhost -p 9053 ca.test A
dig @localhost -p 9053 anything.test A
# Test from container network
docker run --rm --network knot-step-acme_lab alpine:latest \
nslookup ca.test 10.0.0.10
```
## Volumes
- `knot-db`: Persistent storage for Knot DNS zone files
- `step-data`: Persistent storage for Step CA configuration and certificates
## Network
- **Name**: `knot-step-acme_lab`
- **Subnet**: `10.0.0.0/24`
- **Gateway**: `10.0.0.1`
## Health Checks
Both services include health checks:
- **Knot**: Verifies DNS SOA response for `test.` domain
- **Step CA**: Verifies HTTPS endpoint accessibility
## Troubleshooting
### Check service logs
```bash
docker-compose logs knot
docker-compose logs step-ca
```
### Verify DNS resolution
```bash
# From host
dig @localhost -p 9053 ca.test A
# From container network
docker exec knot dig @127.0.0.1 ca.test A
```
### Test Step CA
```bash
# Check if CA is responding
curl -sk https://ca.test:9000/health
# Get CA roots
curl -sk https://ca.test:9000/roots.pem
```
### Reset everything
```bash
docker-compose down -v
docker-compose up -d
```
## File Structure
```text
.
├── docker-compose.yml # Main orchestration
├── knot/
│ ├── Dockerfile # Knot DNS container
│ ├── knot.conf # Knot DNS configuration
│ └── test.zone # DNS zone file
└── step-ca/
├── Dockerfile # Step CA container
└── start-step-ca.sh # Step CA initialization script
```
## Requirements
- Docker
- Docker Compose
- (Optional) `dig` command for testing
## Notes
- The `.test` TLD is reserved for testing (RFC 6761)
- Change IP addresses in [`knot/test.zone`](knot/test.zone) if needed for your environment
- Step CA generates a random password on first run, stored in the `step-data` volume
- Services depend on each other: Step CA waits for Knot DNS to be healthy