Files
homelab-docs/infrastructure/LOCAL-CA-SETUP.md

11 KiB

Local Certificate Authority Setup

This document describes the setup of a local Certificate Authority (CA) for issuing HTTPS certificates to internal services.

Overview

Problem: Internal services (Home Assistant, AD5M printer, Dockge, etc.) use HTTP, causing:

  • Browser security warnings
  • Some services refuse to work without HTTPS
  • Insecure credential transmission

Solution: Local CA using step-ca that issues trusted certificates for .nianticbooks.home domain.

Architecture

CA Server Location

  • Host: main-pve (10.0.10.3) - most reliable server
  • Container: LXC container for isolation
  • IP: 10.0.10.15 (reserved for ca-server)
  • Domain: ca.nianticbooks.home

Services Requiring Certificates

Service Current IP Domain Port
Home Assistant 10.0.10.24 bob.nianticbooks.home 8123
AD5M Printer 10.0.10.30 ad5m.nianticbooks.home 80
Dockge 10.0.10.27 dockge.nianticbooks.home 5001
Proxmox (main-pve) 10.0.10.3 freddesk.nianticbooks.home 8006
Proxmox (pve-router) 10.0.10.2 pve-router.nianticbooks.home 8006
OMV 10.0.10.5 omv.nianticbooks.home 80

Implementation Plan

Phase 1: CA Server Setup

  1. Create LXC container on main-pve
  2. Install step-ca
  3. Initialize CA with:
    • CA name: "Homelab Internal CA"
    • Domain: nianticbooks.home
    • Validity: 10 years (root), 1 year (leaf certificates)

Phase 2: Certificate Generation

For each service, generate:

  • Server certificate
  • Private key
  • Auto-renewal script

Phase 3: Service Configuration

Configure each service to:

  • Use generated certificate
  • Listen on HTTPS port
  • Optionally redirect HTTP → HTTPS

Phase 4: Client Trust

Distribute root CA certificate to:

  • Linux clients (this computer, servers)
  • Windows clients
  • macOS clients
  • Mobile devices (iOS/Android)

Step-by-Step Implementation

1. Create CA Server Container

# On main-pve (10.0.10.3)
pct create 115 local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst \
  --hostname ca-server \
  --cores 1 \
  --memory 512 \
  --swap 512 \
  --storage local-lvm \
  --rootfs 8 \
  --net0 name=eth0,bridge=vmbr0,ip=10.0.10.15/24,gw=10.0.10.1 \
  --unprivileged 1 \
  --features nesting=1 \
  --onboot 1

# Start container
pct start 115

# Enter container
pct enter 115

2. Install step-ca

# Inside container
apt update && apt install -y wget

# Download step-ca
wget https://dl.smallstep.com/gh-release/cli/gh-release-header/v0.25.0/step-cli_0.25.0_amd64.deb
wget https://dl.smallstep.com/gh-release/certificates/gh-release-header/v0.25.0/step-ca_0.25.0_amd64.deb

# Install
dpkg -i step-cli_0.25.0_amd64.deb step-ca_0.25.0_amd64.deb

# Verify
step version
step-ca version

3. Initialize CA

# Create CA user
useradd -r -s /bin/bash -m -d /etc/step-ca step

# Initialize CA (as step user)
su - step
step ca init

# Configuration prompts:
# Name: Homelab Internal CA
# DNS: ca.nianticbooks.home
# Address: :443
# Provisioner: admin@nianticbooks.home
# Password: [generate strong password, save to password manager]

4. Configure CA Service

# Create systemd service
cat > /etc/systemd/system/step-ca.service <<'EOF'
[Unit]
Description=step-ca Certificate Authority
After=network.target

[Service]
Type=simple
User=step
Group=step
WorkingDirectory=/etc/step-ca
ExecStart=/usr/bin/step-ca /etc/step-ca/config/ca.json --password-file=/etc/step-ca/password.txt
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

