Building Secure Local SSL Infrastructure with DNS
Creating a secure internal SSL/TLS infrastructure is essential for protecting communication between services in local networks, development environments, and private clouds. This guide provides a complete implementation for building a production-ready local SSL infrastructure integrated with DNS for automatic certificate validation and distribution.
Architecture Overview
A properly designed local SSL infrastructure consists of several key components working together:
graph TB    subgraph "Certificate Authority"        CA[Root CA]        ICA[Intermediate CA]        CA --> ICA    end
    subgraph "DNS Infrastructure"        DNS[Internal DNS Server]        ACME[ACME DNS Challenge]        DNS --> ACME    end
    subgraph "Certificate Management"        CM[Cert Manager]        VAULT[HashiCorp Vault]        CM --> VAULT    end
    subgraph "Services"        WEB[Web Services]        API[API Services]        DB[Database Services]    end
    ICA --> CM    CM --> WEB    CM --> API    CM --> DB    ACME --> CM
    subgraph "Clients"        BROWSER[Browsers]        APP[Applications]        CLI[CLI Tools]    end
    WEB --> BROWSER    API --> APP    DB --> CLISetting Up the Certificate Authority
Step 1: Create Root Certificate Authority
First, establish a secure root CA that will be the trust anchor for your infrastructure:
#!/bin/bash# ConfigurationCA_DIR="/etc/ssl/ca"ROOT_KEY="$CA_DIR/private/root-ca.key"ROOT_CERT="$CA_DIR/certs/root-ca.crt"ROOT_CONFIG="$CA_DIR/root-ca.conf"
# Create directory structuremkdir -p "$CA_DIR"/{certs,crl,newcerts,private,requests}chmod 700 "$CA_DIR/private"touch "$CA_DIR/index.txt"echo 1000 > "$CA_DIR/serial"
# Create Root CA configurationcat > "$ROOT_CONFIG" << 'EOF'[ ca ]default_ca = CA_default
[ CA_default ]dir               = /etc/ssl/cacerts             = $dir/certscrl_dir           = $dir/crlnew_certs_dir     = $dir/newcertsdatabase          = $dir/index.txtserial            = $dir/serialRANDFILE          = $dir/private/.randprivate_key       = $dir/private/root-ca.keycertificate       = $dir/certs/root-ca.crtcrlnumber         = $dir/crlnumbercrl               = $dir/crl/root-ca.crlcrl_extensions    = crl_extdefault_crl_days  = 30default_md        = sha256name_opt          = ca_defaultcert_opt          = ca_defaultdefault_days      = 3650preserve          = nopolicy            = policy_loose
[ policy_loose ]countryName             = optionalstateOrProvinceName     = optionallocalityName            = optionalorganizationName        = optionalorganizationalUnitName  = optionalcommonName              = suppliedemailAddress            = optional
[ req ]default_bits        = 4096distinguished_name  = req_distinguished_namestring_mask         = utf8onlydefault_md          = sha256x509_extensions     = v3_ca
[ req_distinguished_name ]countryName                     = Country Name (2 letter code)stateOrProvinceName             = State or Province NamelocalityName                    = Locality Name0.organizationName              = Organization NameorganizationalUnitName          = Organizational Unit NamecommonName                      = Common NameemailAddress                    = Email Address
[ v3_ca ]subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid:always,issuerbasicConstraints = critical, CA:truekeyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ v3_intermediate_ca ]subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid:always,issuerbasicConstraints = critical, CA:true, pathlen:0keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ crl_ext ]authorityKeyIdentifier=keyid:alwaysEOF
# Generate Root CA private keyopenssl genrsa -aes256 -out "$ROOT_KEY" 4096chmod 400 "$ROOT_KEY"
# Generate Root CA certificateopenssl req -config "$ROOT_CONFIG" \    -key "$ROOT_KEY" \    -new -x509 -days 7300 -sha256 -extensions v3_ca \    -out "$ROOT_CERT" \    -subj "/C=US/ST=State/L=City/O=Organization/OU=IT/CN=Root CA"
# Verify Root CA certificateopenssl x509 -noout -text -in "$ROOT_CERT"Step 2: Create Intermediate Certificate Authority
The intermediate CA will issue certificates for services:
#!/bin/bash# ConfigurationCA_DIR="/etc/ssl/ca"INT_DIR="$CA_DIR/intermediate"INT_KEY="$INT_DIR/private/intermediate-ca.key"INT_CSR="$INT_DIR/csr/intermediate-ca.csr"INT_CERT="$INT_DIR/certs/intermediate-ca.crt"INT_CONFIG="$INT_DIR/intermediate-ca.conf"
# Create intermediate directory structuremkdir -p "$INT_DIR"/{certs,crl,csr,newcerts,private}chmod 700 "$INT_DIR/private"touch "$INT_DIR/index.txt"echo 1000 > "$INT_DIR/serial"echo 1000 > "$INT_DIR/crlnumber"
# Create Intermediate CA configurationcat > "$INT_CONFIG" << 'EOF'[ ca ]default_ca = CA_default
[ CA_default ]dir               = /etc/ssl/ca/intermediatecerts             = $dir/certscrl_dir           = $dir/crlnew_certs_dir     = $dir/newcertsdatabase          = $dir/index.txtserial            = $dir/serialRANDFILE          = $dir/private/.randprivate_key       = $dir/private/intermediate-ca.keycertificate       = $dir/certs/intermediate-ca.crtcrlnumber         = $dir/crlnumbercrl               = $dir/crl/intermediate-ca.crlcrl_extensions    = crl_extdefault_crl_days  = 30default_md        = sha256name_opt          = ca_defaultcert_opt          = ca_defaultdefault_days      = 375preserve          = nopolicy            = policy_loose
[ policy_loose ]countryName             = optionalstateOrProvinceName     = optionallocalityName            = optionalorganizationName        = optionalorganizationalUnitName  = optionalcommonName              = suppliedemailAddress            = optional
[ req ]default_bits        = 4096distinguished_name  = req_distinguished_namestring_mask         = utf8onlydefault_md          = sha256
[ req_distinguished_name ]countryName                     = Country Name (2 letter code)stateOrProvinceName             = State or Province NamelocalityName                    = Locality Name0.organizationName              = Organization NameorganizationalUnitName          = Organizational Unit NamecommonName                      = Common NameemailAddress                    = Email Address
[ v3_intermediate_ca ]subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid:always,issuerbasicConstraints = critical, CA:true, pathlen:0keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ server_cert ]basicConstraints = CA:FALSEnsCertType = servernsComment = "OpenSSL Generated Server Certificate"subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid,issuer:alwayskeyUsage = critical, digitalSignature, keyEnciphermentextendedKeyUsage = serverAuthsubjectAltName = @alt_names
[ crl_ext ]authorityKeyIdentifier=keyid:always
[ ocsp ]basicConstraints = CA:FALSEsubjectKeyIdentifier = hashauthorityKeyIdentifier = keyid,issuerkeyUsage = critical, digitalSignatureextendedKeyUsage = critical, OCSPSigningEOF
# Generate Intermediate CA private keyopenssl genrsa -aes256 -out "$INT_KEY" 4096chmod 400 "$INT_KEY"
# Generate Intermediate CA certificate requestopenssl req -config "$INT_CONFIG" -new -sha256 \    -key "$INT_KEY" \    -out "$INT_CSR" \    -subj "/C=US/ST=State/L=City/O=Organization/OU=IT/CN=Intermediate CA"
# Sign Intermediate CA certificate with Root CAopenssl ca -config "$CA_DIR/root-ca.conf" \    -extensions v3_intermediate_ca \    -days 3650 -notext -md sha256 \    -in "$INT_CSR" \    -out "$INT_CERT"
# Create certificate chaincat "$INT_CERT" "$CA_DIR/certs/root-ca.crt" > "$INT_DIR/certs/ca-chain.crt"
# Verify certificate chainopenssl verify -CAfile "$CA_DIR/certs/root-ca.crt" "$INT_CERT"DNS Integration for Certificate Validation
CoreDNS Configuration with ACME Support
Configure CoreDNS to handle DNS challenges for automatic certificate issuance:
apiVersion: v1kind: ConfigMapmetadata:  name: coredns-configdata:  Corefile: |    .:53 {        errors        health {            lameduck 5s        }        ready
        # Internal zone        file /etc/coredns/zones/internal.zone internal.company.com {            reload 30s        }
        # ACME DNS challenge support        file /etc/coredns/zones/acme.zone _acme-challenge.internal.company.com {            reload 5s        }
        # Forwarding for external domains        forward . 8.8.8.8 8.8.4.4 {            max_concurrent 1000        }
        cache 30        loop        reload        loadbalance
        # Prometheus metrics        prometheus :9153
        # Logging        log . {            class error        }    }
  internal.zone: |    $ORIGIN internal.company.com.    $TTL 3600    @   IN  SOA ns1.internal.company.com. admin.internal.company.com. (            2024010101  ; Serial            3600        ; Refresh            1800        ; Retry            604800      ; Expire            86400       ; Minimum TTL    )
    ; Name servers    @   IN  NS  ns1.internal.company.com.    @   IN  NS  ns2.internal.company.com.
    ; A records    ns1     IN  A   10.0.1.10    ns2     IN  A   10.0.1.11    ca      IN  A   10.0.1.20    vault   IN  A   10.0.1.21
    ; Service records    web     IN  A   10.0.2.10    api     IN  A   10.0.2.20    db      IN  A   10.0.2.30
    ; CNAME records    www     IN  CNAME   web
  acme.zone: |    $ORIGIN _acme-challenge.internal.company.com.    $TTL 60    @   IN  SOA ns1.internal.company.com. admin.internal.company.com. (            2024010101  ; Serial            60          ; Refresh            30          ; Retry            3600        ; Expire            60          ; Minimum TTL    )DNS-01 Challenge Automation
Implement automated DNS-01 challenge handling:
#!/usr/bin/env python3import osimport timeimport dns.resolverimport dns.updateimport dns.tsigkeyringimport dns.queryfrom flask import Flask, request, jsonify
app = Flask(__name__)
# ConfigurationDNS_SERVER = os.environ.get('DNS_SERVER', '10.0.1.10')DNS_ZONE = os.environ.get('DNS_ZONE', 'internal.company.com')TSIG_KEY_NAME = os.environ.get('TSIG_KEY_NAME', 'acme-key')TSIG_KEY = os.environ.get('TSIG_KEY', 'base64-encoded-key')TSIG_ALGO = os.environ.get('TSIG_ALGO', 'hmac-sha256')
# Create TSIG keyringkeyring = dns.tsigkeyring.from_text({    TSIG_KEY_NAME: TSIG_KEY})
@app.route('/present', methods=['POST'])def present_challenge():    """Add DNS TXT record for ACME challenge"""    data = request.json    domain = data.get('domain')    token = data.get('token')
    if not domain or not token:        return jsonify({'error': 'Missing domain or token'}), 400
    # Create DNS update    update = dns.update.Update(DNS_ZONE, keyring=keyring, keyalgorithm=TSIG_ALGO)
    # Add TXT record    txt_name = f'_acme-challenge.{domain}'    update.add(txt_name, 60, 'TXT', f'"{token}"')
    try:        response = dns.query.tcp(update, DNS_SERVER)
        # Wait for DNS propagation        time.sleep(5)
        # Verify record exists        resolver = dns.resolver.Resolver()        resolver.nameservers = [DNS_SERVER]        answers = resolver.resolve(f'{txt_name}.{DNS_ZONE}', 'TXT')
        for rdata in answers:            if token in str(rdata):                return jsonify({'status': 'success'}), 200
        return jsonify({'error': 'Record not found after creation'}), 500
    except Exception as e:        return jsonify({'error': str(e)}), 500
@app.route('/cleanup', methods=['POST'])def cleanup_challenge():    """Remove DNS TXT record after challenge completion"""    data = request.json    domain = data.get('domain')
    if not domain:        return jsonify({'error': 'Missing domain'}), 400
    # Create DNS update    update = dns.update.Update(DNS_ZONE, keyring=keyring, keyalgorithm=TSIG_ALGO)
    # Delete TXT record    txt_name = f'_acme-challenge.{domain}'    update.delete(txt_name, 'TXT')
    try:        response = dns.query.tcp(update, DNS_SERVER)        return jsonify({'status': 'success'}), 200    except Exception as e:        return jsonify({'error': str(e)}), 500
if __name__ == '__main__':    app.run(host='0.0.0.0', port=8080)Automated Certificate Management
Step-CA for ACME Protocol
Deploy Step-CA as an ACME server integrated with your CA:
{  "root": "/etc/step-ca/certs/root-ca.crt",  "federatedRoots": [],  "crt": "/etc/step-ca/certs/intermediate-ca.crt",  "key": "/etc/step-ca/secrets/intermediate-ca.key",  "address": ":443",  "insecureAddress": "",  "dnsNames": ["ca.internal.company.com"],  "logger": { "format": "json" },  "db": { "type": "badgerv2", "dataSource": "/etc/step-ca/db" },  "authority":    {      "provisioners":        [          {            "type": "ACME",            "name": "acme",            "forceCN": true,            "claims":              {                "maxTLSCertDuration": "720h",                "defaultTLSCertDuration": "168h",              },          },          {            "type": "JWK",            "name": "admin",            "key":              {                "use": "sig",                "kty": "EC",                "kid": "admin-key-id",                "crv": "P-256",                "alg": "ES256",                "x": "base64-x-coordinate",                "y": "base64-y-coordinate",              },            "encryptedKey": "encrypted-private-key",            "claims":              {                "maxTLSCertDuration": "8760h",                "defaultTLSCertDuration": "720h",                "enableSSHCA": true,              },          },        ],    },  "tls":    {      "cipherSuites":        [          "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",          "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",          "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",          "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",        ],      "minVersion": 1.2,      "maxVersion": 1.3,      "renegotiation": false,    },}Cert-Manager Integration
Deploy cert-manager for Kubernetes environments:
apiVersion: cert-manager.io/v1kind: ClusterIssuermetadata:  name: internal-ca-issuerspec:  acme:    server: https://ca.internal.company.com/acme/acme/directory    email: admin@company.com    privateKeySecretRef:      name: internal-ca-account-key    solvers:      - dns01:          webhook:            groupName: acme.company.com            solverName: internal-dns            config:              endpoint: http://dns-challenge-handler:8080              zone: internal.company.com---apiVersion: v1kind: Secretmetadata:  name: ca-root-cert  namespace: cert-managertype: Opaquedata:  ca.crt: # base64 encoded root CA certificateCertificate Lifecycle Automation
#!/bin/bash# ConfigurationCERT_DIR="/etc/ssl/services"CA_URL="https://ca.internal.company.com"RENEWAL_DAYS=30LOG_FILE="/var/log/cert-manager.log"
# Logging functionlog() {    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"}
# Check certificate expirationcheck_cert_expiry() {    local cert_file=$1    local days_until_expiry
    if [[ -f "$cert_file" ]]; then        days_until_expiry=$(openssl x509 -enddate -noout -in "$cert_file" | \            cut -d= -f2 | xargs -I {} date -d {} +%s | \            awk -v now=$(date +%s) '{print int(($1-now)/86400)}')
        echo "$days_until_expiry"    else        echo "-1"    fi}
