mirror of https://github.com/docker/docs.git
140 lines
10 KiB
HTML
140 lines
10 KiB
HTML
<p>The document provides a minimal example on setting up Docker Content Trust (DCT) in
|
||
Universal Control Plane (UCP) for use with a Continuous Integration (CI) system. It
|
||
covers setting up the necessary accounts and trust delegations to restrict only those
|
||
images built by your CI system to be deployed to your UCP managed cluster.</p>
|
||
|
||
<h2 id="set-up-ucp-accounts-and-teams">Set up UCP accounts and teams</h2>
|
||
|
||
<p>The first step is to create a user account for your CI system. For the purposes of
|
||
this document we will assume you are using Jenkins as your CI system and will therefore
|
||
name the account “jenkins”. As an admin user logged in to UCP, navigate to “User Management”
|
||
and select “Add User”. Create a user with the name “jenkins” and set a strong password.</p>
|
||
|
||
<p>Next, create a team called “CI” and add the “jenkins” user to this team. All signing
|
||
policy is team based, so if we want only a single user to be able to sign images
|
||
destined to be deployed on the cluster, we must create a team for this one user.</p>
|
||
|
||
<h2 id="set-up-the-signing-policy">Set up the signing policy</h2>
|
||
|
||
<p>While still logged in as an admin, navigate to “Admin Settings” and select the “Content Trust”
|
||
subsection. Select the checkbox to enable content trust and in the select box that appears,
|
||
select the “CI” team we have just created. Save the settings.</p>
|
||
|
||
<p>This policy will require that every image that referenced in a <code class="highlighter-rouge">docker image pull</code>,
|
||
<code class="highlighter-rouge">docker container run</code>, or <code class="highlighter-rouge">docker service create</code> must be signed by a key corresponding
|
||
to a member of the “CI” team. In this case, the only member is the “jenkins” user.</p>
|
||
|
||
<h2 id="create-keys-for-the-jenkins-user">Create keys for the Jenkins user</h2>
|
||
|
||
<p>The signing policy implementation uses the certificates issued in user client bundles
|
||
to connect a signature to a user. Using an incognito browser window (or otherwise),
|
||
log in to the “jenkins” user account you created earlier. Download a client bundle for
|
||
this user. It is also recommended to change the description associated with the public
|
||
key stored in UCP such that you can identify in the future which key is being used for
|
||
signing.</p>
|
||
|
||
<p>Each time a user retrieves a new client bundle, a new keypair is generated. It is therefore
|
||
necessary to keep track of a specific bundle that a user chooses to designate as their signing bundle.</p>
|
||
|
||
<p>Once you have decompressed the client bundle, the only two files you need for the purposes
|
||
of signing are <code class="highlighter-rouge">cert.pem</code> and <code class="highlighter-rouge">key.pem</code>. These represent the public and private parts of
|
||
the user’s signing identity respectively. We will load the <code class="highlighter-rouge">key.pem</code> file onto the Jenkins
|
||
servers, and use <code class="highlighter-rouge">cert.pem</code> to create delegations for the “jenkins” user in our
|
||
Trusted Collection.</p>
|
||
|
||
<h2 id="prepare-the-jenkins-server">Prepare the Jenkins server</h2>
|
||
|
||
<h3 id="load-keypem-on-jenkins">Load <code class="highlighter-rouge">key.pem</code> on Jenkins</h3>
|
||
|
||
<p>You will need to use the notary client to load keys onto your Jenkins server. Simply run
|
||
<code class="highlighter-rouge">notary -d /path/to/.docker/trust key import /path/to/key.pem</code>. You will be asked to set
|
||
a password to encrypt the key on disk. For automated signing, this password can be configured
|
||
into the environment under the variable name <code class="highlighter-rouge">DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE</code>. The <code class="highlighter-rouge">-d</code>
|
||
flag to the command specifies the path to the <code class="highlighter-rouge">trust</code> subdirectory within the server’s <code class="highlighter-rouge">docker</code>
|
||
configuration directory. Typically this is found at <code class="highlighter-rouge">~/.docker/trust</code>.</p>
|
||
|
||
<h3 id="enable-content-trust">Enable content trust</h3>
|
||
|
||
<p>There are two ways to enable content trust: globally, and per operation. To enabled content
|
||
trust globally, set the environment variable <code class="highlighter-rouge">DOCKER_CONTENT_TRUST=1</code>. To enable on a per
|
||
operation basis, wherever you run <code class="highlighter-rouge">docker image push</code> in your Jenkins scripts, add the flag
|
||
<code class="highlighter-rouge">--disable-content-trust=false</code>. You may wish to use this second option if you only want
|
||
to sign some images.</p>
|
||
|
||
<p>The Jenkins server is now prepared to sign images, but we need to create delegations referencing
|
||
the key to give it the necessary permissions.</p>
|
||
|
||
<h2 id="initialize-a-repository">Initialize a repository</h2>
|
||
|
||
<p>Any commands displayed in this section should <em>not</em> be run from the Jenkins server. You
|
||
will most likely want to run them from your local system.</p>
|
||
|
||
<p>If this is a new repository, create it in Docker Trusted Registry (DTR) or Docker Hub,
|
||
depending on which you use to store your images, before proceeding further.</p>
|
||
|
||
<p>We will now initialize the trust data and create the delegation that provides the Jenkins
|
||
key with permissions to sign content. The following commands initialize the trust data and
|
||
rotate snapshotting responsibilities to the server. This is necessary to ensure human involvement
|
||
is not required to publish new content.</p>
|
||
|
||
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>notary -s https://my_notary_server.com -d ~/.docker/trust init my_repository
|
||
notary -s https://my_notary_server.com -d ~/.docker/trust key rotate my_repository snapshot -r
|
||
notary -s https://my_notary_server.com -d ~/.docker/trust publish my_repository
|
||
</code></pre></div></div>
|
||
|
||
<p>The <code class="highlighter-rouge">-s</code> flag specifies the server hosting a notary service. If you are operating against
|
||
Docker Hub, this will be <code class="highlighter-rouge">https://notary.docker.io</code>. If you are operating against your own DTR
|
||
instance, this will be the same hostname you use in image names when running docker commands preceded
|
||
by the <code class="highlighter-rouge">https://</code> scheme. For example, if you would run <code class="highlighter-rouge">docker image push my_dtr:4443/me/an_image</code> the value
|
||
of the <code class="highlighter-rouge">-s</code> flag would be expected to be <code class="highlighter-rouge">https://my_dtr:4443</code>.</p>
|
||
|
||
<p>If you are using DTR, the name of the repository should be identical to the full name you use
|
||
in a <code class="highlighter-rouge">docker image push</code> command. If however you use Docker Hub, the name you use in a <code class="highlighter-rouge">docker image push</code>
|
||
must be preceded by <code class="highlighter-rouge">docker.io/</code>. i.e. if you ran <code class="highlighter-rouge">docker image push me/alpine</code>, you would
|
||
<code class="highlighter-rouge">notary init docker.io/me/alpine</code>.</p>
|
||
|
||
<p>For brevity, we will exclude the <code class="highlighter-rouge">-s</code> and <code class="highlighter-rouge">-d</code> flags from subsequent command, but be aware you
|
||
will still need to provide them for the commands to work correctly.</p>
|
||
|
||
<p>Now that the repository is initialized, we need to create the delegations for Jenkins. Docker
|
||
Content Trust treats a delegation role called <code class="highlighter-rouge">targets/releases</code> specially. It considers this
|
||
delegation to contain the canonical list of published images for the repository. It is therefore
|
||
generally desirable to add all users to this delegation with the following command:</p>
|
||
|
||
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>notary delegation add my_repository targets/releases --all-paths /path/to/cert.pem
|
||
</code></pre></div></div>
|
||
|
||
<p>This solves a number of prioritization problems that would result from needing to determine
|
||
which delegation should ultimately be trusted for a specific image. However, because it
|
||
is anticipated that any user will be able to sign the <code class="highlighter-rouge">targets/releases</code> role it is not trusted
|
||
in determining if a signing policy has been met. Therefore it is also necessary to create a
|
||
delegation specifically for Jenkins:</p>
|
||
|
||
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>notary delegation add my_repository targets/jenkins --all-paths /path/to/cert.pem
|
||
</code></pre></div></div>
|
||
|
||
<p>We will then publish both these updates (remember to add the correct <code class="highlighter-rouge">-s</code> and <code class="highlighter-rouge">-d</code> flags):</p>
|
||
|
||
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>notary publish my_repository
|
||
</code></pre></div></div>
|
||
|
||
<p>Informational (Advanced): If we included the <code class="highlighter-rouge">targets/releases</code> role in determining if a signing policy
|
||
had been met, we would run into the situation of images being opportunistically deployed when
|
||
an appropriate user signs. In the scenario we have described so far, only images signed by
|
||
the “CI” team (containing only the “jenkins” user) should be deployable. If a user “Moby” could
|
||
also sign images but was not part of the “CI” team, they might sign and publish a new <code class="highlighter-rouge">targets/releases</code>
|
||
that contained their image. UCP would refuse to deploy this image because it was not signed
|
||
by the “CI” team. However, the next time Jenkins published an image, it would update and sign
|
||
the <code class="highlighter-rouge">targets/releases</code> role as whole, enabling “Moby” to deploy their image.</p>
|
||
|
||
<h2 id="conclusion">Conclusion</h2>
|
||
|
||
<p>With the Trusted Collection initialized, and delegations created, the Jenkins server will
|
||
now use the key we imported to sign any images we push to this repository.</p>
|
||
|
||
<p>Through either the Docker CLI, or the UCP browser interface, we will find that any images
|
||
that do not meet our signing policy cannot be used. The signing policy we set up requires
|
||
that the “CI” team must have signed any image we attempt to <code class="highlighter-rouge">docker image pull</code>, <code class="highlighter-rouge">docker container run</code>,
|
||
or <code class="highlighter-rouge">docker service create</code>, and the only member of that team is the “jenkins” user. This
|
||
restricts us to only running images that were published by our Jenkins CI system.</p>
|