SPORK ACME Server Guide

v0.4.0-beta.4 | 2,401 tests | 0 warnings | 0 audit vulnerabilities | 12/12 CI jobs green | FIPS 140-3 default

SPORK ACME Server is a standalone, RFC 8555-compliant certificate authority that works with certbot, acme.sh, and any ACME client out of the box. It includes a built-in micro-CA, interactive setup wizard, admin dashboard, and FIPS 140-3 mode.

What you get: A single static binary (18 MB, musl-linked, zero dependencies) that runs on any Linux x86_64 system. 5 deployment modes: 2-tier, 3-tier, subordinate to Windows CA, WinRM bridge, or import PFX. Supports ECDSA, RSA, Ed25519, ML-DSA (post-quantum), SLH-DSA, and hybrid composite algorithms.

Download

Download the standalone binary and run the setup wizard:

# Download
curl -fSL -o spork-acme \
  https://rayketcham.com/CRLs/sp0rk/static/spork-acme-0.4.0-beta.4-linux-x86_64

# Make executable
chmod +x spork-acme

# Run the interactive setup wizard (requires root)
sudo ./spork-acme
Static binary: This is a musl-linked binary that runs on any Linux x86_64 system (Rocky 8+, Ubuntu 18.04+, Debian 10+, Alpine 3.12+). No glibc or OpenSSL required.

System Requirements

RequirementDetails
Architecturex86_64 (amd64)
Operating SystemLinux (any distribution with kernel 4.x+)
RAM256 MB minimum, 512 MB recommended
Disk100 MB for binary + 1 GB for CA data
NetworkPort 6446 (ACME/HTTPS, configurable)
DependenciesNone — statically linked against musl libc

Setup Wizard

The setup wizard runs automatically on first launch. It generates a complete CA hierarchy, TLS certificates, systemd service, and starts the ACME server.

sudo ./spork-acme

Deployment Types

OptionDescriptionRecommended
2-tierRoot CA + Issuing CA (root stays offline)Yes, for testing
3-tierRoot + Policy CA + Issuing CAEnterprise production
SubordinateSubordinate to Windows CA (CSR export/import)AD-integrated environments
WinRM BridgeDirect subordination via WinRM to Windows CAAutomated AD environments
Import PFXImport existing CA from PFX/PKCS#12 fileMigration from existing CA
Recommended: Choose 2-tier for testing. It creates a root CA (kept offline after setup) and an issuing CA that handles day-to-day certificate signing.

Algorithm Selection

AlgorithmTypeNotes
ECDSA P-256ClassicalDefault. Fastest, broadest compatibility. FIPS-approved.
ECDSA P-384ClassicalHigher security margin. FIPS-approved.
RSA 3072 / 4096ClassicalLegacy compatibility. FIPS-approved (≥3072).
ML-DSA-65Post-QuantumNIST FIPS 204. Requires PQC-aware clients.
ML-DSA-65 + ECDSA P-256HybridComposite signature. Best of both worlds.
ML-DSA-87 + ECDSA P-384HybridMaximum security for both classical and PQC.
SLH-DSA-SHA2-128sPost-QuantumHash-based, conservative security assumptions.
FIPS by default: The official binary ships with FIPS 140-3 enabled. Ed25519 and RSA-2048 are not available in the setup wizard. If you need these, build from source with --no-default-features --features ceremony.
PQC note: Post-quantum and hybrid algorithms produce larger certificates and signatures. If your clients are standard browsers/tools, start with ECDSA P-256 and migrate later.

DN Builder

The setup wizard includes an interactive Distinguished Name builder for configuring your CA subject. It supports multi-level attributes with editing and reordering.

AttributeDescriptionExample
CN (Common Name)CA display nameMy Org Issuing CA
O (Organization)Organization name (removable)My Organization Inc
OU (Org Unit)Multi-level OU supportIT Security, PKI Operations
C (Country)2-letter ISO codeUS
ST (State)State or provinceCalifornia
L (Locality)CitySan Francisco
DC (Domain Component)Auto-parsed from domainDC=example,DC=com

