> For the complete documentation index, see [llms.txt](https://docs.kangal.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kangal.dev/gateway/tls-certificates.md).

# TLS and certificates

Kangal stores server certificates, hostname-to-certificate mappings, and CA certificates as gateway configuration. Use these resources to prepare TLS material for a data plane or an edge component that consumes the Kangal configuration bundle.

## TLS resource types

| Resource       | Purpose                                                            | Contains private key material |
| -------------- | ------------------------------------------------------------------ | ----------------------------- |
| Certificate    | Server certificate and private key used to present a TLS identity. | Yes                           |
| SNI            | Maps a hostname such as `api.example.com` to a certificate.        | No                            |
| CA certificate | Trust anchor used by a TLS or authentication policy.               | No                            |

Certificates, CA certificates, and SNI mappings are included in data-plane configuration bundles. The serving edge must be configured to consume these resources and terminate TLS. After a change, verify both bundle acknowledgement and the certificate presented by the public listener.

## Before you upload a certificate

Prepare a PEM-encoded certificate chain and its matching PEM-encoded private key. Put the leaf certificate first, followed by any intermediates required by clients. Keep the root CA out of the served chain unless your PKI requires it.

Before upload, operators must validate the certificate chain, certificate/key match, validity and expiry dates, and SAN coverage for every served hostname.

Inspect the certificate:

```bash
openssl x509 -in tls.crt -noout -subject -issuer -serial -dates \
  -ext subjectAltName
```

Compare certificate and key public keys:

```bash
umask 077

openssl x509 -in tls.crt -pubkey -noout \
  | openssl pkey -pubin -outform DER \
  | openssl sha256

openssl pkey -in tls.key -pubout -outform DER \
  | openssl sha256
```

The two SHA-256 values must match. Also verify the chain against your trust bundle:

```bash
openssl verify -CAfile root-ca.pem -untrusted intermediate-ca.pem tls.crt
```

## Upload a certificate

### Console workflow

1. Open a gateway and select **Certificates**.
2. Select **Create certificate**.
3. Enter a stable name.
4. Paste the certificate chain into the certificate field.
5. Paste the matching private key into the key field.
6. Save, then create an SNI mapping for each served hostname.

Certificate names are unique per tenant. Use a naming convention that includes the hostname and environment, for example `api-example-com-prod`.

### Admin API

```bash
umask 077
set -e
certificate_payload="$(mktemp)"
trap 'rm -f "$certificate_payload"' EXIT

export KANGAL_ADMIN_URL="https://control.example.com"
export KANGAL_TOKEN="replace-with-admin-token"
export KANGAL_TENANT_ID="tenant_123"
export KANGAL_GATEWAY_ID="gateway_123"

jq -n \
  --arg tenant_id "$KANGAL_TENANT_ID" \
  --arg name "api-example-com-prod" \
  --rawfile cert_pem tls.crt \
  --rawfile key_pem tls.key \
  '{tenant_id: $tenant_id, name: $name, cert_pem: $cert_pem, key_pem: $key_pem}' \
  > "$certificate_payload"

curl -sS -X POST \
  "$KANGAL_ADMIN_URL/api/v1/admin/gateways/$KANGAL_GATEWAY_ID/certificates" \
  -H "Authorization: Bearer $KANGAL_TOKEN" \
  -H "Content-Type: application/json" \
  --data-binary "@$certificate_payload"

rm -f "$certificate_payload"
trap - EXIT
```

The response intentionally returns metadata rather than the PEM values:

* `id`
* `tenant_id`
* `name`
* `cert_digest`
* `previous_cert_digest`
* `rotation_version`
* `rotated_at`

Record the returned `id`; SNI mappings refer to it as `certificate_id`.

## Map a hostname with SNI

Create one SNI resource per hostname that should present the certificate:

```bash
export KANGAL_CERTIFICATE_ID="certificate_123"

curl -sS -X POST \
  "$KANGAL_ADMIN_URL/api/v1/admin/gateways/$KANGAL_GATEWAY_ID/snis" \
  -H "Authorization: Bearer $KANGAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "'"$KANGAL_TENANT_ID"'",
    "name": "api.example.com",
    "certificate_id": "'"$KANGAL_CERTIFICATE_ID"'",
    "labels": {"environment": "production"},
    "tags": ["public"],
    "metadata": {"owner": "platform"}
  }'
```

The certificate must belong to the same gateway and tenant. Repeating the same hostname and certificate returns the existing mapping. Mapping the same hostname to another certificate returns `409 Conflict`.

Hostnames managed by custom-domain automation are reserved. Manage those hostnames through the custom-domain workflow so certificate renewal and SNI ownership stay consistent.

An SNI mapping selects certificate material for the TLS hostname. Route `snis` are separate request matchers: use them only when route selection must also depend on the TLS server name.

## Rotate a certificate

Validate the replacement certificate and key, then use the rotation operation:

```bash
umask 077
set -e
rotation_payload="$(mktemp)"
trap 'rm -f "$rotation_payload"' EXIT

export KANGAL_CERTIFICATE_ID="certificate_123"

jq -n \
  --arg name "api-example-com-prod" \
  --arg rotated_by "platform-rotation" \
  --rawfile cert_pem tls-new.crt \
  --rawfile key_pem tls-new.key \
  '{name: $name, cert_pem: $cert_pem, key_pem: $key_pem, rotated_by: $rotated_by}' \
  > "$rotation_payload"

curl -sS -X POST \
  "$KANGAL_ADMIN_URL/api/v1/admin/gateways/$KANGAL_GATEWAY_ID/certificates/$KANGAL_CERTIFICATE_ID/rotate" \
  -H "Authorization: Bearer $KANGAL_TOKEN" \
  -H "Content-Type: application/json" \
  --data-binary "@$rotation_payload"

rm -f "$rotation_payload"
trap - EXIT
```

Rotation requires both `cert_pem` and `key_pem`. It replaces the active material, increments `rotation_version`, records the previous digest, and appends rotation history. Existing SNI resources continue to reference the same certificate ID.

This is a single-record replacement rather than a dual-certificate serving period. Coordinate the change with configuration rollout and edge reloads, and retain the previous certificate and key in your approved secret backup until the new listener has been verified.

Verify the live endpoint:

```bash
openssl s_client -connect api.example.com:443 \
  -servername api.example.com -showcerts </dev/null
```

Compare the presented certificate fingerprint with the replacement material:

```bash
openssl x509 -in tls-new.crt -noout -fingerprint -sha256
```

## Add a CA certificate

CA certificates are trust material and contain no private key. Create one when a client-certificate authentication policy or another TLS consumer needs the CA:

```bash
curl -sS -X POST \
  "$KANGAL_ADMIN_URL/api/v1/admin/gateways/$KANGAL_GATEWAY_ID/ca-certificates" \
  -H "Authorization: Bearer $KANGAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n \
    --arg tenant_id "$KANGAL_TENANT_ID" \
    --arg name "partner-client-ca" \
    --rawfile cert_pem partner-ca.crt \
    '{tenant_id: $tenant_id, name: $name, cert_pem: $cert_pem,
      labels: {purpose: "client-auth"}}')"
```

The API returns the CA PEM and its SHA-256 digest. Supplying the same name and digest returns the existing resource; the same name with different material returns `409 Conflict`.

Creating a CA resource establishes trust material only. To enforce mutual TLS, also configure the authentication plugin or edge policy that requests and validates client certificates.

## Update and delete safely

Certificate update accepts `name`, `cert_pem`, `key_pem`, and `tenant_id`. Prefer the rotation operation for active certificate replacement because it records rotation metadata. The Console edit workflow requests the complete name, certificate, and key; prepare all three before opening the form.

SNI update accepts `name`, `certificate_id`, `labels`, `tags`, and `metadata`. Use it to move a hostname to a prevalidated replacement certificate when that workflow is preferable to rotating in place.

Certificate deletion is not dependency-cascading. Delete or repoint every SNI that references the certificate before deleting it. Otherwise the configuration contains a hostname mapping whose certificate cannot be resolved.

For CA certificates, remove every consuming authentication or edge policy before deletion.

## Security requirements

* Treat certificate API requests, temporary JSON files, terminal history, audit access, backups, and database access as secret-bearing surfaces.
* The manual certificate workflow stores the submitted private key in the certificate record. Require encrypted database storage, encrypted backups, strict tenant-scoped authorization, and limited operator access.
* Use `--data-binary @file` or generated JSON files instead of putting PEM keys directly in shell arguments.
* Start private-key shell workflows with `umask 077`; register a cleanup trap for every secret-bearing temporary file and remove it immediately after use.
* Never place private keys in labels, tags, metadata, route configuration, or support tickets.
* Monitor expiry externally and rotate early enough to complete rollout and edge verification before the previous certificate expires.

## Troubleshooting

### The API accepts a certificate that the listener rejects

Run the OpenSSL validation steps again. Check PEM boundaries, chain order, key matching, key passphrase requirements, expiry, SAN coverage, and the edge component's supported algorithms.

### Clients receive the wrong certificate

Confirm the client sends the expected SNI hostname, the SNI resource has the correct `certificate_id`, and the serving edge has acknowledged and loaded the latest bundle. Test with `openssl s_client -servername` rather than an IP-only connection.

### SNI creation returns `400`

The certificate and SNI tenant IDs differ, or the certificate belongs to another gateway. Use a certificate from the same tenant and gateway.

### SNI creation returns `409`

The hostname is already mapped to another certificate or is owned by a custom domain. Update the owning resource instead of creating a second mapping.

### Rotation metadata changed but clients still see the old certificate

The control-plane record is updated, but the serving data plane or external edge still needs the new configuration. Check bundle acknowledgement, edge reload status, DNS destination, and every listener behind the load balancer.

### Mutual TLS accepts no clients

Confirm that the CA resource is referenced by an active client-certificate authentication policy, the edge requests client certificates, and clients send the full required chain.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kangal.dev/gateway/tls-certificates.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
