Chapter 6. Working with signed containers 6.1. Deploying your system for container signing Automation hub implements image signing to provide better security for the execution environment container images. To deploy your system so that it is ready for container signing, create a signing script. Note Installer looks for the script and key on the same server where installer is located. Procedure From a terminal, create a signing script, and pass the script path as an installer parameter. Example : #!/usr/bin/env bash # pulp_container SigningService will pass the 4 variables to the script. MANIFEST_PATH=USD1 FINGERPRINT="USDPULP_SIGNING_KEY_FINGERPRINT" IMAGE_REFERENCE="USDREFERENCE" SIGNATURE_PATH="USDSIG_PATH" # Create container signature using skopeo skopeo standalone-sign \ USDMANIFEST_PATH \ USDIMAGE_REFERENCE \ USDFINGERPRINT \ --output USDSIGNATURE_PATH # Optionally pass the passphrase to the key if password protected. # --passphrase-file /path/to/key_password.txt # Check the exit status STATUS=USD? if [ USDSTATUS -eq 0 ]; then echo {\"signature_path\": \"USDSIGNATURE_PATH\"} else exit USDSTATUS fi Review the Ansible Automation Platform installer inventory file for options for container signing that begin with automationhub_* . [all:vars] . . . automationhub_create_default_container_signing_service = True automationhub_container_signing_service_key = /absolute/path/to/key/to/sign automationhub_container_signing_service_script = /absolute/path/to/script/that/signs Once installation is complete, navigate to your automation hub. From the navigation panel, select Signature Keys . Ensure that you have a key titled container-default , or container - anyname . Note The container-default service is created by the Ansible Automation Platform installer. 6.2. Adding containers remotely to automation hub There are two ways to add containers to automation hub: Create Remotes Execution Environment Procedure To add a remote registry: In automation hub, click Execution Environments in the main menu pane. Two choices become available, Execution Environments , and Remote Registries . Click Remote Registries . Click Add Remote Registry in the main window. In the Name field, enter the name of the registry where the container resides. In the URL field, enter the URL of the registry where the container resides. In the Username field, enter the username if necessary. In the Password field, enter the password if necessary. Click Save . 6.3. Adding an execution environment Procedure Navigate to Execution Environments . Enter the name of the execution environment. Optional: Enter the upstream name. Under the Registry , select the name of the registry from the drop-down menu. Enter tags in the Add tags to include field. If the field is left blank, all of the tags will be passed. So it is important to pass repository specific tags. The remaining fields are optional: Currently included tags Add tag(s) to exclude Currently excluded tag(s) Description Groups with access Click Save . Synchronize the image. 6.4. Pushing container images from your local Use the following procedure to sign images on a local system and push those signed images to the automation hub registry. Procedure From a terminal, log into podman, or any container client currently in use. > podman pull <container-name> After the image is pulled, add tags (for example: latest, rc, beta, or version numbers, such as 1.0; 2.3, and so on): > podman tag <container-name> <server-address>/<container-name>:<tag name> Sign the image after changes have been made, and push it back up to the automation hub registry: > podman push <server-address>/<container-name>:<tag name> --tls-verify=false --sign-by <reference to the gpg key on your local> If the image is not signed, it can only be pushed with any current signature embedded. Alternatively, you can use the following script to push the image without signing it: > podman push <server-address>/<container-name>:<tag name> --tls-verify=false Once the image has been pushed, navigate to your automation hub. Click the "Refresh" icon to refresh the page to show the new execution environment. Click the name of the image to view your pushed image. In the details page, below the image name, will be displayed whether or not the image has been signed. In this case, it displays "Unsigned." The details page in automation hub indicates whether or not an image has been signed. If the details page indicates that an image is Unsigned , you can sign the image from automation hub using the following steps: Click the image name to navigate to the details page. Click the three dots in the upper right hand corner of the details page. Three options are available: Use in Controller Delete Sign Click sign from the drop-down menu. The signing service signs the image. Once the image is signed, the status changes to "signed". 6.5. Using policies with signed images Policies can be used by podman or other image clients to ensure the validity of the image by assigning specific policies to that signature. 6.5.1. Using podman to ensure an image is signed by a specific signature When ensuring a signature is signed by a specific signature(s), the signature must be on your local. Procedure In the navigation pane, select Signature Keys . Click the three dots on the right hand side of the signature that you are using. Select Download key from the drop-down menu. A new window opens. In the Name field, enter the name of the key. Click Save . 6.6. Configuring the client to verify signatures To ensure a container image pulled from the remote registry is properly signed, you must first configure the image with the proper public key in a policy file. Prerequisites The client must have sudo privileges configured to verify signatures. Procedure Open your terminal and use the following command: > sudo <name of editor> /etc/containers/policy.json The file that is displayed is similar to this: { "default": [{"type": "reject"}], "transports": { "docker": { "quay.io": [{"type": "insecureAcceptAnything"}], "docker.io": [{"type": "insecureAcceptAnything"}], "<server-address>": [ { "type": "signedBy", "keyType": "GPGKeys", "keyPath": "/tmp/containersig.txt" }] } } } This file shows that neither quay.io , or docker.io will perform the verification, because the type is insecureAcceptAnything which overrides the default type of reject . However, <server-address> will perform the verification, because the parameter type is set to "signedBy" . Note The only keyType currently supported is GPG keys. Under the <server-address> entry, modify the keyPath <1> to include the name of your key file. { "default": [{"type": "reject"}], "transports": { "docker": { "quay.io": [{"type": "insecureAcceptAnything"}], "docker.io": [{"type": "insecureAcceptAnything"}], "<server-address>": [{ "type": "signedBy", "keyType": "GPGKeys", "keyPath": "/tmp/<key file name>", "signedIdentity": { "type": "matchExact" } }] } } } Save and close the file. Pull the file using podman, or your client of choice: > podman pull <server-address>/<container-name>:<tag name> --tls-verify=false This response verifies the image has been signed with no errors. If the image is not signed, the command fails. Additional resources For more information about policy.json, see documentation for containers-policy.json .

