Skip to content

Instantly share code, notes, and snippets.

@jkeam
Last active November 30, 2020 18:07
Show Gist options
  • Save jkeam/ad83c4a4f7c2e9bd3f176393315f3ced to your computer and use it in GitHub Desktop.
Save jkeam/ad83c4a4f7c2e9bd3f176393315f3ced to your computer and use it in GitHub Desktop.
OpenShift Enforce Image Signature

Signing Validation

Assumptions

  1. Valid image in some registry, say something like docker.io:/jonnyman9/hello-node:latest
  2. Valid signature being served by some signature server, say something like https://sigserver.com
  3. You have access to the public PGP key used to sign the image, say something like pubring.gpg

Steps

There really only is 1 important file we need, the machine-config.yaml that forces the host to verify the image against some signature when pulling it in. This one file however is made up of 3 smaller components:

  1. pubring.gpg
  2. default.yaml
  3. policy.json

Let's look at each in detail.

pubring.gpg

The PGP/GPG public key that was used to create the signature.

-----BEGIN PGP PUBLIC KEY BLOCK-----

mQENBFqHrTIBCAC38ftILiXtiSLkKrRpuilHWf+oBio1Lm11BryqwNmbm4boDBEH
zkNkqquOryPnDRVyKqSbPxDP6OdEeuoRAhFaWCijVHj+BHELhI9XXqMP8+DJjpoo
gmqF9BcQf+nKz4bb+HG5jB5FMN02O6Xgv+RER3avrRrmo4uQVpIrzuSVJ1qLgYn/
Zz0mW9o6w7cxdOqKKTBaMNrYlo4qVh9nq1oZ5JxXZ7rtDjAzBtOCsHFE7pPW43PM
ZToTw27o2PbdBCJqnUVYGAJvd//wqyELL+0lI+gkgispQUz3quwctCufGKxSg0L7
klACtLAGLa4Orz0WmicR4976z7LJLvex+95hABEBAAG0IW9wZW5zaGlmdCA8b3Bl
bnNoaWZ0QGV4YW1wbGUuY29tPokBOQQTAQIAIwUCWoetMgIbLwcLCQgHAwIBBhUI
AgkKCwQWAgMBAh4BAheAAAoJED/8ykA1UK0V+qUIALCUVSVAfkkscpl61M616XK/
QA7piViIxJzIzs0XBYI0W4NuIEpJog/1gkQjmngKowZOMVWd5eki7EEDB48T0+aR
3/2n2MSqU8JI9FXiT8LqcbNWmc6IN89RatPce9oHcjaB39/BWuMP1p47o1zmcURf
jzCt+7nKsoE4utWg3OC8G2xhiTRmGeyJfH35whBVCGV1mtXa6AYwj0f+kKW7n/NR
Roi0Q3oK5Gewn0UfX/gWNiLumwADCCFE7rBIxOmOcouQHe9bEe+2vfCkOG0mfdVP
A7c8gzI9ptC1caydQRL+CHtZ+TmcNhWXOz66KVoKAUJFj5V0+GP93MNcq+0xg+0=
=Bh2m
-----END PGP PUBLIC KEY BLOCK-----

default.yaml

This configures where the signature server is.

docker:
  docker.io:
    sigstore: https://sigserver.com

policy.json

This configures where to look for the public key.

{
    "default": [{
        "type": "insecureAcceptAnything"
    }],
    "transports": {
        "docker": {
            "docker.io": [{
                "type": "signedBy",
                "keyType": "GPGKeys",
                "keyPath": "/root/pubkey.gpg"
            }]
        },
        "docker-daemon": {
            "": [{"type":"insecureAcceptAnything"}]
        }
    }
}

machine-config.yaml

This is the real file we need. We take the files above and can use some a bit of python to create what we need to put into the file.

cat pubkey.gpg | python3 -c "import sys, urllib.parse; print(urllib.parse.quote(''.join(sys.stdin.readlines())))"
cat default.yaml | python3 -c "import sys, urllib.parse; print(urllib.parse.quote(''.join(sys.stdin.readlines())))"
cat policy.json | python3 -c "import sys, urllib.parse; print(urllib.parse.quote(''.join(sys.stdin.readlines())))"