# Request new certificate using ACMErequest_certificate() {    local domain=$1    local cert_path="$CERT_DIR/$domain"
    log "Requesting certificate for $domain"
    # Create certificate directory    mkdir -p "$cert_path"
    # Request certificate using step CLI    step ca certificate "$domain" \        "$cert_path/cert.pem" \        "$cert_path/key.pem" \        --ca-url="$CA_URL" \        --root="/etc/ssl/ca/certs/root-ca.crt" \        --acme="$CA_URL/acme/acme/directory" \        --kty=RSA \        --size=2048
    if [[ $? -eq 0 ]]; then        log "Certificate successfully obtained for $domain"
        # Set proper permissions        chmod 644 "$cert_path/cert.pem"        chmod 600 "$cert_path/key.pem"
        # Create combined certificate chain        cat "$cert_path/cert.pem" \            "/etc/ssl/ca/intermediate/certs/intermediate-ca.crt" \            > "$cert_path/fullchain.pem"
        return 0    else        log "ERROR: Failed to obtain certificate for $domain"        return 1    fi}
# Renew certificatesrenew_certificates() {    local services_file="/etc/ssl/services.conf"
    while IFS= read -r line; do        # Skip comments and empty lines        [[ "$line" =~ ^#.*$ ]] || [[ -z "$line" ]] && continue
        # Parse service configuration        domain=$(echo "$line" | cut -d':' -f1)        service=$(echo "$line" | cut -d':' -f2)        reload_cmd=$(echo "$line" | cut -d':' -f3)
        cert_file="$CERT_DIR/$domain/cert.pem"        days_left=$(check_cert_expiry "$cert_file")
        if [[ $days_left -lt $RENEWAL_DAYS ]]; then            log "Certificate for $domain expires in $days_left days, renewing..."
            if request_certificate "$domain"; then                # Reload service if specified                if [[ -n "$reload_cmd" ]]; then                    log "Reloading service: $service"                    eval "$reload_cmd"                fi            fi        else            log "Certificate for $domain is valid for $days_left more days"        fi
    done < "$services_file"}
