<a id="understanding-tls"></a>

# Understanding TLS in Juju deployments

X.509 certificates are essential to the security of many Internet protocols, including TLS/SSL. If your charm workload communicates over HTTPS, you most likely need these certificates. Within the Juju ecosystem, the `tls-certificates` charm relation interface handles X.509 certificate creation, renewal, and revocation.

This guide walks you through two key decisions:

1. **Where** should TLS be terminated in your deployment?
2. **Which** TLS provider charm should you use?

## Where to terminate TLS

The first question to answer is where TLS connections should be terminated. This determines whether your application charm needs to manage certificates directly.

### At the ingress level

In deployments where X.509 certificates are needed to ensure HTTPS communication with users or systems **outside** of the Juju model, it is recommended to use an ingress controller like [Traefik](https://charmhub.io/traefik-k8s). Traefik handles TLS termination, sending decrypted traffic to your application over HTTP. Your application charm does not need to manage X.509 certificates — implementing the ingress integration is all that’s required as Traefik already supports the `tls-certificates` interface.

In this setup:

- The **client** communicates with the ingress over **HTTPS**.
- The **ingress** (Traefik) terminates TLS and forwards **HTTP** traffic to the application.
- The **TLS provider** supplies certificates to the ingress through the `tls-certificates` integration.

### At the application level

For use cases where HTTPS communication is needed **inside** the Juju model — such as between an ingress and the application, or between different units of the same application — TLS connections must be terminated at the application level. This requires implementing the requirer side of the `tls-certificates` interface in the application charm.

A typical example is enabling various units of a PostgreSQL cluster to communicate via TLS, or ensuring end-to-end encryption between an ingress and the backend application.

In this setup:

- The **client** communicates with the ingress over **HTTPS**.
- The **ingress** (Traefik) forwards **HTTPS** traffic to the application (end-to-end encryption).
- The **TLS provider** supplies certificates to **both** the ingress and the application through the `tls-certificates` integration.

## Choosing a TLS provider

Once you’ve determined where TLS will be terminated, the next step is choosing the right TLS provider charm for your deployment. Use the following decision tree:

### Self-signed certificates

The [self-signed-certificates](https://charmhub.io/self-signed-certificates) operator is ideal for:

- **Internal communication**: Securing traffic between units of the same application (e.g., PostgreSQL cluster replication) or between applications within a Juju model.
- **Development and non-production environments**: Quick setup without external dependencies.

Upon deployment, the self-signed-certificates operator generates a private key and a CA certificate (not signed by any external authority). It signs each certificate request it receives using this self-signed CA certificate. Because the CA is self-signed, clients and applications must explicitly trust it — see [CA trust best practices](explanation/ca-trust-best-practices.md#ca-trust-best-practices) for details.

This charm implements the `tls-certificates` interface using the [tls_certificates library](https://documentation.ubuntu.com/charmlibs/reference/charmlibs/interfaces/tls-certificates), which provides automatic certificate renewal before expiry. Additionally supports CA private key rotation via the `rotate-private-key` action, which automatically revokes and regenerates all certificates for integrated applications.

### Lego (Let’s Encrypt or another ACME server)

The [Lego](https://charmhub.io/lego) charm operator requests certificates using the ACME protocol from providers like Let’s Encrypt. The ACME server validates domain ownership using the [DNS-01](https://letsencrypt.org/docs/challenge-types/) challenge.

This is a good option when you need publicly trusted certificates and your DNS provider is supported. If your DNS provider is not currently supported, reach out to the team.

This charm implements the `tls-certificates` interface using the [tls_certificates library](https://documentation.ubuntu.com/charmlibs/reference/charmlibs/interfaces/tls-certificates), which provides automatic certificate renewal before expiry and structured error reporting with standardized codes when certificate requests fail.

### Manual TLS certificates

The [manual-tls-certificates](https://charmhub.io/manual-tls-certificates) operator is designed for organisations that have an existing manual process for managing certificates. It supports actions to list certificate requests, retrieve CSRs, and supply manually obtained certificates.

For a more streamlined experience, consider [Notary](https://canonical-notary.readthedocs-hosted.com/en/latest/), which provides:

- A **web UI** for managing certificate requests and lifecycle.
- **User management** for controlling access to certificate operations.
- **HSM support** for hardware-backed key storage.

### Vault

[Vault](https://charmhub.io/vault-k8s) is a popular secret management platform. Both the Kubernetes and machine versions of the Vault charm can be used as an intermediate Certificate Authority to provide certificates in the Juju ecosystem.

Vault is well-suited for production deployments that require a full-featured PKI solution with secret management capabilities. See the [Vault charm documentation](https://canonical-vault-charms.readthedocs-hosted.com/en/latest/) for details.

This charm implements the `tls-certificates` interface using the [tls_certificates library](https://documentation.ubuntu.com/charmlibs/reference/charmlibs/interfaces/tls-certificates), which provides automatic certificate renewal before expiry and structured error reporting with standardized codes when certificate requests fail.

## Why API and internal certificates often require separate CAs

For API-facing (external) traffic, teams often use a publicly trusted CA through `lego` (e.g., Let’s Encrypt). However, **publicly trusted CAs cannot issue certificates that contain IP addresses as Subject Alternative Names (SANs)**, and they will not issue certificates for private or internal hostnames (e.g., `10.0.0.5` or `postgresql.cluster.local`).

This is a hard constraint from the CA/Browser Forum baseline requirements—not a configuration option.

Internal unit-to-unit communication frequently requires certificates with IP SANs or private hostnames (for example, a PostgreSQL unit certificate must include the unit’s IP address so that peer connections can be validated). Because a public CA cannot satisfy these requirements, **a private CA is always required for internal certificates**, even if a public CA is used for external API traffic.

The practical consequence for most production deployments is:

- **API-facing (ingress) certificates**: issued by a public CA via `lego`, or by Vault/Notary when a private PKI with public trust is acceptable.
- **Internal (unit-to-unit) certificates**: issued by `self-signed-certificates` or Vault, using a private CA that can include IP SANs and private hostnames.

See [Securing API communication](explanation/securing-api-communication.md#securing-api-communication) for an example of how to wire both providers in the same deployment.
