{"id":30222732,"url":"https://github.com/fdeantoni/knot-step-acme","last_synced_at":"2025-08-14T11:09:38.297Z","repository":{"id":309798098,"uuid":"1037518471","full_name":"fdeantoni/knot-step-acme","owner":"fdeantoni","description":"An ACME setup using knot and step-ca","archived":false,"fork":false,"pushed_at":"2025-08-13T22:07:15.000Z","size":14,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-13T22:20:00.301Z","etag":null,"topics":["acme-dns","knot-dns","step-ca"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fdeantoni.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-13T17:37:38.000Z","updated_at":"2025-08-13T22:11:15.000Z","dependencies_parsed_at":"2025-08-13T22:20:54.899Z","dependency_job_id":"822f1fec-b754-43a9-af35-db4a86c3ad34","html_url":"https://github.com/fdeantoni/knot-step-acme","commit_stats":null,"previous_names":["fdeantoni/knot-step-acme"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/fdeantoni/knot-step-acme","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fdeantoni%2Fknot-step-acme","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fdeantoni%2Fknot-step-acme/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fdeantoni%2Fknot-step-acme/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fdeantoni%2Fknot-step-acme/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fdeantoni","download_url":"https://codeload.github.com/fdeantoni/knot-step-acme/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fdeantoni%2Fknot-step-acme/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270407963,"owners_count":24578345,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-14T02:00:10.309Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acme-dns","knot-dns","step-ca"],"created_at":"2025-08-14T11:09:36.924Z","updated_at":"2025-08-14T11:09:38.242Z","avatar_url":"https://github.com/fdeantoni.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Knot DNS + Step CA ACME Development Environment\n\nA Docker Compose setup that provides a local DNS server (Knot DNS) with ACME certificate authority (Step CA) for development and testing.\n\n## Overview\n\nThis project sets up:\n\n- **Knot DNS Server**: Authoritative DNS server for the `.test` domain\n- **Step CA**: ACME-enabled certificate authority for issuing TLS certificates\n- **Custom Network**: Isolated Docker network for service communication\n\n## Architecture\n\n```text\n┌─────────────────┐    ┌─────────────────┐\n│   Knot DNS      │    │    Step CA      │\n│   10.0.0.10:53  │◄───┤  10.0.0.11:9000 │\n│                 │    │                 │\n│ • test. zone    │    │ • ACME endpoint │\n│ • ca.test → CA  │    │ • ca.test cert  │\n└─────────────────┘    └─────────────────┘\n```\n\n## Quick Start\n\n1. **Start the services:**\n\n   ```bash\n   docker-compose up -d\n   # or rebuild everything with:\n   # docker compose up --build --force-recreate -d\n   ```\n\n2. **Wait for services to be healthy:**\n\n   ```bash\n   docker-compose ps\n   ```\n\n3. **Test DNS resolution:**\n\n   ```bash\n   dig @localhost -p 9053 ca.test A\n   ```\n\n4. **Access Step CA:**\n   - ACME directory: `https://ca.test:9000/acme/acme/directory`\n   - Root certificate: `https://ca.test:9000/roots.pem`\n\n## Services\n\n### Knot DNS (`knot`)\n\n- **Port**: `9053` (mapped from container port 53)\n- **IP**: `10.0.0.10` (internal network)\n- **Zone file**: [`knot/test.zone`](knot/test.zone)\n- **Config**: [`knot/knot.conf`](knot/knot.conf)\n\n**DNS Records:**\n\n- `ca.test.` → `10.0.0.11` (Step CA)\n- `ns1.test.` → `10.0.0.10` (DNS server)\n\n### Step CA (`step-ca`)\n\n- **Port**: `9000`\n- **IP**: `10.0.0.11` (internal network)\n- **Domain**: `ca.test`\n- **ACME Endpoint**: `/acme/acme/directory`\n\n### Step Secrets (`step-secrets`)\n\n- One-time service that generates CA password\n- Creates `/home/step/secrets/password` with random password\n\n## Configuration\n\n### Default DNS Configuration\n\nThe Knot DNS server is configured in [`knot/knot.conf`](knot/knot.conf) with:\n\n- Authority for `test.` domain\n- HMAC-SHA256 key for dynamic updates automatically generated\n- Forwarding to upstream DNS (1.1.1.1, 9.9.9.9) for other domains\n\n### Default Step CA Configuration\n\n- Automatically initialized on first run\n- ACME provisioner enabled\n- Certificate for `ca.test` domain\n- Remote management enabled\n\n### Enable Remote Access\n\nBy 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:\n\n```bash\n# First extract the tsig.key from Knot\n./extract-tsig.sh\n\n# Now switch to using the LAN IP for ca.test\n./enable-remote.sh\n```\n\n### Configuring K3D\n\nIf you will run your K3D cluster on the same machine, you can tell it to use the knot and step-ca service:\n\n```bash\nk3d cluster create my-cluster \\\n  --k3s-arg \"--cluster-dns=10.0.0.10@server:*\" \\\n  --k3s-arg \"--cluster-domain=cluster.local@server:*\" \\\n  --network knot-step-acme_lab \\\n  --wait\n```\n\nIf 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:\n\n```bash\n# Create a config file pointing .test to your Knot instance\nDNS_SERVER=\"\u003cLAN IP exposing remote Knot\u003e\"\ncat \u003e coredns_custom.yaml \u003c\u003cEOF\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: coredns-custom\n  namespace: kube-system\ndata:\n  test.server: |\n    test:53 {\n        errors\n        cache 30\n        forward . $DNS_SERVER\n    }\nEOF\n\n# Launch your K3D cluster with custom CoreDNS properties\nk3d cluster create my-cluster \\\n  --volume ./coredns_custom.yaml:/var/lib/rancher/k3s/server/manifests/coredns-custom.yaml@server:0 \\\n  --wait\n```\n\nYou can test if its working with the following:\n\n```bash\n# Deploy a test pod\nkubectl run test-dns --image=alpine:latest --rm -it -- sh\n\n# Inside the pod, test DNS resolution\nnslookup ca.test\n```\n\n\n### Host Networking\n\nBy default, this setup uses Docker's bridge networking for isolation which is generally the recommended method. If really want, though, you can switch to host networking.\n\nYou can do so by setting `network_mode: host` in the docker-compose.yml file, e.g.:\n\n```yaml\nservices:\n    knot:\n        # ...\n        network_mode: host  # Use host networking\n\n    step-ca:\n        # ...\n        network_mode: host  # Use host networking       \n```\n\nReplace the IP addresses in the `knot/test.zone` with the actual IPs in your network:\n\n```zone\n$ORIGIN test.\n$TTL 60\n@     IN SOA ns1.test. hostmaster.test. (1 1h 15m 30d 2h)\n      IN NS  ns1.test.\nns1   IN A   192.168.1.10    ; LAN IP where this will run\n@     IN A   192.168.1.100   ; Your development machine's LAN IP  \n*     IN A   192.168.1.100   ; Your development machine's LAN IP  \nca    IN A   192.168.1.10    ; LAN IP where this will run\n```\n\nBefore starting, ensure ports 9000 and 9053 are available:\n\n```bash\n# Check if port 9053 is in use\nsudo netstat -tulpn | grep :9053\n```\n\nLastly, create a resolved config that will forward any queries to the test domain to your knot instance:\n\n```bash\nsudo mkdir -p /etc/systemd/resolved.conf.d\nsudo tee /etc/systemd/resolved.conf.d/test-domain.conf \u003c\u003c EOF\n[Resolve]\nDNS=127.0.0.1:9053~test\nEOF\n```\n\nNow restart resolved:\n\n```bash\nsudo systemctl restart systemd-resolved\n```\n\n## Usage Examples\n\n### Add a subdomain to Knot DNS\n\nUse this to create a wildcard subdomain (*.your-sub.test) that points to your machine.\n\n1) Extract the TSIG key and CA root\n- Run [extract-tsig.sh](extract-tsig.sh). This writes [tsig.key](tsig.key) and [dev_root_ca.pem](dev_root_ca.pem).\n  ```bash\n  ./extract-tsig.sh\n  ```\n\n2) Trust the Step CA root on your host (so TLS to https://ca.test:9000 is trusted)\n- macOS:\n  ```bash\n  sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain dev_root_ca.pem\n  ```\n- Debian/Ubuntu:\n  ```bash\n  sudo cp dev_root_ca.pem /usr/local/share/ca-certificates/dev_root_ca.crt\n  sudo update-ca-certificates\n  ```\n- Windows (Administrator PowerShell):\n  ```powershell\n  certutil -addstore -f Root dev_root_ca.pem\n  ```\n\n3) Create the subdomain\n- Run [add-subdomain.sh](add-subdomain.sh). You can provide your IP and desired subdomain, or let the script auto-detect/generate them.\n  ```bash\n  ./add-subdomain.sh -k ./tsig.key -i \u003cyour-ip\u003e -s \u003csubdomain\u003e\n  # Example:\n  ./add-subdomain.sh -k ./tsig.key -s myapp\n  ```\n  This creates: *.myapp.test → \u003cyour-ip\u003e in Knot.\n\n4) Test\n  ```bash\n  dig @localhost -p 9053 app.\u003csubdomain\u003e.test A\n  ```\n\n### Using with curl/ACME clients\n\n1. **Get the tsig.key and CA root certificate:**\n\n   ```bash\n   ./extract-tsig.sh\n   ```\n\n2. **Use with certbot:**\n\n    ```bash\n    docker run --rm -it \\\n        --network knot-step-acme_lab \\\n        --dns 10.0.0.10 \\\n        -v $(pwd)/dev_root_ca.pem:/etc/certs/dev_root_ca.pem \\\n        -e REQUESTS_CA_BUNDLE=\"/etc/certs/dev_root_ca.pem\" \\\n        certbot/certbot certonly \\\n        --manual \\\n        --server https://ca.test:9000/acme/acme/directory \\\n        --preferred-challenges dns \\\n        --work-dir /tmp --logs-dir /tmp --config-dir /etc/letsencrypt \\\n        -d \"example.test\"\n    ```\n\n3. **Use nsupdate with TSIG key:**\n\n    ```bash\n    # Create nsupdate script\n    cat \u003e update.txt \u003c\u003c EOF\n    server localhost 9053\n    zone test.\n    update add _acme-challenge.example.test. 60 IN TXT \"your-acme-challenge-token\"\n    send\n    quit\n    EOF\n\n    # Apply the update\n    nsupdate -k ./tsig.key update.txt\n\n    # Clean up\n    rm update.txt    \n    ```\n\n### DNS Testing\n\n```bash\n# Test DNS resolution\ndig @localhost -p 9053 test SOA\ndig @localhost -p 9053 ca.test A\ndig @localhost -p 9053 anything.test A\n\n# Test from container network\ndocker run --rm --network knot-step-acme_lab alpine:latest \\\n  nslookup ca.test 10.0.0.10\n```\n\n## Volumes\n\n- `knot-db`: Persistent storage for Knot DNS zone files\n- `step-data`: Persistent storage for Step CA configuration and certificates\n\n## Network\n\n- **Name**: `knot-step-acme_lab`\n- **Subnet**: `10.0.0.0/24`\n- **Gateway**: `10.0.0.1`\n\n## Health Checks\n\nBoth services include health checks:\n\n- **Knot**: Verifies DNS SOA response for `test.` domain\n- **Step CA**: Verifies HTTPS endpoint accessibility\n\n## Troubleshooting\n\n### Check service logs\n\n```bash\ndocker-compose logs knot\ndocker-compose logs step-ca\n```\n\n### Verify DNS resolution\n\n```bash\n# From host\ndig @localhost -p 9053 ca.test A\n\n# From container network\ndocker exec knot dig @127.0.0.1 ca.test A\n```\n\n### Test Step CA\n\n```bash\n# Check if CA is responding\ncurl -sk https://ca.test:9000/health\n\n# Get CA roots\ncurl -sk https://ca.test:9000/roots.pem\n```\n\n### Reset everything\n\n```bash\ndocker-compose down -v\ndocker-compose up -d\n```\n\n## File Structure\n\n```text\n.\n├── docker-compose.yml          # Main orchestration\n├── knot/\n│   ├── Dockerfile             # Knot DNS container\n│   ├── knot.conf              # Knot DNS configuration\n│   └── test.zone              # DNS zone file\n└── step-ca/\n    ├── Dockerfile             # Step CA container\n    └── start-step-ca.sh       # Step CA initialization script\n```\n\n## Requirements\n\n- Docker\n- Docker Compose\n- (Optional) `dig` command for testing\n\n## Notes\n\n- The `.test` TLD is reserved for testing (RFC 6761)\n- Change IP addresses in [`knot/test.zone`](knot/test.zone) if needed for your environment\n- Step CA generates a random password on first run, stored in the `step-data` volume\n- Services depend on each other: Step CA waits for Knot DNS to be healthy\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffdeantoni%2Fknot-step-acme","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffdeantoni%2Fknot-step-acme","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffdeantoni%2Fknot-step-acme/lists"}