Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/how-to/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ Manage identities <manage-identities>
```


## Trust contexts

Use named "trust contexts" to make services, checks, and log targets trust custom TLS CA certificates.

```{toctree}
:titlesonly:
:maxdepth: 1

Use TLS CA certificates with trust contexts <use-tls-ca-certs-with-trust-context>
```


## API

To integrate Pebble with your automated workflows, you can use the Pebble API.
Expand Down
231 changes: 231 additions & 0 deletions docs/how-to/use-tls-ca-certs-with-trust-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# How to use TLS CA certificates with trust contexts

Services, checks, and log targets often need to talk to a server over TLS.
For example, a service calling an internal HTTPS API, an `http` check probing an
HTTPS endpoint, or a `loki`/`opentelemetry` log target pushing logs to a
collector. If that server's certificate is signed by a CA that isn't in the
host's system CA pool (e.g. a private/internal CA), the TLS handshake will fail
unless Pebble is told to trust that CA.

Pebble solves this with **trust contexts**: named collections of trusted CA
certificates, configured once in the plan and then referenced by name wherever
they're needed.

This guide shows how to declare a trust context with a custom CA certificate,
and how to use it from a service, a check, a log target, and `pebble exec`.

## Trust context basics

Trust contexts are declared in the plan's top-level `trust-contexts` section:

```yaml
trust-contexts:
vendorA:
override: merge
tls:
ca-cert: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
```

Two trust contexts are always available, even if you don't declare anything:

- `system`: an immutable trust context backed by the host's default CA
certificate pool. This name is reserved; you cannot declare your own trust
context called `system`.
- `default`: the trust context used automatically by any service, check, or
log target that doesn't set `trust-context` explicitly. The "default" trust
context simply includes `system`, but you can alter it in your plan (see
[Change what "default" trusts](#change-what-default-trusts) below).

For the full specification, see [Layer specification](../reference/layer-specification).

## Add a custom CA certificate

Suppose you run an internal HTTPS service (`https://api.internal.example.com`)
whose certificate is signed by an internal CA, `vendorA`. Add a trust context
with that CA certificate:

```yaml
# ca-cert-layer.yaml
trust-contexts:
vendorA:
override: merge
tls:
ca-cert: |
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAJC1H...
-----END CERTIFICATE-----
```

`ca-cert` accepts one or more PEM-encoded certificates concatenated together,
so you can include an entire chain (intermediate plus root) if needed.

Add the layer:

```{terminal}
pebble add ca-certs ca-cert-layer.yaml

Layer "ca-certs" added successfully from "ca-cert-layer.yaml"
```

At this point, `vendorA` is declared but not used by anything yet.

## Use a trust context in a service

To make a service trust `vendorA`'s CA bundle, set `trust-context` on the
service:

```yaml
services:
myservice:
override: merge
command: /usr/bin/myservice
trust-context: vendorA
```

When `myservice` starts, the `vendorA` trust context is resolved and sets the
`SSL_CERT_FILE` environment variable to point at a PEM bundle containing
`vendorA`'s CA certificate(s). Most TLS libraries (including Go's `crypto/x509`
and OpenSSL-based stacks) honor `SSL_CERT_FILE` automatically, so `myservice`
should trust `https://api.internal.example.com` without any code changes.

If `myservice` sets its own `SSL_CERT_FILE` in `environment`, that value is
not modified.

## Use a trust context in a check

### HTTP check

For an `http` check that probes an HTTPS URL signed by `vendorA`, set
`trust-context` on the check's `http` section:

```yaml
checks:
api-up:
override: replace
http:
url: https://api.internal.example.com/health
trust-context: vendorA
```

The resolved CA pool is used to validate the server's certificate when
performing the check.

### Exec check

For an `exec` check that runs a command needing to make its own TLS connections,
set `trust-context` on the check's `exec` section, the same way as for services:

```yaml
checks:
api-check:
override: replace
exec:
command: /usr/bin/check-api.sh
trust-context: vendorA
```

As with services, this sets `SSL_CERT_FILE` environment variable (unless it's
already set).

## Use a trust context for a log target

If your Loki or OpenTelemetry collector uses a certificate signed by `vendorA`,
set `trust-context` on the log target:

```yaml
log-targets:
loki-internal:
override: merge
type: loki
location: https://loki.internal.example.com:3100/loki/api/v1/push
services: [all]
trust-context: vendorA
```

`trust-context` is only supported for `loki` and `opentelemetry` log targets.

## Use a trust context with `pebble exec`

`pebble exec --context <service-name>` inherits environment variables,
user/group, and working directory from the named service. If that service has a
`trust-context` configured, the trust context is inherited too:

```{terminal}
pebble exec --context myservice curl https://api.internal.example.com/health

{"status": "ok"}
```

Without `--context`, `pebble exec` uses the `default` trust context (typically
just the system CA pool), so an unrelated internal CA won't be trusted unless
you add it to `default` (see below).

(change-what-default-trusts)=
## Change what "default" trusts

If most things in your plan need to trust `vendorA`, it may be simpler to add
it to the `default` trust context instead of setting `trust-context` everywhere.
The `default` trust context can only use `include` (i.e. it can't declare its
own `tls.ca-cert` directly):

```yaml
trust-contexts:
default:
override: merge
include: [system, vendorA]
```

With this in place, any service, check, log target or `pebble exec` that doesn't
set `trust-context` explicitly will trust `vendorA`'s CA in addition to the
system CA pool.