The builder provides a review screen showing the full DN preview:

Subject DN: CN=My Org Issuing CA, OU=PKI Operations, O=My Organization Inc, C=US

  [1] Edit attributes
  [2] Reorder attributes
  [3] Remove an attribute
  [4] Accept and continue
New in beta.13: You can now remove the O= attribute and reorder DN attributes in any order. Previous versions required O= to be present.

Bind Address & Ports

SettingDefaultDescription
Bind address0.0.0.0IP to listen on (127.0.0.1 for localhost-only)
ACME port6446HTTPS port for ACME protocol and admin UI
External URLAuto-detectedPublic URL clients use to reach this server

The wizard also prompts for:

Save your credentials now. The SuperAdmin PFX password and recovery code are displayed only once during setup. These credentials cannot be recovered later. You need them to access the admin dashboard and perform administrative operations.

FIPS Mode

The official SPORK ACME binary ships with FIPS 140-3 enabled by default. All cryptographic operations use aws-lc-rs (NIST Certificate #4816) as the backend. No special build steps, flags, or separate downloads are needed — one binary, FIPS by default.

Setup Wizard Toggle

During setup, the wizard asks you to choose FIPS mode:

  FIPS 140-3 MODE
  ═══════════════

  This binary uses aws-lc-rs (NIST Certificate #4816)
  for all cryptographic operations.

  > FIPS On  - FIPS-approved algorithms only (Recommended)
    FIPS Off - All algorithms available (Ed25519, RSA-2048, PQC)

FIPS Algorithm Matrix

AlgorithmFIPS StatusNotes
ECDSA P-256ApprovedRecommended default
ECDSA P-384ApprovedHigher security margin
RSA ≥3072ApprovedRSA-2048 rejected
RSA-PSS ≥3072Approved
Ed25519Not approvedAvailable with FIPS Off
RSA-2048Not approvedAvailable with FIPS Off
ML-DSA-*Approved (FIPS 204)Requires PQC feature flag
SLH-DSA-*Approved (FIPS 205)Requires PQC feature flag

Verifying FIPS Mode

# Check if binary was built with FIPS
./spork-acme --version
# Output includes "fips" in feature list

# Server logs show FIPS status at startup
sudo journalctl -u spork-acme | grep -i fips
FIPS compliance note: FIPS mode enforces algorithm restrictions at the cryptographic layer. The ACME protocol behavior, challenge validation, and certificate issuance logic are identical. Only the underlying cryptographic primitives change.

Domain Policy

SPORK ACME Server uses a deny-by-default domain policy. All certificate requests are rejected unless the requested domain matches an --allow-domain pattern.

Configuration

# Allow all domains (universal wildcard)
sudo spork-acme -s /opt/spork-acme --allow-domain "**"

# Allow a specific domain and all subdomains
sudo spork-acme -s /opt/spork-acme --allow-domain "**.example.com"

# Allow only single-level subdomains
sudo spork-acme -s /opt/spork-acme --allow-domain "*.example.com"

# Allow an exact domain
sudo spork-acme -s /opt/spork-acme --allow-domain "server1.example.com"

# Multiple patterns (repeat the flag)
sudo spork-acme -s /opt/spork-acme \
  --allow-domain "**.internal.corp" \
  --allow-domain "*.lab.local"

Wildcard Patterns

PatternMatchesDoes Not Match
**Everything
**.example.coma.example.com, b.c.example.comexample.com
*.example.coma.example.comb.c.example.com
server1.example.comserver1.example.comserver2.example.com
Deny-by-default: If you do not configure --allow-domain, the server will reject all certificate requests. During the setup wizard, you are prompted to configure allowed domains. Use --allow-domain "**" for testing environments.

Service Management

The setup wizard installs two systemd units:

UnitPurpose
spork-acme.serviceMain ACME server process
spork-acme-crl.timerPeriodic CRL regeneration (every 12 hours)

Common Commands

# Check server status
sudo systemctl status spork-acme

# Start / stop / restart
sudo systemctl start spork-acme
sudo systemctl stop spork-acme
sudo systemctl restart spork-acme

# Enable at boot
sudo systemctl enable spork-acme

# View live logs
sudo journalctl -u spork-acme -f

# View CRL timer status
sudo systemctl list-timers spork-acme-crl.timer

Management Menu

Running spork-acme with an existing installation presents a management menu:

sudo spork-acme
SPORK ACME Server v0.4.0-beta.4
Existing installation detected at /opt/spork-acme/

  1) Configure    - Web UI, TLS certificates, proxy settings
  2) Backup       - Create/restore encrypted backups, scheduling
  3) Security     - Audit, hardening, permissions, user management
  4) Upgrade      - Upgrade binary with full backup
  5) Status       - Service health and configuration review
  6) Uninstall    - Clean removal with optional backup

