HTTPS and Let's Encrypt

The Orchestrator serves plain HTTP unless you give it a certificate. It supports four setups.

Setup Settings
Plain HTTP (the default) None. Port ORC_PORT (8080).
Your own certificate ORC_CERT_FILE, plus ORC_CERT_FILE_PRIVATE_KEY for a PEM, and ORC_CERT_PWD for a PFX or an encrypted key.
A self-signed certificate ORC_CERT_SELF_SIGNED=true.
Let's Encrypt ORC_LETSENCRYPT=true with ORC_LETSENCRYPT_EMAIL and ORC_LETSENCRYPT_ACCEPT_TERMS=true.

With a certificate, ORC_PORT defaults to 443. ORC_PORT_LOCAL (default 8080) adds a plain HTTP port that listens on loopback only, or on every address with ORC_REDIRECT_TO_HTTPS=true. Set it to 0 to disable it. ORC_USE_HSTS=true adds HSTS headers.

Your own certificate

ORC_CERT_FILE takes a PFX, a CER or a PEM certificate. For a PEM, set ORC_CERT_FILE_PRIVATE_KEY to the key. To redirect http:// to HTTPS, set ORC_PORT_HTTP=80. The certificate has to cover *.<your host> as well when you use subdomain routing.

Self-signed

ORC_CERT_SELF_SIGNED=true generates a certificate valid for 5 years (RSA 2048) into ORC_STORAGE/self.pfx and reuses it. It names the host of ORC_PUBLIC_ADDRESS and *.<that host>. Browsers warn about it. It is the right choice for a try-out or for traffic inside a private network.

Let's Encrypt

ORC_ADMIN_PASSWORD='choose-a-password' ORC_PUBLIC_ADDRESS=https://workspaces.example.com \
ORC_LETSENCRYPT=true ORC_LETSENCRYPT_EMAIL=ops@example.com ORC_LETSENCRYPT_ACCEPT_TERMS=true \
curiosity-orchestrator

The Orchestrator picks a certificate per host name (SNI).

Host name Certificate
workspaces.example.com (ORC_LETSENCRYPT_DOMAINS) One certificate for all of the Orchestrator's own names, issued at start-up.
aviation.workspaces.example.com The workspace's own, issued at start-up or, for a workspace created later, on the next pass (10 minutes, or 15 after an issuance).
Anything else: an IP address, no SNI, a name still waiting The fallback: ORC_CERT_FILE when set, otherwise the self-signed certificate.

Requirements

  • Ports 443 and 80 reachable from the internet, and DNS for every name pointing at the server. A wildcard record covers the workspaces.
  • Validation uses TLS-ALPN-01 on 443, with HTTP-01 on ORC_PORT_HTTP as the alternative. ORC_PORT_HTTP defaults to 80 with Let's Encrypt. It answers challenges and redirects everything else to HTTPS with a 307.
  • ORC_LETSENCRYPT_EMAIL and ORC_LETSENCRYPT_ACCEPT_TERMS=true are required. The second accepts the subscriber agreement.

What it does not do

  • No wildcard certificate. That needs a DNS challenge, so each workspace gets its own certificate. Turn per-workspace certificates off with ORC_LETSENCRYPT_WORKSPACES=false.
  • Rate limits apply. Let's Encrypt issues 50 certificates per registered domain per week, and each workspace is one. Use ORC_LETSENCRYPT_STAGING=true while trying things out.

Renewal, failures and status

  • Renewal starts 30 days before expiry. Certificates and the account key are kept in ORC_STORAGE/letsencrypt and loaded at start-up, so a restart orders nothing. Back that folder up with the rest of ORC_STORAGE.
  • Failures (DNS not set up yet, a port closed) are retried after 15 minutes. That stays under Let's Encrypt's limit of 5 failed validations per name per hour, and the fallback answers meanwhile. If certificate management stops on an unexpected error, for example the CA being unreachable at start-up, it starts again after 15 minutes.
  • Status: Settings in the console lists every name with its state (valid, waiting, expiring, expired, fallback), its issuer and its expiry. Scripts can read GET /api/admin/https.

Staging first

Start with ORC_LETSENCRYPT_STAGING=true. The certificates are untrusted, so browsers warn, but the rate limits are much higher. When everything works, remove the setting and delete the staging certificates:

sudo systemctl stop curiosity-orchestrator
sudo rm -rf "$ORC_STORAGE/letsencrypt"
sudo systemctl start curiosity-orchestrator

A private CA

ORC_LETSENCRYPT_SERVER points at another ACME directory, and ORC_LETSENCRYPT_ISSUERS lists PEM files of that CA's root and intermediates, comma separated. The ACME client follows Let's Encrypt's behavior. A CA that requires POST-as-GET to read an account's orders fails the first order, and one that rejects a repeated terms of service agreement fails on every restart. Try a private CA with staging data first.

A separate host for the console

ORC_MANAGEMENT_HOST=manage.example.com makes the console and its API answer only on that host, and workspaces are not served there. Let's Encrypt includes the name in the Orchestrator's certificate. See Security.

Step by step: run it on a server