Skip to content
Draft
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
2 changes: 2 additions & 0 deletions docs/explanation/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ As you write your charm, follow good security practices and produce security doc
security
```

For concrete actions, see [](#secure-your-charm).

## Charm maturity

Your charm should increase in maturity and quality over time, especially if you plan for it to be publicly listed on Charmhub.
Expand Down
10 changes: 10 additions & 0 deletions docs/howto/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,16 @@ Ops enables you to trace your charm code and send data to sources such as the [C
Trace your charm <trace-your-charm>
```

## Security

To harden your charm and produce security documentation for users, work through the [Security](#security) explanation and then follow the concrete actions below.

```{toctree}
:maxdepth: 1

Secure your charm <secure-your-charm>
```

% TOC only. Nothing shown on the page.

```{toctree}
Expand Down
108 changes: 108 additions & 0 deletions docs/howto/secure-your-charm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
myst:
html_meta:
description: Concrete steps for securing a charm that uses Ops -- handling sensitive data, restricting access, hardening dependencies, and reporting vulnerabilities.
---

(secure-your-charm)=
# How to secure your charm

See first:

- [](#security)
- {external+juju:ref}`Juju | Harden your deployment <harden-your-deployment>`
- {external+pebble:ref}`Pebble | Security <security>`

This guide walks through the actions that a charm author takes to harden a charm that uses Ops, and points to the parts of the [security explanation](#security) that describe why each action matters. Work through the steps that apply to your charm; not every workload needs every step.

## Keep sensitive data out of the observable surface

Ops forwards charm logs to Juju through the `juju-log` hook command, buffers trace data locally, and lets the workload surface information back through hook commands and events. Any of these can end up in operator-visible places -- `juju debug-log`, `juju status`, trace receivers, crash reports, or the state database on disk.

To avoid leaking sensitive data:

- Do not include secrets, tokens, or other sensitive values in log messages, exception messages, or trace attributes.
- Do not pass sensitive values on the command line of processes you run from the charm; they typically end up in logs, traces, or exceptions. Pass them through the environment, a file, or standard input instead.
- Do not put sensitive values into `ops.StoredState`. The state database is not encrypted at rest (see [](#ops-charm-unit-databases)).

Ops does not mask sensitive values for you.

## Store and share sensitive data with Juju secrets

Use {external+juju:ref}`Juju secrets <secret>` for anything that a charm needs to keep confidential -- credentials, tokens, TLS material, and so on. Juju stores the value, controls which units can read it, and rotates access when relations change.

See more: {ref}`manage-secrets`

If your charm accepts a user-provided secret through configuration, define the config option with `type: secret` in `charmcraft.yaml` rather than a plain string.

## Send trace data over HTTPS

When a charm has the `tracing` extra installed and is integrated with a trace receiver, Ops sends buffered trace data over the network. This is the only outbound network connection Ops makes on the charm's behalf.

To avoid traces being intercepted, ensure that Juju users who integrate your charm with a trace receiver also integrate it with a certificate authority provider so that the traffic is TLS-protected. Document this expectation in your charm's own docs.

See more: [](#ops-cryptographic-technology)

## Add static security checks to your project

Configure the checks that your charm project runs before every merge:

- **`ruff`** for Python lint rules, including [`ruff`'s Bandit-derived security rules](https://docs.astral.sh/ruff/rules/#flake8-bandit-s). Enable the `S` rule set in `pyproject.toml`.
- **`zizmor`** for GitHub Actions workflow audits. Configure it to run on every push against the workflow files in `.github/workflows/`.
- **`codespell`** or an equivalent to catch typos in log messages and comments that would otherwise reach operators.

Run these checks in CI so they block merges rather than only running locally.

See more: [](#set-up-ci-integration)

## Keep dependencies patched

Charms pick up security fixes for their dependencies (including Ops itself) at rebuild time, so the release pipeline needs to see new versions promptly. To make that happen:

1. Restrict the version of `ops` in `pyproject.toml` in a way that allows compatible releases to be picked up on the next re-lock, for example `ops~=3.0` (or `ops~=2.23` if you support Ubuntu 20.04). See [](#ops-supported-versions) for the current list of supported releases.
2. Commit a lock file (`uv.lock`, `poetry.lock`, or equivalent) so every rebuild produces a reproducible dependency set.
3. Enable automated dependency updates -- for example, [Dependabot](https://docs.github.com/en/code-security/dependabot/dependabot-security-updates/about-dependabot-security-updates) or [Renovate](https://www.mend.io/renovate/) -- for both Python dependencies and any workflow actions your charm uses.
4. Rebuild and re-release the charm on a regular cadence so that picked-up fixes actually reach deployed units.

Keep the list of runtime dependencies small. Every dependency you add is a dependency you take on responsibility for updating.

## Restrict what the charm can do on its host

Machine charms and Kubernetes charms have different levers here:

**Machine charms.** Set an explicit `os.umask()` before creating files or directories the workload will use, so that group- and world-permissions are not inherited from whatever the calling context happened to be. Set ownership on files and directories the charm creates for the workload user.

**Kubernetes charms.** Prefer running the charm and its sidecar containers as a non-root user. Set the {external+charmcraft:ref}`charm-user key in charmcraft.yaml <charmcraft-yaml-key-charm-user>` to `non-root`, and set an explicit `uid` and `gid` on each container in `charmcraft.yaml`.

## Harden the workload

The workload is separate from Ops and typically has its own security hardening story. Follow the guidance for your workload upstream; if there is no upstream hardening guide, produce one and link to it from the charm's documentation. Existing charm-side examples to model on include:

- [Charmed PostgreSQL on Kubernetes](https://canonical-charmed-postgresql-k8s.readthedocs-hosted.com/14/explanation/security/)
- [Charmed Kubeflow](https://discourse.charmhub.io/t/security/15935)
- [Wordpress Hardening](https://developer.wordpress.org/advanced-administration/security/hardening/) (upstream)

## Verify the version deployed in a unit

To confirm that a running unit has picked up the version of Ops you expect (for example, after a security release):

```text
juju exec --unit <unit> -- bash -c '/var/lib/juju/agents/unit-*/charm/venv/bin/python -c "import ops; print(ops.__version__)"'
```

Compare the result to the [version on PyPI](https://pypi.org/project/ops/). See [](#ops-verifying-update) for background.

## Document the security posture

Include a security section in your charm's own documentation that covers, at a minimum:

- Which workload the charm manages and where its upstream hardening guide lives.
- Which relations the charm requires for a secure deployment (for example, a certificate authority provider for TLS).
- Any configuration options that materially change the security posture (for example, opening extra ports, or relaxing authentication).
- How to report vulnerabilities to you. If your charm repository has a `SECURITY.md`, link to it.

The security explanation for Ops itself lives at [](#security); mirror that structure in your charm if it helps operators reason about the deployment.

## Report vulnerabilities in Ops

If you find a vulnerability in Ops, do not open a public issue. Follow the instructions in [SECURITY.md](https://github.com/canonical/operator/blob/main/SECURITY.md) in the `canonical/operator` repository, which routes reports through the [Ubuntu Security disclosure and embargo policy](https://ubuntu.com/security/disclosure-policy).