# TLS

Source: https://apigenie.pl/docs/reference/server/tls
Part of the reference of [apigenie-server](https://apigenie.pl/docs/reference/server.md).
Its other pages: [Responses](https://apigenie.pl/docs/reference/server/responses.md), [Scenarios and extensions](https://apigenie.pl/docs/reference/server/scenarios.md), [Scripts](https://apigenie.pl/docs/reference/server/scripts.md), [Document store](https://apigenie.pl/docs/reference/server/store.md), [Several services](https://apigenie.pl/docs/reference/server/services.md), [Validation and security](https://apigenie.pl/docs/reference/server/validation.md), [Contract testing (validating proxy)](https://apigenie.pl/docs/reference/server/contract-testing.md), [Session recording and replay](https://apigenie.pl/docs/reference/server/recording.md), [AsyncAPI](https://apigenie.pl/docs/reference/server/asyncapi.md), [Protobuf / gRPC](https://apigenie.pl/docs/reference/server/grpc.md), [GraphQL](https://apigenie.pl/docs/reference/server/graphql.md), [Configuration](https://apigenie.pl/docs/reference/server/configuration.md), [Dashboard and admin API](https://apigenie.pl/docs/reference/server/dashboard.md).

`apigenie-server` serves TLS itself, and its proxies reach `https://` and `wss://` servers and
`mqtts://` brokers. TLS 1.2 and 1.3 come from Mbed TLS. It's built with what modern servers use:
AES-GCM and ChaCha20-Poly1305, ECDHE key exchange on x25519, P-256, P-384 and P-521, and RSA and
ECDSA certificates. A server that offers only static RSA or DHE key exchange, or other ciphers,
can't be reached.

**Serving.** `--tls-cert server.pem --tls-key server.key` (or `tls.cert` and `tls.key` in the
configuration) turns the port into HTTPS. WebSocket channels and GraphQL subscriptions become
WSS, and clients that ask for `h2` by ALPN get HTTP/2, which is how browsers and gRPC clients
over TLS connect. The admin API on a port of its own (`admin.port`) uses the same certificate. The MQTT
broker of an AsyncAPI document serves MQTT over TLS too, on port 8883 unless `--mqtt-port` says
otherwise. The startup line shows `https://` and `mqtts://` URLs and, as JSON, `"tls": true`. The
certificate file may hold the chain, the server's certificate first. The key must be PEM and not
encrypted. For local development, `mkcert localhost 127.0.0.1` makes a pair your system trusts:

```sh
apigenie-server openapi.yaml --tls-cert localhost+1.pem --tls-key localhost+1-key.pem
curl https://localhost:4010/pets
```

**Upstreams.** `--proxy-to https://api.example.com` (OpenAPI, GraphQL, AsyncAPI HTTP channels),
`wss://...` (AsyncAPI WebSocket channels), `mqtts://...` (AsyncAPI MQTT channels, port 8883 unless
given) and `https://host[:port]` (gRPC, HTTP/2 over TLS) connect over TLS, port 443 unless given.
Certificates are verified against the system's roots:

- macOS: the system keychain, and the certificates an administrator or the user marked as always
  trusted;
- Windows: the ROOT store;
- Linux: the first CA bundle found (`/etc/ssl/certs/ca-certificates.crt`, `/etc/pki/tls/certs/ca-bundle.crt`, ...).

`SSL_CERT_FILE` and `SSL_CERT_DIR` replace them on every system. `--ca-cert FILE` adds authorities,
such as a company's own root or a test CA, and `--insecure` skips verification altogether. The host
name of the URL is checked against the certificate (an IP address against its IP addresses). An
upstream whose certificate is rejected gets the usual 502 `upstream-unreachable`, with the reason
in the detail.

```yaml
tls:
  cert: certs/server.pem     # relative to the configuration file
  key: certs/server.key
  ca_certs: [certs/company-root.pem]
  insecure: false
```

## Client certificates (mTLS)

**Requiring them.** `--tls-client-ca clients-ca.pem` (or `tls.client_ca`) makes every HTTPS, WSS,
gRPC and MQTTS connection present a client certificate issued by an authority of that PEM file, as
a service behind mutual TLS does. It needs `--tls-cert`. The certificate's extended key usage must
allow client authentication when it has one.

- A client without a certificate, or with one the authorities didn't issue, fails the TLS
  handshake. The log gets a warning that says why, for example `TLS handshake with 127.0.0.1
  failed: the client presented no certificate`.
- The subject of an accepted certificate (`CN=billing, O=Example`) is in each event of the JSON log
  (`"clientSubject"`), on a `~ client` line of the stylish log, and in the admin API's log.
- The contract report lists the clients: `clients` in JSON (`[{"subject": "CN=billing, O=Example",
  "exchanges": 12}]`, counting HTTP requests, gRPC calls, WebSocket connections and MQTT
  messages), a `client` property per subject in JUnit, and a `Clients:` line in the summary.
- The admin API asks for a certificate too, on the main port and on `admin.port`.

```sh
apigenie-server openapi.yaml --tls-cert server.pem --tls-key server.key --tls-client-ca clients-ca.pem
curl --cacert ca.pem --cert client.pem --key client.key https://localhost:4010/pets
```

**Presenting one.** `--client-cert client.pem --client-key client.key` (or `tls.client_cert` and
`tls.client_key`) is the certificate the proxies present to `https://`, `wss://`, `mqtts://` and
gRPC TLS upstreams that ask for one. Upstreams that don't ask never see it. It works with
`--insecure` too. The key must be PEM and not encrypted. An upstream that refuses the proxy for
want of a certificate gets the usual 502 `upstream-unreachable`, which says so in the detail (an
MQTT client's CONNECT gets return code 3).

```yaml
tls:
  cert: certs/server.pem
  key: certs/server.key
  client_ca: certs/partners-ca.pem
  client_cert: certs/proxy.pem
  client_key: certs/proxy.key
```

**Not supported.** There is no session resumption: every connection does a full handshake. Neither
certificate revocation lists (CRL) nor OCSP are checked, so a revoked client or upstream
certificate is accepted as long as its chain is valid. Remove a revoked client's authority from
`--tls-client-ca`, or issue client certificates with short lifetimes.