Select an option [1-6]:

Server Mode (No Systemd)

Run the server directly in the foreground for debugging:

Replace placeholder passphrases with a strong, unique value. Never use example values in production.
sudo SPORK_LOCKBOX_PASSPHRASE="your-passphrase" \
  spork-acme -s /opt/spork-acme

Admin Dashboard

The admin portal is served at https://hostname:6446/admin and provides certificate inventory, revocation status, CA health, and audit logs.

Authentication Methods

MethodSetupSecurity Level
HTTP Basic Auth--admin-user / --admin-passwordStandard
mTLS (Client Cert)Import SuperAdmin PFX into browserHigh
Bearer TokenSet via Configure > Web UIStandard

mTLS Setup

  1. During setup, the wizard generates a SuperAdmin PFX at /opt/spork-acme/admin/superadmin.pfx
  2. Copy the PFX to your workstation
  3. Import into your browser:
    • Firefox: Settings → Privacy & Security → Certificates → View Certificates → Import
    • Chrome: Settings → Privacy and Security → Security → Manage certificates → Import
  4. Navigate to https://hostname:6446/admin and select the client certificate when prompted

Admin Certificate Hierarchy

LevelCapabilities
SuperAdminFull control, issue admin certs, factory reset
AdminRevoke certs, manage CRLs, issue certs
OperatorIssue certs, view logs
ViewerView-only access

Dashboard Features

Read-only by design: The WebUI cannot perform destructive operations. Revocation, CRL signing, and admin certificate management require CLI access with proper admin certificate authentication.

Test with Certbot

Verify the ACME server works with a certbot dry-run:

# Dry-run test (does not save a certificate)
certbot certonly --standalone \
  --server https://localhost:6446/acme/directory \
  --no-verify-ssl \
  --agree-tos \
  -d test.example.com \
  --dry-run

Expected output:

Simulating a certificate request for test.example.com
The dry run was successful.

Issue a real certificate:

certbot certonly --standalone \
  --server https://localhost:6446/acme/directory \
  --no-verify-ssl \
  --agree-tos \
  -d myhost.internal.example.com
--no-verify-ssl: Required because certbot does not trust your private CA by default. After adding the CA certificate to your system trust store (see platform guides below), you can remove this flag.

Rocky Linux / RHEL / CentOS Setup

Important: Rocky Linux has SELinux and firewalld enabled by default. Both require configuration for SPORK ACME to work correctly.

Install Certbot

sudo dnf install -y epel-release
sudo dnf install -y certbot

Open the Firewall

sudo firewall-cmd --add-port=6446/tcp --permanent
sudo firewall-cmd --reload

SELinux Configuration

# Allow proxy connections (if using reverse proxy)
sudo setsebool -P httpd_can_network_connect 1

# Set file context for spork-acme binary
sudo semanage fcontext -a -t bin_t '/opt/spork-acme/bin/spork-acme'
sudo restorecon -v /opt/spork-acme/bin/spork-acme

# If denials persist, generate a targeted policy module
sudo ausearch -m avc -ts recent | audit2allow -M spork-acme
sudo semodule -i spork-acme.pp
Do not disable SELinux. Use audit2allow to create targeted policy modules rather than switching to permissive mode.