After you cat each file, grab the output and paste it into your machine-config.yaml so that yours matches below. Don't forget to prepend with data:,.

apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
  labels:
    machineconfiguration.openshift.io/role: worker
  name: 50-worker-signatures
spec:
  config:
    ignition:
      version: 2.2.0
    storage:
      files:
      - path: /etc/containers/policy.json
        mode: 0644
        filesystem: root
        contents:
          source: data:,%7B%0A%20%20%20%20%22default%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22type%22%3A%20%22insecureAcceptAnything%22%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%5D%2C%0A%20%20%20%20%22transports%22%3A%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22docker%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22docker.io%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22type%22%3A%20%22signedBy%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22keyType%22%3A%20%22GPGKeys%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22keyPath%22%3A%20%22/root/pubkey.gpg%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22docker-daemon%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%22%3A%20%5B%7B%22type%22%3A%22insecureAcceptAnything%22%7D%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%7D%0A
      - path: /etc/containers/registries.d/default.yaml
        mode: 0644
        filesystem: root
        contents:
          source: data:,docker%3A%0A%20%20docker.io%3A%0A%20%20%20%20sigstore%3A%20http%3A//sigstore-service%3A8080%0A
      - path: /root/pubkey.gpg
        mode: 0644
        filesystem: root
        contents:
          source: data:,-----BEGIN%20PGP%20PUBLIC%20KEY%20BLOCK-----%0A%0AmQENBFqHrTIBCAC38ftILiXtiSLkKrRpuilHWf%2BoBio1Lm11BryqwNmbm4boDBEH%0AzkNkqquOryPnDRVyKqSbPxDP6OdEeuoRAhFaWCijVHj%2BBHELhI9XXqMP8%2BDJjpoo%0AgmqF9BcQf%2BnKz4bb%2BHG5jB5FMN02O6Xgv%2BRER3avrRrmo4uQVpIrzuSVJ1qLgYn/%0AZz0mW9o6w7cxdOqKKTBaMNrYlo4qVh9nq1oZ5JxXZ7rtDjAzBtOCsHFE7pPW43PM%0AZToTw27o2PbdBCJqnUVYGAJvd//wqyELL%2B0lI%2BgkgispQUz3quwctCufGKxSg0L7%0AklACtLAGLa4Orz0WmicR4976z7LJLvex%2B95hABEBAAG0IW9wZW5zaGlmdCA8b3Bl%0AbnNoaWZ0QGV4YW1wbGUuY29tPokBOQQTAQIAIwUCWoetMgIbLwcLCQgHAwIBBhUI%0AAgkKCwQWAgMBAh4BAheAAAoJED/8ykA1UK0V%2BqUIALCUVSVAfkkscpl61M616XK/%0AQA7piViIxJzIzs0XBYI0W4NuIEpJog/1gkQjmngKowZOMVWd5eki7EEDB48T0%2BaR%0A3/2n2MSqU8JI9FXiT8LqcbNWmc6IN89RatPce9oHcjaB39/BWuMP1p47o1zmcURf%0AjzCt%2B7nKsoE4utWg3OC8G2xhiTRmGeyJfH35whBVCGV1mtXa6AYwj0f%2BkKW7n/NR%0ARoi0Q3oK5Gewn0UfX/gWNiLumwADCCFE7rBIxOmOcouQHe9bEe%2B2vfCkOG0mfdVP%0AA7c8gzI9ptC1caydQRL%2BCHtZ%2BTmcNhWXOz66KVoKAUJFj5V0%2BGP93MNcq%2B0xg%2B0%3D%0A%3DBh2m%0A-----END%20PGP%20PUBLIC%20KEY%20BLOCK-----%0A

After, apply this file to your cluster, warning this takes a while.

oc apply -f ./machine-config.yaml

Testing

Try and deploy any app in OpenShift using an image from docker.io and see it succeed if your signatures are available and valid, and see it fail if not.

oc new-project example-app
oc new-app docker.io/jonnyman9/hello-node:latest
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment