Skip to content

Commit ca04db6

Browse files
authored
[ISV-1953] Fix operator index image signing with manifest list retrival (#300)
[ISV-1953] Fix operator index image signing with manifest list retrival Previously the pipeline was signing manifest digests straight from IIB builds, when it should be the manifest list digests that's retrieved from the registry, using on the IIB digests. This change fixes that.
1 parent 04d6bc5 commit ca04db6

File tree

7 files changed

+205
-86
lines changed

7 files changed

+205
-86
lines changed

ansible/roles/operator-pipeline/templates/openshift/pipelines/operator-release-pipeline.yml

Lines changed: 77 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -423,68 +423,85 @@ spec:
423423
- name: kerberos_keytab_secret_key
424424
value: "$(params.kerberos_keytab_secret_key)"
425425

426+
# use manifest list digests from IIB output and get the manifest digests from registry
427+
- name: get-manifest-digests
428+
runAfter:
429+
- publish-bundle
430+
taskRef:
431+
name: get-manifest-digests
432+
params:
433+
- name: pipeline_image
434+
value: "$(params.pipeline_image)"
435+
- name: manifest_list_digests
436+
value: "$(tasks.publish-bundle.results.manifest_list_digests)"
437+
- name: environment
438+
value: "$(params.env)"
439+
workspaces:
440+
- name: registry-credentials
441+
workspace: registry-credentials
442+
426443
# send UMB message for RADAS to sign the container image
427-
# - name: request-signature
428-
# runAfter:
429-
# - publish-bundle
430-
# taskRef:
431-
# name: request-signature
432-
# params:
433-
# - name: pipeline_image
434-
# value: "$(params.pipeline_image)"
435-
# - name: manifest_digest
436-
# value: "$(tasks.publish-bundle.results.manifest_digests)"
437-
# - name: reference
438-
# value: "$(tasks.publish-bundle.results.docker_references)"
439-
# - name: requester
440-
# value: "amisstea"
441-
# - name: sig_key_id
442-
# value: "$(tasks.set-env.results.sig_key_id)"
443-
# - name: sig_key_name
444-
# value: "$(tasks.set-env.results.sig_key_name)"
445-
# - name: umb_ssl_secret_name
446-
# value: "$(params.pyxis_ssl_secret_name)"
447-
# - name: umb_ssl_cert_secret_key
448-
# value: "$(params.pyxis_ssl_cert_secret_key)"
449-
# - name: umb_ssl_key_secret_key
450-
# value: "$(params.pyxis_ssl_key_secret_key)"
451-
# - name: umb_client_name
452-
# value: "$(tasks.set-env.results.umb_client_name)"
453-
# - name: umb_url
454-
# value: "$(tasks.set-env.results.umb_url)"
455-
# workspaces:
456-
# - name: source
457-
# workspace: repository
458-
# subPath: signing
444+
- name: request-signature
445+
runAfter:
446+
- get-manifest-digests
447+
taskRef:
448+
name: request-signature
449+
params:
450+
- name: pipeline_image
451+
value: "$(params.pipeline_image)"
452+
- name: manifest_digest
453+
value: "$(tasks.get-manifest-digests.results.manifest_digests)"
454+
- name: reference
455+
value: "$(tasks.get-manifest-digests.results.docker_references)"
456+
- name: requester
457+
value: "amisstea"
458+
- name: sig_key_id
459+
value: "$(tasks.set-env.results.sig_key_id)"
460+
- name: sig_key_name
461+
value: "$(tasks.set-env.results.sig_key_name)"
462+
- name: umb_ssl_secret_name
463+
value: "$(params.pyxis_ssl_secret_name)"
464+
- name: umb_ssl_cert_secret_key
465+
value: "$(params.pyxis_ssl_cert_secret_key)"
466+
- name: umb_ssl_key_secret_key
467+
value: "$(params.pyxis_ssl_key_secret_key)"
468+
- name: umb_client_name
469+
value: "$(tasks.set-env.results.umb_client_name)"
470+
- name: umb_url
471+
value: "$(tasks.set-env.results.umb_url)"
472+
workspaces:
473+
- name: source
474+
workspace: repository
475+
subPath: signing
459476

460-
# - name: upload-signature
461-
# runAfter:
462-
# - request-signature
463-
# taskRef:
464-
# name: upload-signature
465-
# params:
466-
# - name: pipeline_image
467-
# value: "$(params.pipeline_image)"
468-
# - name: signature_data_file
469-
# value: "$(tasks.request-signature.results.signature_data_file)"
470-
# - name: pyxis_ssl_secret_name
471-
# value: "$(params.pyxis_ssl_secret_name)"
472-
# - name: pyxis_ssl_cert_secret_key
473-
# value: "$(params.pyxis_ssl_cert_secret_key)"
474-
# - name: pyxis_ssl_key_secret_key
475-
# value: "$(params.pyxis_ssl_key_secret_key)"
476-
# - name: pyxis_url
477-
# value: "$(tasks.set-env.results.pyxis_url)"
478-
# - name: signing_pub_secret_name
479-
# value: "$(params.signing_pub_secret_name)"
480-
# - name: signing_pub_secret_key
481-
# value: "$(params.signing_pub_secret_key)"
482-
# - name: verify_signature
483-
# value: "true"
484-
# workspaces:
485-
# - name: source
486-
# workspace: repository
487-
# subPath: signing
477+
- name: upload-signature
478+
runAfter:
479+
- request-signature
480+
taskRef:
481+
name: upload-signature
482+
params:
483+
- name: pipeline_image
484+
value: "$(params.pipeline_image)"
485+
- name: signature_data_file
486+
value: "$(tasks.request-signature.results.signature_data_file)"
487+
- name: pyxis_ssl_secret_name
488+
value: "$(params.pyxis_ssl_secret_name)"
489+
- name: pyxis_ssl_cert_secret_key
490+
value: "$(params.pyxis_ssl_cert_secret_key)"
491+
- name: pyxis_ssl_key_secret_key
492+
value: "$(params.pyxis_ssl_key_secret_key)"
493+
- name: pyxis_url
494+
value: "$(tasks.set-env.results.pyxis_url)"
495+
- name: signing_pub_secret_name
496+
value: "$(params.signing_pub_secret_name)"
497+
- name: signing_pub_secret_key
498+
value: "$(params.signing_pub_secret_key)"
499+
- name: verify_signature
500+
value: "true"
501+
workspaces:
502+
- name: source
503+
workspace: repository
504+
subPath: signing
488505

489506
# Publish Vendor, Repository
490507
- name: publish-resources
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
apiVersion: tekton.dev/v1beta1
3+
kind: Task
4+
metadata:
5+
name: get-manifest-digests
6+
spec:
7+
params:
8+
- name: manifest_list_digests
9+
description: |
10+
Comma-separated list of manifest list digests generated by the IIB builds, in the
11+
format of registry.redhat.io/redhat/test-index@sha256:123
12+
- name: environment
13+
description: |
14+
Which environment the pipeline is running in. Can be one of [dev, qa, stage, prod]
15+
- name: pipeline_image
16+
results:
17+
- name: docker_references
18+
description: Comma-separated list of indices
19+
- name: manifest_digests
20+
description: Comma-separated list of manifest digests retrieved from registry
21+
workspaces:
22+
- name: registry-credentials
23+
description: Docker config for retrieving the bundle image
24+
steps:
25+
- name: podman-manifest-inspect
26+
image: "$(params.pipeline_image)"
27+
script: |
28+
set -xe
29+
30+
ENV=$(params.environment)
31+
32+
# no-op for dev or qa since IIB doesn't run
33+
# output dummy/test values for signing purposes
34+
if [[ $ENV == "dev" || $ENV == "qa" ]]; then
35+
echo -n "registry.redhat.io/redhat/test-operator-index:v4.9" | tee "$(results.docker_references.path)"
36+
echo "$(params.manifest_list_digests)" | awk -F '@' '{print $2}' | tee "$(results.manifest_list_digests.path)"
37+
echo "Getting manifest digests is a NOOP for dev and qa environments at this time."
38+
exit 0
39+
fi
40+
41+
DIGEST_LIST=$(echo $(params.manifest_list_digests) | tr "," " ")
42+
43+
if [[ $ENV != "prod" ]]; then
44+
export HTTP_PROXY="http://squid.corp.redhat.com:3128"
45+
export HTTPS_PROXY="http://squid.corp.redhat.com:3128"
46+
47+
# TODO find a better way to set registry based on env
48+
# Replace registry urls with stage urls when in preprod
49+
DIGEST_LIST=${DIGEST_LIST//registry.redhat.io/registry.stage.redhat.io}
50+
fi
51+
52+
# podman manifest inspect doesn't support any of the authfile options, this is the only way
53+
cp $(workspaces.registry-credentials.path)/.dockerconfigjson $HOME/.docker/config.json
54+
55+
DOCKER_REFERENCES=""
56+
MANIFEST_DIGESTS=""
57+
for i in $DIGEST_LIST
58+
do
59+
REFERENCE=$(echo $i | awk -F '@' '{print $1}')
60+
61+
# remove version from reference before combining with sha
62+
DIGEST=$(echo $REFERENCE | awk -F ':' '{print $1}')
63+
DIGEST+=@$(echo $i | awk -F '@' '{print $2}')
64+
65+
66+
MANIFEST_LIST=$(podman manifest inspect $DIGEST)
67+
MANIFEST_LIST=$(echo $MANIFEST_LIST | jq -r '.manifests[].digest')
68+
69+
# create comma separated index images that match each digest
70+
for j in $MANIFEST_LIST
71+
do
72+
DOCKER_REFERENCES+=$REFERENCE,
73+
done
74+
75+
# parse json output of manifest inspect into comma separated list of manifest digests for signing
76+
MANIFEST_DIGESTS+=$(echo $MANIFEST_LIST | tr " " ",")
77+
MANIFEST_DIGESTS+=","
78+
79+
done
80+
81+
echo -n $DOCKER_REFERENCES | tee "$(results.docker_references.path)"
82+
echo -n $MANIFEST_DIGESTS | tee "$(results.manifest_digests.path)"

ansible/roles/operator-pipeline/templates/openshift/tasks/publish-bundle.yml

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,8 @@ spec:
2525
description: |
2626
Which environment the pipeline is running in. Can be one of [dev, qa, stage, prod]
2727
results:
28-
- name: docker_references
29-
description: Comma-separated list of indices
30-
- name: manifest_digests
31-
description: Comma-separated list of manifest digests generated by the IIB builds
28+
- name: manifest_list_digests
29+
description: Comma-separated list of image name + manifest list digests generated by the IIB builds
3230
- name: status
3331
description: Indicates a status of publishing a bundle to an index
3432
volumes:
@@ -96,8 +94,7 @@ spec:
9694
echo "Publishing bundle to an index is a NOOP for dev and qa environments at this time."
9795
echo -n "success" | tee "$(results.status.path)"
9896
# output dummy/test values for signing purposes
99-
echo -n "registry.redhat.io/redhat/test-operator-index:v4.9" | tee "$(results.docker_references.path)"
100-
echo "$(params.bundle_pullspec)" | awk -F '@' '{print $2}' | tee "$(results.manifest_digests.path)"
97+
echo -n "$(params.bundle_pullspec)" | tee "$(results.manifest_list_digests.path)"
10198
exit 0
10299
;;
103100
esac
@@ -110,11 +107,8 @@ spec:
110107
--from-index $FROM_INDEX \
111108
--indices $INDICES \
112109
--bundle-pullspec "$(params.bundle_pullspec)" \
113-
--output manifest-digests.txt
110+
--output manifest-list-digests.txt
114111
115112
116113
echo -n "success" | tee "$(results.status.path)"
117-
# comma-separate list for signing input
118-
REFERENCES="$(echo $(params.bundle_versions) | tr -d '[ ]')"
119-
echo $REFERENCES | tee "$(results.docker_references.path)"
120-
cat manifest-digests.txt | tee "$(results.manifest_digests.path)"
114+
cat manifest-list-digests.txt | tee "$(results.manifest_list_digests.path)"

operator-pipeline-images/Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ LABEL description="Cli tools for operator certification pipeline"
44
LABEL summary="This image contains tools required for operator bundle certification pipeline."
55

66
ARG USER_UID=1000
7+
ARG PODMAN_USER_UID=1001
8+
79

810
USER root
911

@@ -31,6 +33,7 @@ RUN dnf update -y && \
3133
origin-clients \
3234
pinentry \
3335
pip \
36+
podman \
3437
python3-devel && \
3538
dnf clean all
3639

@@ -43,6 +46,11 @@ RUN curl -LO https://github.com/operator-framework/operator-registry/releases/do
4346

4447
RUN useradd -ms /bin/bash -u "${USER_UID}" user
4548

49+
RUN useradd -u "${PODMAN_USER_UID}" podman; \
50+
echo podman:10000:5000 > /etc/subuid; \
51+
echo podman:10000:5000 > /etc/subgid;
52+
53+
4654
WORKDIR /home/user
4755

4856
COPY ./operator-pipeline-images ./

operator-pipeline-images/operatorcert/entrypoints/index.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def publish_bundle(
9999
from_index: str,
100100
bundle_pullspec: str,
101101
iib_url: str,
102-
index_versions: List[str],
102+
indices: List[str],
103103
output: str,
104104
) -> None:
105105
"""
@@ -109,7 +109,7 @@ def publish_bundle(
109109
iib_url: url of IIB instance
110110
bundle_pullspec: bundle pullspec
111111
from_index: target index pullspec
112-
index_versions: list of index versions (tags)
112+
indices: list of original indices
113113
output: file name to output resulting manifest digests to
114114
Raises:
115115
Exception: Exception is raised when IIB build fails
@@ -120,6 +120,7 @@ def publish_bundle(
120120

121121
payload = {"build_requests": []}
122122

123+
index_versions = parse_indices(indices)
123124
for version in index_versions:
124125
payload["build_requests"].append(
125126
{
@@ -140,10 +141,11 @@ def publish_bundle(
140141
):
141142
raise Exception("IIB build failed")
142143
else:
143-
extract_manifest_digests(index_versions, output, response)
144+
extract_manifest_digests(indices, index_versions, output, response)
144145

145146

146147
def extract_manifest_digests(
148+
indices: List[str],
147149
index_versions: List[str],
148150
output: str,
149151
response: Dict[str, Any],
@@ -152,11 +154,13 @@ def extract_manifest_digests(
152154
LOGGER.info("Extracting manifest digests for signing...")
153155
manifest_digests = []
154156
# go through each version to ensure order is the same as the indices list
155-
for version in index_versions:
157+
for i in range(0, len(index_versions)):
158+
index = indices[i]
159+
version = index_versions[i]
156160
for build in response["items"]:
157161
if build["index_image"].endswith(version):
158162
digest = build["index_image_resolved"].split("@")[-1]
159-
manifest_digests.append(digest)
163+
manifest_digests.append(f"{index}@{digest}")
160164
with open(output, "w") as f:
161165
f.write(",".join(manifest_digests))
162166
LOGGER.info(f"Manifest digests written to output file {output}.")
@@ -202,7 +206,7 @@ def main() -> None: # pragma: no cover
202206
args.from_index,
203207
args.bundle_pullspec,
204208
args.iib_url,
205-
parse_indices(args.indices),
209+
args.indices,
206210
args.output,
207211
)
208212

operator-pipeline-images/operatorcert/entrypoints/request_signature.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,8 @@ def request_signature(args: Any) -> None:
203203
"""
204204
Format and send out a UMB message to request signing, and retry as needed.
205205
"""
206-
manifests = args.manifest_digest.split(",")
207-
references = args.reference.split(",")
206+
manifests = args.manifest_digest.strip(",").split(",")
207+
references = args.reference.strip(",").split(",")
208208

209209
if len(manifests) != len(references):
210210
LOGGER.error(

0 commit comments

Comments
 (0)