0106cf
% CONTAINERS-POLICY.JSON 5 policy.json Man Page
fa61e0
% Miloslav Trmač
fa61e0
% September 2016
fa61e0
fa61e0
# NAME
fa61e0
containers-policy.json - syntax for the signature verification policy file
fa61e0
fa61e0
## DESCRIPTION
fa61e0
fa61e0
Signature verification policy files are used to specify policy, e.g. trusted keys,
fa61e0
applicable when deciding whether to accept an image, or individual signatures of that image, as valid.
fa61e0
fa61e0
By default, the policy is read from `$HOME/.config/containers/policy.json`, if it exists, otherwise from `/etc/containers/policy.json`;  applications performing verification may allow using a different policy instead.
fa61e0
fa61e0
## FORMAT
fa61e0
fa61e0
The signature verification policy file, usually called `policy.json`,
fa61e0
uses a JSON format.  Unlike some other JSON files, its parsing is fairly strict:
fa61e0
unrecognized, duplicated or otherwise invalid fields cause the entire file,
fa61e0
and usually the entire operation, to be rejected.
fa61e0
fa61e0
The purpose of the policy file is to define a set of *policy requirements* for a container image,
fa61e0
usually depending on its location (where it is being pulled from) or otherwise defined identity.
fa61e0
fa61e0
Policy requirements can be defined for:
fa61e0
fa61e0
- An individual *scope* in a *transport*.
fa61e0
  The *transport* values are the same as the transport prefixes when pushing/pulling images (e.g. `docker:`, `atomic:`),
fa61e0
  and *scope* values are defined by each transport; see below for more details.
fa61e0
fa61e0
  Usually, a scope can be defined to match a single image, and various prefixes of
fa61e0
  such a most specific scope define namespaces of matching images.