# Store CA password securely
echo "YOUR_CA_PASSWORD" > /etc/step-ca/password.txt
chown step:step /etc/step-ca/password.txt
chmod 600 /etc/step-ca/password.txt

# Enable and start
systemctl daemon-reload
systemctl enable step-ca
systemctl start step-ca
systemctl status step-ca

5. Issue Certificates for Services

Home Assistant (bob.nianticbooks.home)

# Generate certificate
step certificate create bob.nianticbooks.home \
  bob.crt bob.key \
  --profile leaf \
  --not-after 8760h \
  --ca /etc/step-ca/certs/intermediate_ca.crt \
  --ca-key /etc/step-ca/secrets/intermediate_ca_key \
  --bundle

# Copy to Home Assistant
scp bob.crt bob.key homeassistant.local:/ssl/

Configure Home Assistant (configuration.yaml):

http:
  ssl_certificate: /ssl/bob.crt
  ssl_key: /ssl/bob.key
  server_port: 8123

AD5M Printer (Fluidd/Klipper)

step certificate create ad5m.nianticbooks.home \
  ad5m.crt ad5m.key \
  --profile leaf \
  --not-after 8760h \
  --ca /etc/step-ca/certs/intermediate_ca.crt \
  --ca-key /etc/step-ca/secrets/intermediate_ca_key \
  --bundle

# Copy to printer
scp ad5m.crt ad5m.key root@10.0.10.30:/etc/nginx/ssl/

Configure nginx for Fluidd:

server {
    listen 443 ssl;
    server_name ad5m.nianticbooks.home;

    ssl_certificate /etc/nginx/ssl/ad5m.crt;
    ssl_certificate_key /etc/nginx/ssl/ad5m.key;

    location / {
        proxy_pass http://localhost:80;
    }
}

Dockge

step certificate create dockge.nianticbooks.home \
  dockge.crt dockge.key \
  --profile leaf \
  --not-after 8760h \
  --ca /etc/step-ca/certs/intermediate_ca.crt \
  --ca-key /etc/step-ca/secrets/intermediate_ca_key \
  --bundle

# Copy to Dockge host

Proxmox

# Main PVE
step certificate create freddesk.nianticbooks.home \
  freddesk.crt freddesk.key \
  --profile leaf \
  --not-after 8760h \
  --ca /etc/step-ca/certs/intermediate_ca.crt \
  --ca-key /etc/step-ca/secrets/intermediate_ca_key \
  --bundle

# Copy to Proxmox
scp freddesk.crt freddesk.key root@10.0.10.3:/etc/pve/local/pveproxy-ssl.pem
scp freddesk.key root@10.0.10.3:/etc/pve/local/pveproxy-ssl.key

# Restart Proxmox web service
ssh root@10.0.10.3 "systemctl restart pveproxy"

6. Certificate Auto-Renewal

Create renewal script on CA server:

cat > /usr/local/bin/renew-certs.sh <<'EOF'
#!/bin/bash
# Auto-renew certificates for homelab services

SERVICES=(
  "bob.nianticbooks.home:10.0.10.24:/ssl/"
  "ad5m.nianticbooks.home:10.0.10.30:/etc/nginx/ssl/"
  "dockge.nianticbooks.home:10.0.10.27:/etc/ssl/"
  "freddesk.nianticbooks.home:10.0.10.3:/etc/pve/local/"
)

for service in "${SERVICES[@]}"; do
  IFS=':' read -r domain ip path <<< "$service"

  echo "Renewing certificate for $domain..."

  # Check if certificate expires in < 30 days
  if step certificate needs-renewal "${domain}.crt"; then
    # Generate new certificate
    step certificate create "$domain" \
      "${domain}.crt" "${domain}.key" \
      --profile leaf \
      --not-after 8760h \
      --ca /etc/step-ca/certs/intermediate_ca.crt \
      --ca-key /etc/step-ca/secrets/intermediate_ca_key \
      --bundle --force

    # Deploy to service
    scp "${domain}.crt" "${domain}.key" "root@${ip}:${path}"

    # Restart service
    case "$domain" in
      bob.*)
        ssh root@${ip} "systemctl restart home-assistant@homeassistant"
        ;;
      ad5m.*)
        ssh root@${ip} "systemctl restart nginx"
        ;;
      freddesk.*)
        ssh root@${ip} "systemctl restart pveproxy"
        ;;
    esac

    echo "✓ Renewed $domain"
  fi
