|
|
fa61e0 |
% 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.
|
|
|
fa61e0 |
- A default policy for a single transport, expressed using an empty string as a scope
|
|
|
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 |
|
|
|
fa61e0 |
This requirement requires an image to be signed 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",
|
|
|
fa61e0 |
"keyData": "base64-encoded-keyring-data",
|
|
|
fa61e0 |
"signedIdentity": identity_requirement
|
|
|
fa61e0 |
}
|
|
|
fa61e0 |
```
|
|
|
fa61e0 |
|
|
|
fa61e0 |
|
|
|
fa61e0 |
Exactly one of `keyPath` 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 |
|
|
|
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. */
|
|
|
fa61e0 |
"docker.io/library/busybox": [{"type": "insecureAcceptAnything"}]
|
|
|
fa61e0 |
/* Allow installing images from all subdomains */
|
|
|
fa61e0 |
"*.temporary-project.example.com": [{"type": "insecureAcceptAnything"}]
|
|
|
fa61e0 |
/* 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",
|
|
|
fa61e0 |
"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>
|