# Main executionmain() {    log "Starting certificate lifecycle manager"
    # Ensure required directories exist    mkdir -p "$CERT_DIR"
    # Run certificate renewal check    renew_certificates
    log "Certificate lifecycle check completed"}
# Run main functionmainService Configuration Examples
Nginx with Auto-Renewed Certificates
server {    listen 443 ssl http2;    listen [::]:443 ssl http2;    server_name app.internal.company.com;
    # Certificate paths    ssl_certificate /etc/ssl/services/app.internal.company.com/fullchain.pem;    ssl_certificate_key /etc/ssl/services/app.internal.company.com/key.pem;
    # Modern SSL configuration    ssl_protocols TLSv1.2 TLSv1.3;    ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;    ssl_prefer_server_ciphers off;
    # OCSP stapling    ssl_stapling on;    ssl_stapling_verify on;    ssl_trusted_certificate /etc/ssl/services/app.internal.company.com/fullchain.pem;
    # Security headers    add_header Strict-Transport-Security "max-age=63072000" always;    add_header X-Frame-Options "SAMEORIGIN" always;    add_header X-Content-Type-Options "nosniff" always;    add_header X-XSS-Protection "1; mode=block" always;
    # Application configuration    location / {        proxy_pass http://localhost:8080;        proxy_set_header Host $host;        proxy_set_header X-Real-IP $remote_addr;        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;        proxy_set_header X-Forwarded-Proto $scheme;    }}
# Redirect HTTP to HTTPSserver {    listen 80;    listen [::]:80;    server_name app.internal.company.com;    return 301 https://$server_name$request_uri;}PostgreSQL with SSL/TLS
# postgresql.confssl = onssl_cert_file = '/etc/ssl/services/db.internal.company.com/cert.pem'ssl_key_file = '/etc/ssl/services/db.internal.company.com/key.pem'ssl_ca_file = '/etc/ssl/ca/certs/ca-chain.crt'ssl_crl_file = ''ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL'ssl_prefer_server_ciphers = onssl_ecdh_curve = 'prime256v1'ssl_min_protocol_version = 'TLSv1.2'ssl_max_protocol_version = ''
# pg_hba.conf# TYPE  DATABASE        USER            ADDRESS                 METHODhostssl all             all             10.0.0.0/16            cert clientcert=verify-fullhostssl replication     replicator      10.0.0.0/16            cert clientcert=verify-fullClient Certificate Management
Generating Client Certificates
#!/bin/bashCLIENT_NAME="$1"CLIENT_DIR="/etc/ssl/clients/$CLIENT_NAME"
if [[ -z "$CLIENT_NAME" ]]; then    echo "Usage: $0 <client-name>"    exit 1fi
# Create client directorymkdir -p "$CLIENT_DIR"
# Generate client private keyopenssl genrsa -out "$CLIENT_DIR/client.key" 2048chmod 600 "$CLIENT_DIR/client.key"
# Create client certificate requestcat > "$CLIENT_DIR/client.conf" << EOF[req]distinguished_name = req_distinguished_namereq_extensions = v3_reqprompt = no
[req_distinguished_name]C = USST = StateL = CityO = OrganizationOU = ITCN = $CLIENT_NAMEemailAddress = $CLIENT_NAME@internal.company.com
[v3_req]keyUsage = critical, digitalSignature, keyEnciphermentextendedKeyUsage = clientAuthsubjectAltName = @alt_names
[alt_names]email = $CLIENT_NAME@internal.company.comEOF
# Generate certificate requestopenssl req -new -key "$CLIENT_DIR/client.key" \    -out "$CLIENT_DIR/client.csr" \    -config "$CLIENT_DIR/client.conf"
# Sign client certificateopenssl ca -config /etc/ssl/ca/intermediate/intermediate-ca.conf \    -extensions usr_cert -days 365 -notext -md sha256 \    -in "$CLIENT_DIR/client.csr" \    -out "$CLIENT_DIR/client.crt"
# Create PKCS12 bundle for easy importopenssl pkcs12 -export \    -out "$CLIENT_DIR/client.p12" \    -inkey "$CLIENT_DIR/client.key" \    -in "$CLIENT_DIR/client.crt" \    -certfile /etc/ssl/ca/intermediate/certs/ca-chain.crt \    -passout pass:changeme
echo "Client certificate generated for $CLIENT_NAME"echo "Certificate: $CLIENT_DIR/client.crt"echo "Private Key: $CLIENT_DIR/client.key"echo "PKCS12 Bundle: $CLIENT_DIR/client.p12"Monitoring and Maintenance
Certificate Monitoring Dashboard
graph LR    subgraph "Monitoring Stack"        PROM[Prometheus]        ALERT[Alertmanager]        GRAF[Grafana]    end
    subgraph "Certificate Checks"        EXP[Expiry Check]        VAL[Validation Check]        REV[Revocation Check]    end
    subgraph "Services"        WEB[Web Services]        API[API Services]        DB[Databases]    end
    EXP --> PROM    VAL --> PROM    REV --> PROM
    WEB --> EXP    API --> EXP    DB --> EXP
    PROM --> ALERT    PROM --> GRAFPrometheus Certificate Exporter
#!/usr/bin/env python3import sslimport socketimport datetimeimport timefrom prometheus_client import start_http_server, Gauge
# Metricscert_expiry_days = Gauge('cert_expiry_days',                        'Days until certificate expiry',                        ['hostname', 'port'])cert_valid = Gauge('cert_valid',                  'Certificate validation status',                  ['hostname', 'port'])
def check_certificate(hostname, port=443):    """Check SSL certificate for a given host"""    try:        # Create SSL context        context = ssl.create_default_context()
        # Connect and get certificate        with socket.create_connection((hostname, port), timeout=10) as sock:            with context.wrap_socket(sock, server_hostname=hostname) as ssock:                cert = ssock.getpeercert()
                # Parse expiry date                not_after = datetime.datetime.strptime(                    cert['notAfter'],                    '%b %d %H:%M:%S %Y %Z'                )
                # Calculate days until expiry                days_left = (not_after - datetime.datetime.utcnow()).days
                # Update metrics                cert_expiry_days.labels(hostname=hostname, port=port).set(days_left)                cert_valid.labels(hostname=hostname, port=port).set(1)
                return days_left
    except Exception as e:        print(f"Error checking {hostname}:{port} - {str(e)}")        cert_valid.labels(hostname=hostname, port=port).set(0)        return -1
def main():    # Start Prometheus metrics server    start_http_server(9100)
    # Services to monitor    services = [        ('web.internal.company.com', 443),        ('api.internal.company.com', 443),        ('db.internal.company.com', 5432),    ]
    while True:        for hostname, port in services:            check_certificate(hostname, port)
        # Check every 5 minutes        time.sleep(300)
if __name__ == '__main__':    main()Security Best Practices
1. Key Management
# Secure key storage with proper permissionschmod 700 /etc/ssl/ca/privatechmod 600 /etc/ssl/ca/private/*.key
# Use hardware security modules (HSM) for production# Example with SoftHSMsofthsm2-util --init-token --slot 0 --label "CA Keys" \    --pin 1234 --so-pin 5678
# Store CA key in HSMpkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \    --login --pin 1234 \    --write-object /etc/ssl/ca/private/root-ca.key \    --type privkey --id 01 --label "Root CA Key"2. Certificate Pinning
// Node.js example with certificate pinningconst https = require("https");const crypto = require("crypto");
const pinnedCerts = ["sha256//BASE64_ENCODED_CERT_FINGERPRINT"];
const options = {  hostname: "api.internal.company.com",  port: 443,  path: "/",  method: "GET",  checkServerIdentity: (host, cert) => {    const fingerprint = crypto      .createHash("sha256")      .update(cert.raw)      .digest("base64");
    if (!pinnedCerts.includes(`sha256//${fingerprint}`)) {      throw new Error("Certificate pin validation failed");    }  },};
https  .request(options, res => {    // Handle response  })  .end();3. Audit Logging
#!/bin/bash# Enable audit logging for CA operationscat >> /etc/ssl/ca/intermediate/intermediate-ca.conf << EOF
[ ca ]# Existing configuration...audit_log = /var/log/ca-audit.log
EOF
# Log rotation configurationcat > /etc/logrotate.d/ca-audit << EOF/var/log/ca-audit.log {    daily    rotate 365    compress    delaycompress    missingok    notifempty    create 0600 root root    sharedscripts    postrotate        # Signal CA service to reopen logs if needed        systemctl reload step-ca 2>/dev/null || true    endscript}EOFTroubleshooting Common Issues
DNS Resolution Issues
# Test DNS resolutiondig _acme-challenge.app.internal.company.com TXT @10.0.1.10
# Verify TSIG keynsupdate -k /etc/bind/acme-key.key <<EOFserver 10.0.1.10zone internal.company.comupdate add test.internal.company.com 60 A 10.0.0.1sendEOF
# Check DNS logsjournalctl -u coredns -fCertificate Validation Problems
# Verify certificate chainopenssl verify -CAfile /etc/ssl/ca/certs/ca-chain.crt \    /etc/ssl/services/app.internal.company.com/cert.pem
# Check certificate detailsopenssl x509 -in /etc/ssl/services/app.internal.company.com/cert.pem \    -noout -text
# Test SSL connectionopenssl s_client -connect app.internal.company.com:443 \    -CAfile /etc/ssl/ca/certs/ca-chain.crt \    -servername app.internal.company.comConclusion
Building a secure local SSL infrastructure with DNS integration provides a robust foundation for internal service communication. By combining a proper PKI hierarchy with automated certificate management and DNS-based validation, organizations can achieve enterprise-grade security for their internal networks while maintaining ease of use and automation capabilities.
The key to success is proper planning, automation of routine tasks, and regular monitoring of certificate health. With the tools and configurations provided in this guide, you can implement a production-ready SSL infrastructure that scales with your organization’s needs.