Runtime Enforcement with Docker Content Trust

Note

  • Runtime enforcement is a feature that is exclusive to MCR. It is not available in Moby or Docker CE.

  • The implementation is separate from the only run signed images feature of Mirantis Kubernetes Engine.

Docker Content Trust within the Mirantis Container Runtime prevents a user from using a container image from an untrusted source. It will also prevent a user from building a container image from a base layer from an untrusted source. Trusted sources can include Official Docker Images, found on the Docker Hub or User trusted sources, with repositories and tags signed with the commands detailed in Signing Images with Docker Content Trust.

Engine Signature Verification prevents the following:

  • $ docker container run of an unsigned or altered image.

  • $ docker pull of an unsigned or altered image.

  • $ docker build where the FROM image is not signed or is not scratch.

Note

The implicit pulls and runs performed by worker nodes for a Swarm service on $ docker service create and $ docker service update are also verified. Tag resolution of services requires that all nodes in the Swarm including managers have content trust enabled and similarly configured.

DCT does not verify that the filesystem of a running container has not been altered from what was in the image. For example, it does not prevent a container from writing to the filesystem, once the container is running. Moreover, it does not prevent the image filesystem from being altered on the disk of a Docker host. DCT will also not prevent unsigned images from being imported, loaded, or created.

Enabling DCT within the Mirantis Container Runtime

DCT is controlled by the Docker Engine configuration file, which by default is located at /etc/docker/daemon.json. For more information on the configuration file, refer to the official docker documentation, Daemon configuration file.

Note

The configuration can be set only on Linux machines.

The content-trust section is based around a mode option that instructs the engine on whether to enforce signed images, and a trust-pinning section that instructs the engine on which sources to trust.

Mode can take one of three values: disabled, permissive, or enforced.

disabled

Verification is not active and the remainder of the content-trust related configuration is ignored. This is the default value if mode is not specified.

permissive

Verification will be performed, while failures are only logged. Images that cannot be verified successfully can still be pulled and run. This configuration is intended for testing of changes related to content-trust. The results of the signature verification are displayed in the MCR daemon logs.

enforced

Content trust is enforced, thus an image that cannot be verified successfully is not pulled or run.

{
    "content-trust": {
        "mode": "<variable-type>"
    }
}

Official Docker images

Note

The IDs of all Notary root keys that are used to verify Docker Official Images are embedded in MCR.

If you want to configure MCR so that only Docker Official Images can be used, you can configure your daemon to do so using the embedded key IDs:

{
  "content-trust": {
    "trust-pinning": {
      "official-library-images": true
    },
    "mode": "enforced"
  }
}

User-signed images

There are two options for trust pinning user-signed images: Notary Canonical Root Key ID and Notary Root key ID.

Notary canonical root key ID

The Notary Canonical Root Key ID, or DCT Root Key, describes only the root key used to sign a repository, or rather its respective keys. This is the root key on the host that originally signed the repository, such as your workstation. It can be retrieved from the workstation that signed the repository through $ grep -r "root" ~/.docker/trust/private/ (assuming your trust data is at ~/.docker/trust/*). It is expected that this canonical ID has initiated multiple image repositories (mymsr/user1/image1 and mymsr/user1/image2).

  1. Retrieve root ID:

    $ grep -r "root" ~/.docker/trust/private
    /home/ubuntu/.docker/trust/private/0b6101527b2ac766702e4b40aa2391805b70e5031c04714c748f914e89014403.key:role:
    root
    
  2. Use a canonical ID. In the example that follows, the canonical ID has signed two repos, mymsr/user1/repo1 and mymsr/user1/repo2. Note that wildcards are allowed.

    {
      "content-trust": {
        "trust-pinning": {
          "root-keys": {
            "mymsr/user1/*": [
              "0b6101527b2ac766702e4b40aa2391805b70e5031c04714c748f914e89014403"
            ]
          }
        },
        "mode": "enforced"
      }
    }
    

Notary root key ID

The Notary Root key ID, or DCT Certificate ID, describes the same as the Notary Canonical Root Key ID; the ID is unique per repository. For example, mymsr/user1/image1 and mymsr/usr1/image2 will each have a unique certificate ID. A certificate ID can be retrieved through a $ docker trust inspect command, labeled as a root-key, referring back to the Notary key name. This is designed for when different users are signing their own repositories, such as when there is no central signing server. As a cert-id is more granular, it would take priority if a conflict occurs over a root ID.

  1. Retrieve root ID:

    $ docker trust inspect mymsr/user1/repo1 | jq -r '.[].AdministrativeKeys[] | select(.Name=="Root") | .Keys[].ID'
    9430d6e31e3b3e240957a1b62bbc2d436aafa33726d0fcb50addbf7e2dfa2168
    
  2. Use cert IDs by specifying two repositories with their DCT root ID.

     {
       "content-trust": {
         "trust-pinning": {
           "cert-ids": {
              "mymsr/user1/repo1": [
                "9430d6e31e3b3e240957a1b62bbc2d436aafa33726d0fcb50addbf7e2dfa2168"
              ],
              "mymsr/user2/repo1": [
                "544cf09f294860f9d5bc953ad80b386063357fd206b37b541bb2c54166f38d08"
              ]
           }
         },
         "mode": "enforced"
       }
    }
    

Using DCT in an offline environment

If your engine is unable to communicate to the registry, DCT can be enabled to trust cached signature data. This is configured through the allow-expired-cached-trust-data option.

{
  "content-trust": {
    "trust-pinning": {
      "official-library-images": true,
      "root-keys": {
        "mymsr/user1/*": [
          "0b6101527b2ac766702e4b40aa2391805b70e5031c04714c748f914e89014403"
        ]
      },
      "cert-ids": {
        "mymsr/user2/repo1": [
          "9430d6e31e3b3e240957a1b62bbc2d436aafa33726d0fcb50addbf7e2dfa2168"
        ],
      }
    },
    "mode": "enforced",
    "allow-expired-cached-trust-data": true
  }
}