7d010a
fa61e0
- A default policy for a single transport, expressed using an empty string as a scope
7d010a
fa61e0
- A global default policy.
fa61e0
fa61e0
If multiple policy requirements match a given image, only the requirements from the most specific match apply,
fa61e0
the more general policy requirements definitions are ignored.
fa61e0
fa61e0
This is expressed in JSON using the top-level syntax
fa61e0
```js
fa61e0
{
fa61e0
    "default": [/* policy requirements: global default */]
fa61e0
    "transports": {
fa61e0
        transport_name: {
fa61e0
            "": [/* policy requirements: default for transport $transport_name */],
fa61e0
            scope_1: [/* policy requirements: default for $scope_1 in $transport_name */],
fa61e0
            scope_2: [/*…*/]
fa61e0
            /*…*/
fa61e0
        },
fa61e0
        transport_name_2: {/*…*/}
fa61e0
        /*…*/
fa61e0
    }
fa61e0
}
fa61e0
```
fa61e0
fa61e0
The global `default` set of policy requirements is mandatory; all of the other fields
fa61e0
(`transports` itself, any specific transport, the transport-specific default, etc.) are optional.
fa61e0
fa61e0
fa61e0
## Supported transports and their scopes
fa61e0
fa61e0
### `atomic:`
fa61e0
fa61e0
The `atomic:` transport refers to images in an Atomic Registry.
fa61e0
fa61e0
Supported scopes use the form _hostname_[`:`_port_][`/`_namespace_[`/`_imagestream_ [`:`_tag_]]],
fa61e0
i.e. either specifying a complete name of a tagged image, or prefix denoting
fa61e0
a host/namespace/image stream or a wildcarded expression for matching all
fa61e0
subdomains. For wildcarded subdomain matching, `*.example.com` is a valid case, but `example*.*.com` is not.
fa61e0
fa61e0
*Note:* The _hostname_ and _port_ refer to the container registry host and port (the one used
fa61e0
e.g. for `docker pull`), _not_ to the OpenShift API host and port.
fa61e0
fa61e0
### `dir:`
fa61e0
fa61e0
The `dir:` transport refers to images stored in local directories.
fa61e0
fa61e0
Supported scopes are paths of directories (either containing a single image or
fa61e0
subdirectories possibly containing images).
fa61e0
fa61e0
*Note:* The paths must be absolute and contain no symlinks. Paths violating these requirements may be silently ignored.
fa61e0
fa61e0
The top-level scope `"/"` is forbidden; use the transport default scope `""`,
fa61e0
for consistency with other transports.
fa61e0
fa61e0
### `docker:`
fa61e0
fa61e0
The `docker:` transport refers to images in a registry implementing the "Docker Registry HTTP API V2".
fa61e0
fa61e0
Scopes matching individual images are named Docker references *in the fully expanded form*, either
fa61e0
using a tag or digest. For example, `docker.io/library/busybox:latest` (*not* `busybox:latest`).
fa61e0
fa61e0
More general scopes are prefixes of individual-image scopes, and specify a repository (by omitting the tag or digest),
fa61e0
a repository namespace, or a registry host (by only specifying the host name)
fa61e0
or a wildcarded expression for matching all subdomains. For wildcarded subdomain
fa61e0
matching, `*.example.com` is a valid case, but `example*.*.com` is not.
fa61e0
fa61e0
### `oci:`
fa61e0
fa61e0
The `oci:` transport refers to images in directories compliant with "Open Container Image Layout Specification".
fa61e0
fa61e0
Supported scopes use the form _directory_`:`_tag_, and _directory_ referring to
fa61e0
a directory containing one or more tags, or any of the parent directories.
fa61e0
fa61e0
*Note:* See `dir:` above for semantics and restrictions on the directory paths, they apply to `oci:` equivalently.
fa61e0
fa61e0
### `tarball:`
fa61e0
fa61e0
The `tarball:` transport refers to tarred up container root filesystems.
fa61e0
fa61e0
Scopes are ignored.
fa61e0
fa61e0
## Policy Requirements
fa61e0
fa61e0
Using the mechanisms above, a set of policy requirements is looked up.  The policy requirements
fa61e0
are represented as a JSON array of individual requirement objects.  For an image to be accepted,
fa61e0
*all* of the requirements must be satisfied simultaneously.
fa61e0
fa61e0
The policy requirements can also be used to decide whether an individual signature is accepted (= is signed by a recognized key of a known author);
fa61e0
in that case some requirements may apply only to some signatures, but each signature must be accepted by *at least one* requirement object.
fa61e0
fa61e0
The following requirement objects are supported:
fa61e0
fa61e0
### `insecureAcceptAnything`
fa61e0
fa61e0
A simple requirement with the following syntax
fa61e0
fa61e0
```json
fa61e0
{"type":"insecureAcceptAnything"}
fa61e0
```
fa61e0
fa61e0
This requirement accepts any image (but note that other requirements in the array still apply).
fa61e0
fa61e0
When deciding to accept an individual signature, this requirement does not have any effect; it does *not* cause the signature to be accepted, though.
fa61e0
fa61e0
This is useful primarily for policy scopes where no signature verification is required;
fa61e0
because the array of policy requirements must not be empty, this requirement is used
fa61e0
to represent the lack of requirements explicitly.
fa61e0
fa61e0
### `reject`
fa61e0
fa61e0
A simple requirement with the following syntax:
fa61e0
fa61e0
```json
fa61e0
{"type":"reject"}
fa61e0
```
fa61e0
fa61e0
This requirement rejects every image, and every signature.
fa61e0
fa61e0
### `signedBy`
fa61e0
462880
This requirement requires an image to be signed using “simple signing” with an expected identity, or accepts a signature if it is using an expected identity and key.
fa61e0
fa61e0
```js
fa61e0
{
fa61e0
    "type":    "signedBy",
fa61e0
    "keyType": "GPGKeys", /* The only currently supported value */
fa61e0
    "keyPath": "/path/to/local/keyring/file",
462880
    "keyPaths": ["/path/to/local/keyring/file1","/path/to/local/keyring/file2"…],
fa61e0
    "keyData": "base64-encoded-keyring-data",
fa61e0
    "signedIdentity": identity_requirement
fa61e0
}
fa61e0
```
fa61e0
fa61e0
462880
Exactly one of `keyPath`, `keyPaths` and `keyData` must be present, containing a GPG keyring of one or more public keys.  Only signatures made by these keys are accepted.
fa61e0
fa61e0
The `signedIdentity` field, a JSON object, specifies what image identity the signature claims about the image.
fa61e0
One of the following alternatives are supported:
fa61e0
fa61e0
- The identity in the signature must exactly match the image identity.  Note that with this, referencing an image by digest (with a signature claiming a _repository_`:`_tag_ identity) will fail.
fa61e0
fa61e0
  ```json
fa61e0
  {"type":"matchExact"}
fa61e0
  ```