Trust the CA Certificate

sudo cp /opt/spork-acme/ca/ca.crt /etc/pki/ca-trust/source/anchors/spork-ca.crt
sudo update-ca-trust

# Verify
openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt /opt/spork-acme/ca/ca.crt

Keystore Passphrase for Service Mode

Replace placeholder passphrases with a strong, unique value. Never use example values in production.
sudo mkdir -p /etc/spork-acme
sudo tee /etc/spork-acme/env > /dev/null <<'EOF'
SPORK_LOCKBOX_PASSPHRASE=your-passphrase-here
EOF
sudo chmod 600 /etc/spork-acme/env
sudo chown root:root /etc/spork-acme/env

Ubuntu / Debian Setup

Install Certbot

sudo apt update
sudo apt install -y certbot

Trust the CA Certificate

sudo cp /opt/spork-acme/ca/ca.crt /usr/local/share/ca-certificates/spork-ca.crt
sudo update-ca-certificates

Firewall (if UFW is enabled)

sudo ufw allow 6446/tcp
sudo ufw reload

Ubuntu and Debian do not run SELinux by default, so no additional security context configuration is needed. The keystore passphrase setup is the same as Rocky Linux (see above).

ACME Client Quick Start

The spork CLI includes a built-in ACME client that handles the full certificate lifecycle. It works with any RFC 8555-compliant ACME server.

# 1. Download and install the spork client
curl -fSL -o spork-client-installer \
  https://rayketcham.com/CRLs/sp0rk/static/spork-client-0.3.0-beta.9-linux-x86_64-installer
chmod +x spork-client-installer
sudo ./spork-client-installer

# 2. Request a certificate
spork acme certonly \
  -d myhost.example.com \
  --server https://acme.internal:6446/acme/directory \
  --webroot /var/www/html \
  --agree-tos

# 3. Check the issued certificate
spork acme status --domain myhost.example.com

# 4. (Optional) Deploy to Apache with automatic backup
sudo spork acme install \
  -d myhost.example.com \
  --server https://acme.internal:6446/acme/directory \
  --apache \
  --agree-tos

# 5. Set up automated renewal (cron)
echo "0 3 * * * spork acme renew -d myhost.example.com -s https://acme.internal:6446/acme/directory" \
  | sudo crontab -
Where certificates are stored: By default, certonly saves certificates to ~/.spork/certs/<domain>/. Each domain directory contains fullchain.pem (certificate + chain) and privkey.pem (private key).

certonly — Request a Certificate

Performs the full ACME flow in one command: register account, create order, complete challenge, generate key + CSR, finalize, and download.

spork acme certonly [OPTIONS] -d <DOMAIN> -s <SERVER>
FlagDescription
-d, --domain <DOMAIN>Domain name(s). Repeat for SANs: -d a.com -d b.com
-s, --server <URL>ACME server directory URL
--webroot <DIR>Web root for HTTP-01 challenge
--standaloneBuilt-in HTTP server on port 80 for challenge
--dnsDNS-01 challenge (manual TXT record, required for wildcards)
-o, --output-dir <DIR>Custom output directory
--agree-tosAgree to terms of service

Examples

# Webroot challenge
spork acme certonly \
  -d myhost.example.com \
  --server https://acme.internal:6446/acme/directory \
  --webroot /var/www/html --agree-tos

# Multiple SANs
spork acme certonly \
  -d example.com -d www.example.com -d mail.example.com \
  --server https://acme.internal:6446/acme/directory \
  --webroot /var/www/html --agree-tos

# DNS-01 challenge (for wildcards)
spork acme certonly \
  -d example.com \
  --server https://acme.internal:6446/acme/directory \
  --dns --agree-tos

install — Request and Deploy

EXPERIMENTAL: The install command modifies your web server configuration. A backup is automatically created. Use spork acme rollback to restore.

Performs the full certonly flow, then detects your web server, backs up its config, deploys the certificate, tests, and reloads. Automatic rollback on failure.