## Combine multiple CAs

A trust context can include other trust contexts, so you can build up a combined
set of trusted CAs. For example, to trust both `vendorA` and `vendorB`:

```yaml
trust-contexts:
vendorA:
override: merge
tls:
ca-cert: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
vendorB:
override: merge
tls:
ca-cert: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
combined:
override: merge
include: [vendorA, vendorB]

services:
myservice:
override: merge
command: /usr/bin/myservice
trust-context: combined
```

Use the built-in name `system` in an `include` list to add the host's system CA
pool alongside your custom CAs:

```yaml
trust-contexts:
combined:
override: merge
include: [system, vendorA, vendorB]
```

## See more

- [Layer specification](../reference/layer-specification)
- [Health checks](../reference/health-checks)
- [Log forwarding](../reference/log-forwarding)
6 changes: 6 additions & 0 deletions docs/reference/cli-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,12 @@ For `startup: enabled` services that are running:

Replan also starts any `startup: enabled` services that have not yet been started, or that have been manually stopped.

If the service references a trust context, and that referenced trust context has
changed since the service started, replan will cause that service to restart. By
default, a service that does not reference a trust context will implicitly use
the "default" trust context. As such, if the "default" trust context or any of
its included trust contexts have changed, the service will restart.

### Examples

Here is an example, where `srv1` is a service that has `startup: enabled`, and `srv2` does not:
Expand Down
77 changes: 77 additions & 0 deletions docs/reference/layer-specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ services:
# Example: /usr/bin/somedaemon --db=/db/path [ --port 8080 ]
command: <commmand>

# (Optional) The name of the trust context used to provide a CA
# certificate bundle to the service process. If a trust context is
# configured (and it doesn't resolve to the system CA pool), its CA
# bundle is exposed to the process via the SSL_CERT_FILE environment
# variable, unless SSL_CERT_FILE is already set explicitly. If not
# specified, the "default" trust context is used. See the
# "trust-contexts" section below for details.
trust-context: <trust context name>

# (Optional) A short summary of the service.
summary: <summary>

Expand Down Expand Up @@ -180,6 +189,12 @@ checks:
headers:
<name>: <value>

# (Optional) The name of the trust context used to validate the
# TLS certificate presented by the server (relevant only for
# "https" URLs). If not specified, the "default" trust context
# is used. See the "trust-contexts" section below for details.
trust-context: <trust context name>

# Configures a TCP port check, which is successful if the specified
# TCP port is listening and we can successfully open it. Nothing is
# sent to the port.
Expand Down Expand Up @@ -235,6 +250,16 @@ checks:
# command is run in the service manager's current directory.
working-dir: <directory>

# (Optional) The name of the trust context used to provide a CA
# certificate bundle to the command. If a trust context is
# configured (and it doesn't resolve to the system CA pool), its
# CA bundle is exposed to the command via the SSL_CERT_FILE
# environment variable, unless SSL_CERT_FILE is already set
# explicitly (or inherited via service-context). If not
# specified, the "default" trust context is used. See the
# "trust-contexts" section below for details.
trust-context: <trust context name>

# (Optional) A list of remote log receivers, to which service logs can be sent.
log-targets:

Expand Down Expand Up @@ -285,6 +310,58 @@ log-targets:
labels:
<label name>: <label value>

# (Optional) The name of the trust context used to validate the TLS
# certificate presented by the remote log target. Only applicable to the
# "loki" and "opentelemetry" types. If not specified, the default" trust
# context is used. See the "trust-contexts" section below for details.
trust-context: <trust context name>

# (Optional) A list of named trust contexts, each holding a set of trusted CA
# certificates that can be referenced (by name, via "trust-context" fields)
# in services, checks, and log targets that need to establish TLS trust with
# an external server or provide a CA bundle to a process.
#
# Two trust contexts are always available, even if not declared in any
# layer:
#
# - "system": an immutable trust context backed by the host's default x509
# CA certificate pool. This name is reserved and cannot be declared in a
# layer.
# - "default": the trust context used by consumers (services, checks, log
# targets) that don't set "trust-context" explicitly. It may be declared
# in a layer to change what it includes (via "includes"), but cannot define
# trust values of its own. If undefined, "default" only includes "system".
trust-contexts:

<trust context name>:

# (Required) Control how this trust context definition is combined with
# any other pre-existing definition with the same name in the plan.
#
# The value 'merge' will ensure that values in this layer specification
# are merged over existing definitions, whereas 'replace' will entirely
# override the existing trust context spec in the plan with the same
# name.
override: merge | replace

# (Optional) A list of other trust context names whose CA
# certificates should also be trusted as part of this trust context
# (recursively, following further "include" lists). Use the
# built-in name "system" to include the host's default CA
# certificate pool. When merging, the "include" lists from each
# layer are appended together.
include:
- <other trust context name>

# (Optional) Configures the x509 CA certificates trusted directly by
# this trust context. Not allowed for the "default" trust context,
# which may only use "include".
tls:
# (Optional) One or more PEM-encoded CA certificates. When
# merging, the certificate data from each layer is concatenated.
ca-cert: |
<PEM-encoded CA certificate(s)>

# (Optional) HTTPS (using mTLS) communication between the client and server
# requires both sides to be paired first. Pairing is currently only supported
# for HTTPS transport (not HTTP or Unix socket).
Expand Down
Loading
Loading