fa61e0
- If the image identity carries a tag, the identity in the signature must exactly match;
fa61e0
  if the image identity uses a digest reference, the identity in the signature must be in the same repository as the image identity (using any tag).
fa61e0
fa61e0
  (Note that with images identified using digest references, the digest from the reference is validated even before signature verification starts.)
fa61e0
fa61e0
  ```json
fa61e0
  {"type":"matchRepoDigestOrExact"}
fa61e0
  ```
fa61e0
- The identity in the signature must be in the same repository as the image identity.  This is useful e.g. to pull an image using the `:latest` tag when the image is signed with a tag specifying an exact image version.
fa61e0
fa61e0
  ```json
fa61e0
  {"type":"matchRepository"}
fa61e0
  ```
fa61e0
- The identity in the signature must exactly match a specified identity.
fa61e0
  This is useful e.g. when locally mirroring images signed using their public identity.
fa61e0
fa61e0
  ```js
fa61e0
  {
fa61e0
      "type": "exactReference",
fa61e0
      "dockerReference": docker_reference_value
fa61e0
  }
fa61e0
  ```
fa61e0
- The identity in the signature must be in the same repository as a specified identity.
fa61e0
  This combines the properties of `matchRepository` and `exactReference`.
fa61e0
fa61e0
  ```js
fa61e0
  {
fa61e0
      "type": "exactRepository",
fa61e0
      "dockerRepository": docker_repository_value
fa61e0
  }
fa61e0
  ```
fa61e0
- Prefix remapping:
fa61e0
fa61e0
  If the image identity matches the specified prefix, that prefix is replaced by the specified “signed prefix”
fa61e0
  (otherwise it is used as unchanged and no remapping takes place);
fa61e0
  matching then follows the `matchRepoDigestOrExact` semantics documented above
fa61e0
  (i.e. if the image identity carries a tag, the identity in the signature must exactly match,
fa61e0
  if it uses a digest reference, the repository must match).
fa61e0
fa61e0
  The `prefix` and `signedPrefix` values can be either host[:port] values
fa61e0
  (matching exactly the same host[:port], string),
fa61e0
  repository namespaces, or repositories (i.e. they must not contain tags/digests),
fa61e0
  and match as prefixes *of the fully expanded form*.
fa61e0
  For example, `docker.io/library/busybox` (*not* `busybox`) to specify that single repository,
fa61e0
  or `docker.io/library` (not an empty string) to specify the parent namespace of `docker.io/library/busybox`==`busybox`).
fa61e0
fa61e0
  The `prefix` value is usually the same as the scope containing the parent `signedBy` requirement.
fa61e0
fa61e0
  ```js
fa61e0
  {
fa61e0
      "type": "remapIdentity",
fa61e0
      "prefix": prefix,
fa61e0
      "signedPrefix": prefix,
fa61e0
  }
fa61e0
  ```