done
EOF

chmod +x /usr/local/bin/renew-certs.sh

# Add to crontab (weekly check)
echo "0 2 * * 0 /usr/local/bin/renew-certs.sh" >> /etc/crontab

7. Client Trust Configuration

Linux (Ubuntu/Debian)

# Copy root CA certificate
scp root@10.0.10.15:/etc/step-ca/certs/root_ca.crt /usr/local/share/ca-certificates/homelab-ca.crt

# Update trust store
update-ca-certificates

# Verify
curl https://bob.nianticbooks.home:8123

Windows

  1. Copy root_ca.crt to Windows machine
  2. Right-click → Install Certificate
  3. Store Location: Local Machine
  4. Certificate Store: Trusted Root Certification Authorities
  5. Click Next → Finish

macOS

# Copy certificate
scp root@10.0.10.15:/etc/step-ca/certs/root_ca.crt ~/Downloads/

# Import to keychain
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ~/Downloads/root_ca.crt

iOS/Android

  1. Email root_ca.crt to device or host on web server
  2. Open certificate file on device
  3. Install profile
  4. iOS: Settings → General → About → Certificate Trust Settings → Enable
  5. Android: Settings → Security → Install from storage

DNS Configuration

Ensure UCG Ultra has DNS entries for all services:

bob.nianticbooks.home → 10.0.10.24
ad5m.nianticbooks.home → 10.0.10.30
dockge.nianticbooks.home → 10.0.10.27
freddesk.nianticbooks.home → 10.0.10.3
pve-router.nianticbooks.home → 10.0.10.2
omv.nianticbooks.home → 10.0.10.5
ca.nianticbooks.home → 10.0.10.15

Verification

Test each service:

# Home Assistant
curl -I https://bob.nianticbooks.home:8123

# AD5M
curl -I https://ad5m.nianticbooks.home

# Dockge
curl -I https://dockge.nianticbooks.home:5001

# Proxmox
curl -I https://freddesk.nianticbooks.home:8006

Troubleshooting

Certificate Not Trusted

# Check if CA is in trust store
ls /usr/local/share/ca-certificates/

# Verify certificate chain
openssl s_client -connect bob.nianticbooks.home:8123 -showcerts

# Check certificate validity
openssl x509 -in bob.crt -text -noout

Service Won't Start with HTTPS

# Check certificate permissions
ls -la /ssl/

# Check service logs
journalctl -u home-assistant@homeassistant -f

# Verify certificate matches key
openssl x509 -noout -modulus -in cert.crt | openssl md5
openssl rsa -noout -modulus -in cert.key | openssl md5
# Should match

Maintenance

Certificate Expiry Monitoring

# Check certificate expiration
for cert in *.crt; do
  echo "$cert:"
  openssl x509 -in "$cert" -noout -dates
done

Renewing Root CA

Root CA expires in 10 years. To renew:

  1. Generate new root CA
  2. Issue new intermediate CA
  3. Re-issue all service certificates
  4. Update all client trust stores

Security Considerations

  • CA private key stored encrypted on CA server
  • CA server only accessible from local network
  • Certificate validity limited to 1 year
  • Auto-renewal prevents expiry
  • Root CA backed up to OMV storage

Backup

# Backup CA configuration
rsync -av /etc/step-ca/ /mnt/omv-backup/ca-backup/$(date +%Y%m%d)/

# Store root CA password in password manager

Next Steps

  • Create CA server LXC container
  • Install and configure step-ca
  • Generate certificates for all services
  • Configure services to use HTTPS
  • Distribute root CA to all devices
  • Set up auto-renewal
  • Test all services with HTTPS
  • Update Pangolin routes to use HTTPS backend