spork acme install [OPTIONS] -d <DOMAIN> -s <SERVER>
FlagDescription
--apacheDeploy to Apache (Linux)
--nginxDeploy to Nginx (Linux)
--iisDeploy to IIS (Windows)
--backup-dir <DIR>Custom backup directory

renew — Renew a Certificate

Checks if a previously issued certificate is near expiry (within 30 days) and re-requests it.

# Check if renewal is needed
spork acme renew --domain myhost.example.com \
  --server https://acme.internal:6446/acme/directory --dry-run

# Renew if within 30 days of expiry
spork acme renew --domain myhost.example.com \
  --server https://acme.internal:6446/acme/directory

# Force renewal regardless
spork acme renew --domain myhost.example.com \
  --server https://acme.internal:6446/acme/directory --force

Automated Renewal

# Cron: renew daily at 3 AM
0 3 * * * spork acme renew -d myhost.example.com -s https://acme.internal:6446/acme/directory

status — Show Certificate Status

spork acme status --domain myhost.example.com
Certificate Status

  Subject:     myhost.example.com
  SANs:        myhost.example.com, www.myhost.example.com
  Valid From:  2026-02-11 00:00:00 UTC
  Valid Until: 2026-05-12 00:00:00 UTC
  Days Left:   90
  OK

rollback — Restore Web Server Config

# Rollback to most recent backup
sudo spork acme rollback

# Rollback to a specific backup
sudo spork acme rollback --backup-id 2026-02-11-120000

# List available backups
spork acme backups

Apache Integration

The install command locates the virtual host for your domain, updates SSLCertificateFile and SSLCertificateKeyFile directives, runs apachectl configtest, and reloads.

sudo spork acme install \
  -d myhost.example.com \
  --server https://acme.internal:6446/acme/directory \
  --apache --agree-tos

Manual deployment:

# After certonly, update your vhost config:
SSLCertificateFile    /home/user/.spork/certs/myhost.example.com/fullchain.pem
SSLCertificateKeyFile /home/user/.spork/certs/myhost.example.com/privkey.pem

# Test and reload
sudo apachectl configtest
sudo systemctl reload apache2   # or httpd on RHEL

Nginx Integration

The deployer locates the server block for your domain, updates ssl_certificate and ssl_certificate_key directives, runs nginx -t, and reloads.

sudo spork acme install \
  -d myhost.example.com \
  --server https://acme.internal:6446/acme/directory \
  --nginx --agree-tos

Manual deployment:

# After certonly, update your server block:
ssl_certificate     /home/user/.spork/certs/myhost.example.com/fullchain.pem;
ssl_certificate_key /home/user/.spork/certs/myhost.example.com/privkey.pem;

# Test and reload
sudo nginx -t
sudo systemctl reload nginx

IIS Integration (Windows)

On Windows, the deployer imports the certificate into the Windows certificate store, binds it to the IIS site, and restarts the site.

spork acme install ^
  -d myhost.example.com ^
  --server https://acme.internal:6446/acme/directory ^
  --iis --agree-tos
Platform availability: --apache and --nginx are Linux only. --iis is Windows only. Use certonly and deploy manually if your web server is not supported.

Backup & Restore

Interactive Backup

sudo spork-acme
# Navigate: Backup > Create Backup
# Choose encryption option
# Select backup directory

What Gets Backed Up

Restore from Backup

sudo spork-acme
# Navigate: Backup > Restore
# Select backup file
# Enter encryption passphrase if encrypted

Manual Restore

# Stop service
sudo systemctl stop spork-acme

# Extract backup
sudo tar -xzf /var/backups/spork/spork-backup-full-*.tar.gz -C /opt/spork-acme

# Start service
sudo systemctl start spork-acme

Scheduled Backups

sudo spork-acme
# Navigate: Backup > Schedule
# Select frequency: Daily, Weekly, or Monthly
# Enable encryption
Best practices: Encrypt all backups. Store off-site. Test restores quarterly. Use different passphrase for backups vs keystore.