fa61e0
fa61e0
If the `signedIdentity` field is missing, it is treated as `matchRepoDigestOrExact`.
fa61e0
fa61e0
*Note*: `matchExact`, `matchRepoDigestOrExact` and `matchRepository` can be only used if a Docker-like image identity is
fa61e0
provided by the transport.  In particular, the `dir:` and `oci:` transports can be only
fa61e0
used with `exactReference` or `exactRepository`.
fa61e0
fa61e0
fa61e0
462880
462880
### `sigstoreSigned`
462880
462880
This requirement requires an image to be signed using a sigstore signature with an expected identity and key.
462880
462880
```js
462880
{
462880
    "type":    "sigstoreSigned",
7d010a
    "keyPath": "/path/to/local/public/key/file",
7d010a
    "keyData": "base64-encoded-public-key-data",
7d010a
    "fulcio": {
7d010a
        "caPath": "/path/to/local/CA/file",
7d010a
        "caData": "base64-encoded-CA-data",
7d010a
        "oidcIssuer": "https://expected.OIDC.issuer/",
7d010a
        "subjectEmail", "expected-signing-user@example.com",
7d010a
    },
7d010a
    "rekorPublicKeyPath": "/path/to/local/public/key/file",
7d010a
    "rekorPublicKeyData": "base64-encoded-public-key-data",
462880
    "signedIdentity": identity_requirement
462880
}
462880
```
7d010a
Exactly one of `keyPath`, `keyData` and `fulcio` must be present.
7d010a
7d010a
If `keyPath` or `keyData` is present, it contains a sigstore public key.
7d010a
Only signatures made by this key are accepted.
7d010a
7d010a
If `fulcio` is present, the signature must be based on a Fulcio-issued certificate.
7d010a
One of `caPath` and `caData` must be specified, containing the public key of the Fulcio instance.
7d010a
Both `oidcIssuer` and `subjectEmail` are mandatory,
7d010a
exactly specifying the expected identity provider,
7d010a
and the identity of the user obtaining the Fulcio certificate.
7d010a
7d010a
At most one of `rekorPublicKeyPath` and `rekorPublicKeyData` can be present;
7d010a
it is mandatory if `fulcio` is specified.
7d010a
If a Rekor public key is specified,
7d010a
the signature must have been uploaded to a Rekor server
7d010a
and the signature must contain an (offline-verifiable) “signed entry timestamp”
7d010a
proving the existence of the Rekor log record,
7d010a
signed by the provided public key.
462880
462880
The `signedIdentity` field has the same semantics as in the `signedBy` requirement described above.
462880
Note that `cosign`-created signatures only contain a repository, so only `matchRepository` and `exactRepository` can be used to accept them (and that does not protect against substitution of a signed image with an unexpected tag).
462880
462880
To use this with images hosted on image registries, the relevant registry or repository must have the `use-sigstore-attachments` option enabled in containers-registries.d(5).
462880
fa61e0
## Examples
fa61e0
fa61e0
It is *strongly* recommended to set the `default` policy to `reject`, and then
fa61e0
selectively allow individual transports and scopes as desired.
fa61e0
fa61e0
### A reasonably locked-down system
fa61e0
fa61e0
(Note that the `/*`…`*/` comments are not valid in JSON, and must not be used in real policies.)
fa61e0
fa61e0
```js
fa61e0
{
fa61e0
    "default": [{"type": "reject"}], /* Reject anything not explicitly allowed */
fa61e0
    "transports": {
fa61e0
        "docker": {
fa61e0
            /* Allow installing images from a specific repository namespace, without cryptographic verification.
fa61e0
               This namespace includes images like openshift/hello-openshift and openshift/origin. */
fa61e0
            "docker.io/openshift": [{"type": "insecureAcceptAnything"}],
fa61e0
            /* Similarly, allow installing the “official” busybox images.  Note how the fully expanded
fa61e0
               form, with the explicit /library/, must be used. */
462880
            "docker.io/library/busybox": [{"type": "insecureAcceptAnything"}],
fa61e0
            /* Allow installing images from all subdomains */
462880
            "*.temporary-project.example.com": [{"type": "insecureAcceptAnything"}],
462880
            /* A sigstore-signed repository */
462880
            "hostname:5000/myns/sigstore-signed-with-full-references": [
462880
                {
462880
                    "type": "sigstoreSigned",
462880
                    "keyPath": "/path/to/sigstore-pubkey.pub"
462880
                }
462880
            ],
7d010a
            /* A sigstore-signed repository using the community Fulcio+Rekor servers.
7d010a
7d010a
               The community servers’ public keys can be obtained from
7d010a
               https://github.com/sigstore/sigstore/tree/main/pkg/tuf/repository/targets .  */
7d010a
            "hostname:5000/myns/sigstore-signed-fulcio-rekor": [
7d010a
                {
7d010a
                    "type": "sigstoreSigned",
7d010a
                    "fulcio": {
7d010a
                        "caPath": "/path/to/fulcio_v1.crt.pem",
7d010a
                        "oidcIssuer": "https://github.com/login/oauth",
7d010a
                        "subjectEmail": "test-user@example.com"
7d010a
                    },
7d010a
                    "rekorPublicKeyPath": "/path/to/rekor.pub",
7d010a
                }
7d010a
            ],
462880
            /* A sigstore-signed repository, accepts signatures by /usr/bin/cosign */
462880
            "hostname:5000/myns/sigstore-signed-allows-malicious-tag-substitution": [
462880
                {
462880
                    "type": "sigstoreSigned",
462880
                    "keyPath": "/path/to/sigstore-pubkey.pub",
462880
                    "signedIdentity": {"type": "matchRepository"}
462880
                }
7d010a
            ],
7d010a
            /* A sigstore-signed repository using the community Fulcio+Rekor servers,
7d010a
               accepts signatures by /usr/bin/cosign.
7d010a
7d010a
               The community servers’ public keys can be obtained from
7d010a
               https://github.com/sigstore/sigstore/tree/main/pkg/tuf/repository/targets .  */
7d010a
            "hostname:5000/myns/sigstore-signed-fulcio-rekor- allows-malicious-tag-substitution": [
7d010a
                {
7d010a
                    "type": "sigstoreSigned",
7d010a
                    "fulcio": {
7d010a
                        "caPath": "/path/to/fulcio_v1.crt.pem",
7d010a
                        "oidcIssuer": "https://github.com/login/oauth",
7d010a
                        "subjectEmail": "test-user@example.com"
7d010a
                    },
7d010a
                    "rekorPublicKeyPath": "/path/to/rekor.pub",
7d010a
                    "signedIdentity": { "type": "matchRepository" }
7d010a
                }
462880
            ]
7d010a
              /* Other docker: images use the global default policy and are rejected */
fa61e0
        },
fa61e0
        "dir": {
fa61e0
            "": [{"type": "insecureAcceptAnything"}] /* Allow any images originating in local directories */
fa61e0
        },
fa61e0
        "atomic": {
fa61e0
            /* The common case: using a known key for a repository or set of repositories */
fa61e0
            "hostname:5000/myns/official": [
fa61e0
                {
fa61e0
                    "type": "signedBy",
fa61e0
                    "keyType": "GPGKeys",
fa61e0
                    "keyPath": "/path/to/official-pubkey.gpg"
fa61e0
                }
fa61e0
            ],
fa61e0
            /* A more complex example, for a repository which contains a mirror of a third-party product,
fa61e0
               which must be signed-off by local IT */
fa61e0
            "hostname:5000/vendor/product": [
fa61e0
                { /* Require the image to be signed by the original vendor, using the vendor's repository location. */
fa61e0
                    "type": "signedBy",
fa61e0
                    "keyType": "GPGKeys",
fa61e0
                    "keyPath": "/path/to/vendor-pubkey.gpg",
fa61e0
                    "signedIdentity": {
fa61e0
                        "type": "exactRepository",
fa61e0
                        "dockerRepository": "vendor-hostname/product/repository"
fa61e0
                    }
fa61e0
                },
fa61e0
                { /* Require the image to _also_ be signed by a local reviewer. */
fa61e0
                    "type": "signedBy",
fa61e0
                    "keyType": "GPGKeys",
fa61e0
                    "keyPath": "/path/to/reviewer-pubkey.gpg"
fa61e0
                }
fa61e0
            ],
fa61e0
            /* A way to mirror many repositories from a single vendor */
fa61e0
            "private-mirror:5000/vendor-mirror": [
fa61e0
                { /* Require the image to be signed by the original vendor, using the vendor's repository location.
fa61e0
                     For example, private-mirror:5000/vendor-mirror/productA/image1:latest needs to be signed as
fa61e0
                     vendor.example/productA/image1:latest . */
fa61e0
                    "type": "signedBy",
fa61e0
                    "keyType": "GPGKeys",
fa61e0
                    "keyPath": "/path/to/vendor-pubkey.gpg",
fa61e0
                    "signedIdentity": {
fa61e0
                        "type": "remapIdentity",
fa61e0
                        "prefix": "private-mirror:5000/vendor-mirror",
462880
                        "signedPrefix": "vendor.example.com"
fa61e0
                    }
fa61e0
                }
fa61e0
            ]
fa61e0
        }
fa61e0
    }
fa61e0
}
fa61e0
```
fa61e0
fa61e0
### Completely disable security, allow all images, do not trust any signatures
fa61e0
fa61e0
```json
fa61e0
{
fa61e0
    "default": [{"type": "insecureAcceptAnything"}]
fa61e0
}
fa61e0
```
fa61e0
## SEE ALSO
fa61e0
  atomic(1)
fa61e0
fa61e0
## HISTORY
fa61e0
August 2018, Rename to containers-policy.json(5) by Valentin Rothberg <vrothberg@suse.com>
fa61e0
fa61e0
September 2016, Originally compiled by Miloslav Trmač <mitr@redhat.com>