Skip to main content
Mole checking certificates

TLS Certificate Configuration

All peer connections in Muti Metroo use TLS for encryption and authentication. This guide covers certificate generation and configuration.

Overview

Muti Metroo uses a PKI (Public Key Infrastructure) model with centralized TLS configuration:

  1. Certificate Authority (CA): Signs all certificates, configured once globally
  2. Agent Certificate: Used for both server and client authentication
  3. Global mTLS Setting: Enable/disable mutual TLS with a simple boolean

Global TLS Configuration

TLS settings are configured once in the global tls: section:

tls:
# CA certificate for verifying peers and clients
ca: "./certs/ca.crt"

# Agent's identity certificate (used by listeners and peer connections)
cert: "./certs/agent.crt"
key: "./certs/agent.key"

# Enable mutual TLS on listeners (require client certificates)
mtls: true

This single configuration is used by:

  • All listeners (for server TLS and optional mTLS)
  • All peer connections (for client certificate presentation)

Certificate Requirements

Important: Muti Metroo only accepts EC (Elliptic Curve) certificates. RSA certificates are not supported for the mesh CA and agent certificates.

The only exception is when connecting through a WebSocket proxy to an external server - in this case, the external server may use any certificate type since mTLS is not available through proxies.

Generating Certificates

Using the CLI

# Generate CA (do once, share across mesh)
muti-metroo cert ca -n "My Mesh CA" -o ./certs

# Generate agent certificate (signed by the CA)
muti-metroo cert agent -n "agent-1" \
--ca ./certs/ca.crt \
--ca-key ./certs/ca.key \
--dns "agent1.example.com" \
--ip "192.168.1.10" \
-o ./certs

# View certificate info
muti-metroo cert info ./certs/agent-1.crt
Default Paths

If you use -o ./certs for the CA, the --ca and --ca-key flags default to ./certs/ca.crt and ./certs/ca.key, so you can omit them.

All certificates generated by the CLI use P-256 ECDSA keys.

Using OpenSSL

# Generate EC CA key and certificate
openssl ecparam -name prime256v1 -genkey -noout -out ca.key
openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 \
-out ca.crt -subj "/CN=My Mesh CA"

# Generate EC agent key and CSR
openssl ecparam -name prime256v1 -genkey -noout -out agent.key
openssl req -new -key agent.key -out agent.csr \
-subj "/CN=agent-1"

# Sign agent certificate
openssl x509 -req -in agent.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out agent.crt -days 365 -sha256

Configuration Options

File-Based Certificates

Standard approach using file paths:

tls:
ca: "./certs/ca.crt"
cert: "./certs/agent.crt"
key: "./certs/agent.key"
mtls: true

Inline PEM Certificates

Embed certificates directly in config (useful for Kubernetes secrets):

tls:
ca_pem: |
-----BEGIN CERTIFICATE-----
MIIBkTCB+wIJAKi...
-----END CERTIFICATE-----
cert_pem: |
-----BEGIN CERTIFICATE-----
MIIBkTCB+wIJAKi...
-----END CERTIFICATE-----
key_pem: |
-----BEGIN EC PRIVATE KEY-----
MIIEvQIBADANBg...
-----END EC PRIVATE KEY-----
mtls: true

Inline PEM takes precedence over file paths.

Environment Variables

Use environment variables for secrets:

tls:
ca_pem: "${TLS_CA}"
cert_pem: "${TLS_CERT}"
key_pem: "${TLS_KEY}"
mtls: true

Mutual TLS (mTLS)

mTLS requires both sides to present certificates. With the global configuration, enabling mTLS is simple:

tls:
ca: "./certs/ca.crt"
cert: "./certs/agent.crt"
key: "./certs/agent.key"
mtls: true # Enable mTLS on all listeners

When mtls: true:

  • Listeners require connecting peers to present valid client certificates
  • The global CA is used to verify client certificates
  • The global agent certificate is automatically used by peers as their client certificate

Benefits of mTLS

  • Mutual authentication (both sides verified)
  • Prevents unauthorized connections
  • Defense against man-in-the-middle attacks
  • Required for zero-trust environments

Per-Listener Overrides

Individual listeners can override global settings:

tls:
ca: "./certs/ca.crt"
cert: "./certs/agent.crt"
key: "./certs/agent.key"
mtls: true

listeners:
# Uses global settings
- transport: quic
address: "0.0.0.0:4433"

# Override: disable mTLS for this listener
- transport: h2
address: "0.0.0.0:8443"
tls:
mtls: false