CRL Management

The CRL timer runs every 12 hours by default (7-day validity with overlap).

# Check CRL timer
sudo systemctl status spork-acme-crl.timer

# Manual CRL update
sudo spork-acme --update-crl -s /opt/spork-acme

# View last CRL update
sudo journalctl -u spork-acme-crl.service --since today

CRL distribution:

TLS Certificate Renewal

Renew the server's own TLS certificate (used for HTTPS on port 6446):

# Check expiration
openssl s_client -connect localhost:6446 </dev/null 2>/dev/null | \
  openssl x509 -noout -dates

# Manual renewal
sudo spork-acme --renew-tls -s /opt/spork-acme

# Automatic renewal timer
sudo systemctl enable --now spork-acme-renew.timer

Log Management

# Real-time logs
sudo journalctl -u spork-acme -f

# Today's errors only
sudo journalctl -u spork-acme --since today -p err

# Export logs
sudo journalctl -u spork-acme --since "2026-01-01" > spork-logs.txt

Log Levels

LevelUse Case
errorProduction (minimal)
infoNormal operations (default)
debugTroubleshooting
traceDevelopment only
# Set via command line
spork-acme -s /opt/spork-acme --log-level debug

# Set via environment
export RUST_LOG=spork_acme=debug

Security Hardening

Automated Hardening

sudo spork-acme
# Navigate: Security > Harden

This creates a dedicated spork user/group, sets restrictive file permissions, and configures ownership on the state directory.

Manual Hardening

# Create dedicated user
sudo useradd -r -s /bin/false -d /opt/spork-acme spork

