Skip to main content

SSL/TLS Certificates

Aegis manages SSL/TLS certificates for all proxy hosts. It supports three certificate sources: Let’s Encrypt (automatic ACME), Cloudflare Origin CA (API-integrated), and Custom (manual PEM upload). Private keys are encrypted at rest using AES-256-GCM.

Certificate Sources

SourceModeDescription
Let’s EncryptletsencryptAutomatic certificate issuance and renewal via ACME HTTP-01 challenge
Cloudflare Origin CAcustom (source: cloudflare_origin)Origin certificates issued through the Cloudflare API
Custom UploadcustomManual PEM-encoded certificate and key upload
Self-Signed FallbackautomaticGenerated automatically when no other certificate is available for a host

Let’s Encrypt (ACME)

Aegis uses Go’s autocert library to obtain and renew Let’s Encrypt certificates automatically via the ACME HTTP-01 challenge.

How It Works

  1. Set the proxy host’s SSL mode to letsencrypt in the admin UI
  2. Aegis validates that the domain is configured as a proxy host (host policy check)
  3. When a TLS handshake arrives for that domain, autocert requests a certificate from Let’s Encrypt
  4. The HTTP-01 challenge is served on port 80 (the HTTP proxy listener handles /.well-known/acme-challenge/ paths)
  5. The certificate is cached in the autocert cache directory (default .cache/ssl)
  6. Renewal happens automatically before expiration

Requirements

  • Port 80 must be reachable from the internet for HTTP-01 challenge validation
  • The domain must resolve to the Aegis server’s public IP
  • The proxy host must have ssl_mode set to letsencrypt

Configuration

SettingDefaultDescription
AEGIS_SSL_CACHE_DIR.cache/sslDirectory for autocert’s file-based certificate cache
# Start with custom SSL cache directory
AEGIS_SSL_CACHE_DIR=/var/lib/aegis/ssl aegis run

Cloudflare Origin CA

Aegis can request Cloudflare Origin CA certificates directly through the Cloudflare API. This is ideal when Aegis sits behind Cloudflare’s proxy, where Let’s Encrypt HTTP-01 challenges may not reach the origin server.

How It Works

  1. Aegis generates a CSR (Certificate Signing Request) with the requested hostnames
  2. The CSR is submitted to Cloudflare’s Origin CA API (/client/v4/certificates)
  3. Cloudflare issues a signed certificate (valid up to 15 years)
  4. Aegis stores the certificate and encrypted private key in SQLite
  5. The certificate is loaded into the TLS configuration for SNI-based selection

Authentication Methods

MethodHeaderDescription
api_tokenAuthorization: Bearer <token>Cloudflare API Token with Origin CA permissions
origin_ca_keyX-Auth-User-Service-Key: <key>Cloudflare Origin CA Key (found in the Cloudflare dashboard under API Tokens)

Request Options

FieldDefaultDescription
hostnames(required)Domain names for the certificate (e.g., ["app.example.com", "*.example.com"])
request_typeorigin-rsaKey type — origin-rsa (RSA 2048) or origin-ecc (ECDSA P-256)
requested_validity5475Certificate validity in days (default: 15 years)
zone_id(optional)Cloudflare Zone ID (for audit/tracking)

API Endpoint

POST /api/v1/certificates/cloudflare-origin
{
  "auth_method": "api_token",
  "api_token": "your-cloudflare-api-token",
  "hostnames": ["app.example.com", "*.example.com"],
  "request_type": "origin-rsa",
  "requested_validity": 5475
}
Response:
{
  "status": "created",
  "id": 1,
  "cloudflare_id": "cf-cert-id",
  "expires_on": "2041-03-23T00:00:00Z",
  "requested_zone": ""
}
The private key is generated locally, never sent to Cloudflare, and stored encrypted (AES-256-GCM) in SQLite.

Custom Certificate Upload

Upload any PEM-encoded certificate and private key pair through the admin UI or API.

API Endpoint

POST /api/v1/certificates
{
  "domains": ["app.example.com"],
  "cert_pem": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
  "key_pem": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
}

Validation

  • The certificate and key must form a valid X.509 key pair
  • The leaf certificate is parsed to extract the issuer, expiration, and subject
  • The private key is encrypted with AES-256-GCM before storage

Assigning Certificates to Hosts

Certificates are assigned to proxy hosts through the host configuration:

Let’s Encrypt

Set ssl_mode to letsencrypt on the proxy host. No certificate ID is needed — autocert handles issuance automatically.
{
  "domain": "app.example.com",
  "ssl_mode": "letsencrypt",
  "force_https": true
}

Custom / Cloudflare Origin

Set ssl_mode to custom and either:
  • Inline PEM — provide ssl_cert_pem and ssl_key_pem directly in the host update (Aegis stores and encrypts them automatically)
  • Certificate ID — reference an existing certificate by ssl_cert_id
{
  "domain": "app.example.com",
  "ssl_mode": "custom",
  "ssl_cert_id": 1,
  "force_https": true
}

SNI Certificate Selection

When a TLS handshake arrives, Aegis selects the certificate in this order:
  1. Host binding — if the proxy host has an ssl_cert_id, use that certificate
  2. Domain match — check all stored custom certificates for a domain match
  3. Autocert — attempt Let’s Encrypt certificate retrieval (for hosts with ssl_mode: letsencrypt)
  4. Self-signed fallback — generate a self-signed certificate covering all configured domains
This ensures every TLS connection receives a certificate, even if the preferred source is unavailable.

Private Key Encryption

All private keys are encrypted at rest using AES-256-GCM:
  • Key source — provided via AEGIS_SSL_KEY (64-char hex string) or auto-generated on first run
  • Auto-generated keys are stored in the SQLite settings table
  • Each key gets a unique random nonce (GCM nonce size)
  • Decryption happens only when loading certificates into memory at startup or after a reload

Certificate Lifecycle

EventBehavior
Host created with Let’s EncryptCertificate requested on first TLS handshake
Host created with custom certCertificate stored immediately, proxy reloaded
Cloudflare Origin requestedCSR generated, sent to Cloudflare API, cert stored
Certificate approaches expiryLet’s Encrypt: auto-renewed by autocert. Custom/Cloudflare: manual renewal required
Certificate deletedOnly allowed if not assigned to any host. Proxy reloaded after deletion
Host SSL mode changedCertificate binding updated, proxy reloaded

Host SSL Settings

Each proxy host supports these SSL-related settings:
SettingDescription
ssl_modenone, letsencrypt, or custom
ssl_cert_idReference to a stored certificate (for custom mode)
force_https301 redirect HTTP to HTTPS
hsts_enabledEnable Strict Transport Security header
hsts_max_ageHSTS max-age in seconds
hsts_include_subdomainsInclude subdomains in HSTS
hsts_preloadAdd preload directive to HSTS
tls_min_versionMinimum TLS version (default: TLS 1.2)

API Reference

MethodPathDescription
GET/api/v1/certificatesList all stored certificates (domains, source, issuer, expiry)
POST/api/v1/certificatesUpload a custom certificate (PEM cert + key)
DELETE/api/v1/certificates/{id}Delete a certificate (fails if assigned to a host)
POST/api/v1/certificates/cloudflare-originRequest a Cloudflare Origin CA certificate

Certificate Object

FieldTypeDescription
idintegerCertificate ID
domainsarrayDomain names covered by the certificate
sourcestringcustom, cloudflare_origin, or letsencrypt
issuerstringCertificate issuer common name
expires_atstringISO 8601 expiration timestamp
auto_renewbooleanWhether the certificate was marked for auto-renewal
created_atstringISO 8601 creation timestamp