# Override: use different certificate
- transport: ws
address: "0.0.0.0:443"
tls:
cert: "./certs/public-facing.crt"
key: "./certs/public-facing.key"
mtls: false

# No TLS: plaintext for reverse proxy
- transport: ws
address: "127.0.0.1:8080"
plaintext: true

Per-Peer Overrides

Individual peer connections can override global settings:

tls:
ca: "./certs/ca.crt"
cert: "./certs/agent.crt"
key: "./certs/agent.key"
mtls: true

peers:
# Uses global CA and cert
- id: "abc123..."
transport: quic
address: "192.168.1.50:4433"

# Override: different CA for this peer
- id: "def456..."
transport: quic
address: "external.example.com:4433"
tls:
ca: "./certs/external-ca.crt"

# Certificate pinning
- id: "ghi789..."
transport: quic
address: "pinned.example.com:4433"
tls:
fingerprint: "sha256:ab12cd34..."

Certificate Validity

Validity Period

Default validity periods:

  • CA certificates: 365 days
  • Agent/client certificates: 90 days

Specify custom validity:

muti-metroo cert ca --days 730       # 2 years
muti-metroo cert agent --days 180 # 6 months

Subject Alternative Names (SANs)

Always include SANs for agent certificates:

muti-metroo cert agent -n "agent-1" \
--ca ./certs/ca.crt \
--ca-key ./certs/ca.key \
--dns "agent1.example.com" \
--dns "agent1.internal" \
--ip "192.168.1.10" \
--ip "10.0.0.10" \
-o ./certs

Certificate Rotation

Planned Rotation

  1. Generate new certificates before expiration
  2. Deploy new certificates alongside old ones
  3. Update configuration to use new certificates
  4. Restart agents
  5. Remove old certificates

Emergency Rotation

If CA is compromised:

  1. Generate new CA
  2. Generate new certificates for all agents
  3. Deploy and restart all agents simultaneously
  4. Revoke trust in old CA

Monitoring Expiration

CLI Check

muti-metroo cert info ./certs/agent.crt

Output includes expiration date.

OpenSSL Check

openssl x509 -enddate -noout -in ./certs/agent.crt

Automated Monitoring

Add to your monitoring:

#!/bin/bash
# Check if cert expires in next 30 days
openssl x509 -checkend 2592000 -noout -in ./certs/agent.crt
if [ $? -eq 1 ]; then
echo "Certificate expires soon!"
fi

Troubleshooting

Certificate Not Trusted

Error: x509: certificate signed by unknown authority
  • Verify CA certificate is correct
  • Check CA was used to sign agent certificate

Certificate Expired

Error: x509: certificate has expired
  • Generate new certificate
  • Check system time is correct

Name Mismatch

Error: x509: certificate is valid for agent1.example.com, not agent2.example.com
  • Use correct hostname/IP
  • Add SANs when generating certificate

Private Key Mismatch

Error: tls: private key does not match public key
  • Verify key matches certificate:
    openssl x509 -noout -pubkey -in agent.crt | openssl md5
    openssl ec -in agent.key -pubout 2>/dev/null | openssl md5
    # Must match

RSA Certificate Rejected

Error: certificate must use ECDSA, got RSA
  • Muti Metroo only accepts EC certificates
  • Regenerate certificates using ECDSA (P-256)

Best Practices

  1. Use EC certificates: RSA is not supported
  2. Protect CA private key: Use hardware security module or secure vault
  3. Use short validity: 90-365 days for agent certs
  4. Use SANs: Include all hostnames and IPs
  5. Enable mTLS: Especially in production
  6. Automate rotation: Before certificates expire
  7. Monitor expiration: Alert before expiry

Examples

Development

tls:
ca: "./dev-certs/ca.crt"
cert: "./dev-certs/agent.crt"
key: "./dev-certs/agent.key"
mtls: false # Relax for development

listeners:
- transport: quic
address: "0.0.0.0:4433"

Production with mTLS

tls:
ca: "/etc/muti-metroo/certs/ca.crt"
cert: "/etc/muti-metroo/certs/agent.crt"
key: "/etc/muti-metroo/certs/agent.key"
mtls: true

listeners:
- transport: quic
address: "0.0.0.0:4433"

peers:
- id: "abc123..."
transport: quic
address: "peer.example.com:4433"

Kubernetes

tls:
ca_pem: "${CA_CRT}"
cert_pem: "${TLS_CRT}"
key_pem: "${TLS_KEY}"
mtls: true

listeners:
- transport: quic
address: "0.0.0.0:4433"