# Set ownership and permissions
sudo chown -R spork:spork /opt/spork-acme
sudo chmod 750 /opt/spork-acme
sudo chmod 600 /opt/spork-acme/keystore.db
sudo chmod 700 /opt/spork-acme/ca
sudo chmod 700 /opt/spork-acme/admin
sudo chmod 600 /opt/spork-acme/admin/*.pfx

Systemd Hardening

The default service file includes security restrictions. Add these for high-security deployments:

[Service]
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/spork-acme
PrivateTmp=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE

# Additional hardening
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
PrivateDevices=true
RestrictSUIDSGID=true
MemoryDenyWriteExecute=true

Network Hardening

# Bind to localhost only (behind reverse proxy)
spork-acme -s /opt/spork-acme --bind 127.0.0.1

Rate Limiting

LimitDefault
Requests per account10/minute
Requests per IP30/minute
New orders per account20/day
Failed validations5/hour per domain

Security Audit

sudo spork-acme
# Navigate: Security > Audit

Security Checklist

Windows CA Integration

SPORK can operate as a subordinate CA under your Windows Active Directory Certificate Services (AD CS) infrastructure. The Windows CA signs SPORK's CA certificate, then SPORK issues end-entity certificates via ACME.

Windows AD CS Root CA (RSA/ECDSA)
        |
        +--- signs subordinate ---> SPORK Issuing CA
                                         |
                              +----------+----------+
                              v          v          v
                           ECDSA     Hybrid      ML-DSA

Step 1: Configure Windows CA Template

  1. Open Certificate Templates console (certtmpl.msc)
  2. Duplicate "Subordinate Certification Authority" template
  3. Set: name=SPORKSubCA, validity=5-10y, pathLenConstraint=0, CA:TRUE
  4. Publish: certutil -setcatemplates +SPORKSubCA

Step 2: Generate CSR on SPORK

sudo spork-acme
# Select: Subordinate to Windows CA
# Choose algorithm (ECDSA P-256 recommended for Windows compat)
# CSR saved to: /opt/spork-acme/spork-acme.csr

Step 3: Submit CSR to Windows CA

REM On Windows CA server:
certreq -submit -attrib "CertificateTemplate:SPORKSubCA" spork-acme.csr spork-ca.cer

REM If pending approval:
certutil -resubmit <RequestID>

Step 4: Export Root CA Certificate

certutil -ca.cert windows-root.cer
certutil -encode windows-root.cer windows-root.pem

Step 5: Import to SPORK

spork-acme --import-cert /opt/spork-acme/ca/spork-ca.cer \
           --import-chain /opt/spork-acme/ca/windows-root.pem \
           -s /opt/spork-acme

Step 6: Verify

# Test ACME directory
curl -s https://acme.yourcompany.com:6446/directory | jq

# Issue a certificate
certbot certonly --standalone \
  --server https://acme.yourcompany.com:6446/directory \
  --agree-tos -d myserver.yourcompany.com

# Verify chain: TLS cert -> SPORK Issuing CA -> Windows Root CA
openssl s_client -connect acme.yourcompany.com:6446 -showcerts
Algorithm tip: Use ECDSA P-256 or P-384 for the subordinate CA certificate (Windows compatibility). SPORK can still issue PQC or hybrid certificates to end entities.

Troubleshooting

Port 6446 already in use

sudo ss -tlnp | grep 6446
# Kill the conflicting process or change the ACME bind port

Permission denied on startup

sudo chmod +x /opt/spork-acme/bin/spork-acme
sudo chown -R root:root /opt/spork-acme
sudo chmod 700 /opt/spork-acme

SELinux denials (Rocky / RHEL)

sudo ausearch -m avc -ts recent
sudo ausearch -m avc -ts recent | audit2allow -M spork-acme-local
sudo semodule -i spork-acme-local.pp

Firewall blocking connections

# Rocky / RHEL
sudo firewall-cmd --add-port=6446/tcp --permanent
sudo firewall-cmd --reload

# Ubuntu / Debian
sudo ufw allow 6446/tcp

Certbot fails with SSL errors

# Option 1: Use --no-verify-ssl
certbot certonly --standalone \
  --server https://localhost:6446/acme/directory \
  --no-verify-ssl --agree-tos -d test.example.com --dry-run

# Option 2: Set REQUESTS_CA_BUNDLE
export REQUESTS_CA_BUNDLE=/opt/spork-acme/ca/ca.crt
certbot certonly --standalone \
  --server https://localhost:6446/acme/directory \
  --agree-tos -d test.example.com --dry-run

Service fails to start (keystore error)

# Check logs
sudo journalctl -u spork-acme -n 50

# Verify environment file
ls -la /etc/spork-acme/env
sudo grep SPORK_LOCKBOX_PASSPHRASE /etc/spork-acme/env

CRL timer not running

sudo systemctl status spork-acme-crl.timer
sudo systemctl enable --now spork-acme-crl.timer

# Manual trigger
sudo systemctl start spork-acme-crl.service

Domain rejected by server

# Check that --allow-domain is configured
sudo journalctl -u spork-acme | grep -i "domain.*denied\|domain.*reject"

# Add allowed domains
# Edit the systemd ExecStart line or config to include:
#   --allow-domain "**.yourdomain.com"
# Then restart:
sudo systemctl restart spork-acme

FIPS mode rejects algorithm

The official binary ships with FIPS enabled by default. Ed25519 and RSA-2048 are not available. Re-run setup and choose a FIPS-approved algorithm:

# Re-run setup with ECDSA P-256/P-384 or RSA ≥3072
sudo spork-acme

"No supported challenge type"

The ACME server did not offer HTTP-01 or DNS-01 challenges. Check that:

install fails to detect web server

# Specify explicitly
sudo spork acme install -d example.com -s URL --apache --agree-tos

# Or use certonly and deploy manually
spork acme certonly -d example.com -s URL --agree-tos
# Then configure web server with:
#   ~/.spork/certs/example.com/fullchain.pem
#   ~/.spork/certs/example.com/privkey.pem

Windows CA: "Key mismatch" during import

The certificate was signed from a different CSR. Verify you are importing the certificate that matches your CSR. If the CSR was regenerated, request a new certificate from Windows CA.

Windows CA: "Not a CA certificate"

The Windows CA used the wrong template. Verify the template has BasicConstraints CA:TRUE with pathLenConstraint=0, marked critical.