Commands:
#!/usr/bin/env bash pulp_container SigningService will pass the next 4 variables to the script. MANIFEST_PATH=USD1 FINGERPRINT="USDPULP_SIGNING_KEY_FINGERPRINT" IMAGE_REFERENCE="USDREFERENCE" SIGNATURE_PATH="USDSIG_PATH" Create container signature using skopeo skopeo standalone-sign USDMANIFEST_PATH USDIMAGE_REFERENCE USDFINGERPRINT --output USDSIGNATURE_PATH Optionally pass the passphrase to the key if password protected. --passphrase-file /path/to/key_password.txt Check the exit status STATUS=USD? if [ USDSTATUS -eq 0 ]; then echo {\"signature_path\": \"USDSIGNATURE_PATH\"} else exit USDSTATUS fi
[all:vars] . . . automationhub_create_default_container_signing_service = True automationhub_container_signing_service_key = /absolute/path/to/key/to/sign automationhub_container_signing_service_script = /absolute/path/to/script/that/signs
> podman pull <container-name>
> podman tag <container-name> <server-address>/<container-name>:<tag name>
> podman push <server-address>/<container-name>:<tag name> --tls-verify=false --sign-by <reference to the gpg key on your local>
> podman push <server-address>/<container-name>:<tag name> --tls-verify=false
> sudo <name of editor> /etc/containers/policy.json
{ "default": [{"type": "reject"}], "transports": { "docker": { "quay.io": [{"type": "insecureAcceptAnything"}], "docker.io": [{"type": "insecureAcceptAnything"}], "<server-address>": [ { "type": "signedBy", "keyType": "GPGKeys", "keyPath": "/tmp/containersig.txt" }] } } }
{ "default": [{"type": "reject"}], "transports": { "docker": { "quay.io": [{"type": "insecureAcceptAnything"}], "docker.io": [{"type": "insecureAcceptAnything"}], "<server-address>": [{ "type": "signedBy", "keyType": "GPGKeys", "keyPath": "/tmp/<key file name>", "signedIdentity": { "type": "matchExact" } }] } } }
> podman pull <server-address>/<container-name>:<tag name> --tls-verify=false