tests: Test signed OCI images

This commit is contained in:
Sebastian Wick 2025-11-27 19:24:19 +01:00
parent 404aa33ce1
commit f242199eca
4 changed files with 160 additions and 1 deletions

View File

@ -295,9 +295,32 @@ export FL_GPG_ID=7B0961FD
export FL_GPG_ID2=B2314EFC
export FL_GPGARGS="--gpg-homedir=${FL_GPG_HOMEDIR} --gpg-sign=${FL_GPG_ID}"
export FL_GPGARGS2="--gpg-homedir=${FL_GPG_HOMEDIR2} --gpg-sign=${FL_GPG_ID2}"
export FL_GPGCMDARGS="--homedir ${FL_GPG_HOMEDIR} -u ${FL_GPG_ID}"
export FL_GPGCMDARGS2="--homedir ${FL_GPG_HOMEDIR2} -u ${FL_GPG_ID2}"
export FL_GPG_BASE64="mQENBFbPBvoBCADWbz5O+XzuyN+dDExK81pci+gIzBNWB+7SsN0EgoJppKgwBCX+Bd6ERe9Yz0nJbJB/tjazRp7MnnoPnh6fXnhIbHA766/Eciy4sL5X8laqDmWmROCqCe79QZH/w6vYTKsDmoLQrw9eKRP1ilCvECNGcVdhIyfTDlNrU//uy5U4h2PVUz1/Al87lvaJnrj5423m5GnX+qpEG8mmpmcw52lvXNPuC95ykylPQJjI0WnOuaTcxzRhm5eHPkqKQ+nPIS+66iw1SFdobYuye/vg/rDiyp8uyQkh7FWXnzHxz4J8ovesnrCM7pKI4VEHCnZ4/sj2v9E3l0wJlqZxLTULaV3lABEBAAG0D1hkZy1hcHAgdGVzdGluZ4kBOAQTAQIAIgUCVs8G+gIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQE4sx4HsJYf2DiAf7BQ8anU3CgYpJjuO2rT8jQPO0jGRCNaPyaeAcBx8IjFkjf8daKMPCAt6gQioEpC8OhDig86Bl5piYOB7L7JSB53mgUrADJXhgC/dG4soCt7/U4wW30MseXdlXSOqHGApblF/bIs4B30OBGReBj3DcWIqyb48GraSKlPlaCpkZFySNEAcGUCeCqbbygxCQAM8MDq9FgVRk5oVrE/nAUm6oScEBhseoB7+CaHaRTmLoe/SBs0z2AJ7alIH1Sv4X3mQXpfsAIcWf3Zu2MZydF/Vuh8vTMROwPYtOVEtGxZvEBN3h5uc88dHSk928maqsop9T6oEwM43mBKCOu1gdAOw4OLkBDQRWzwb6AQgAx/XuEaQvdI3J2YYmOE6RY0jJZXLauXH46cJR4q70mlDev/OqYKTSLlo4q06D4ozCwzTYflppDak7kmjWMN224/u1koqFOtF76LsglAeLaQmweWmX0ecbPrzFYaX30kaQAqQ9Wk0PRe0+arRzWDWfUv3qX3y1decKUrBCuEC6WvVVwooWs+zX0cUBS8CROhazTjvXFAz36mhK0u+B3WCBlK+T2tIPOjLjlYgzYARw+X7/J6B3C798r2Hw/yXqCDcKLrq7WWUB33kv3buuG2G6LUamctdD8IsTBxi+nIjAvQITFqq4cPbbXAJGaAnWGuLOddQ9e/GhCOI4JjopRnnjOwARAQABiQEfBBgBAgAJBQJWzwb6AhsMAAoJEBOLMeB7CWH9TC8H/A6oreCxeiL8DPOWN29OaQ5sEw7Dg7bnLSZLu8aREgwfCiFSv0numOABjn/G89Y5M6NiEXFZZhUa+SXOALiBLUy98O84lyp9hlP9qGbWRgBXwe5vOAJERqtoUwR5bygpAw5Nc4y3wddPC4vH7upJ8ftU/eEFtPdI0cKrrAZDFdhXFp3RxdCC6fD62wbofE0mo1Ea1iD3xqVh2t7jfWN1RhMV308htHRGkkmWcEbbvHqugwL6dWZEvQmLYi6/7tQyA1KdG4AZksBP/MBi3t2hthRqQx1v52JwdCaZNuItuEe5rWXhfvoGxPoqYZt9ZPjna6yJfcfJwPbMfjNwX2LR4p4="
export FL_GPG_BASE642="mQENBFkSyx4BCACq/8XFcF+NTpJKfoo8F6YyR8RQXww6kCV47zN78Dt7aCh43WSYLRUBRt1tW5MRT8R60pwCsGvKnFiNS2Vqe4T1IW4mDnFMZIZJXdNVwKUqVBPL/jzkIDnQ9NXtuPNH0qET6VhYnb9aykLo/MiBmx6q+4MvYd/qwiN8kstRifRIxjjZx6wsg+muY6yx9fZKxlgvhc3nsrl3oyDo7/+V+b3heYLtMCQFwlHRKz3Yf2X9H0aUSbDYcgTy6w3q94HVNCpJSqeiR+kBG175BQYKR2l7WYdaVPFf5LMEvAJh0SGnqu77X+8TYYRQiiBB5fYjGOeHfOh6uH5GAJRQymVIJwy/ABEBAAG0KkZsYXRwYWsgKFRlc3Qga2V5IDIpIDxmbGF0cGFrQGZsYXRwYWsub3JnPokBOAQTAQIAIgUCWRLLHgIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQdZ9f0LIxTvyeUQf/euAZpipXBkGWxeW4G10r1QRi2tZAWNeLpy8SB17eo9E6yB61SdH80jALborVs/plnZzKcFf+nLvjCn51FLMh6QPL3S+079WHsed//qtUWfbJ85hLevfCMTZMLktUmqwwUh238WW/gKtbUjYOqr1IZSMBoMiQtc0iOVBP7HUdhYigxTKvs/MBEGHANeQkY07ZnX9oFXElOo+EIPAHScwEOSwEVrXUVHpQODzIfjOoPUHWAZtM1yJT+iWmVHe4HtU8CyBnPyUcnTmTWKr92QmgfWkb1T7ugT5gXt/6ZlYAaZGnr9yNuSk3MMhDMOyldtJBM5Zl8eScE9KBf7pRJoxnMLkBDQRZEsseAQgAvA29IyiJpB+jUHj3MOyVyTBOmvLme+0Ndhpt/mTh+swchJUvzb0IzQS9Le5yVAvn+ppAtDCMb+bV4Xh5zrbiH0Hu0qwK4Qk+KcIKRE8ImDiUM8NFE2SZoomZSsgZ1NBWbAdEyVpkBfrt3Dd8FssMrwPF6kqo02TZr7Pxng+BEHUZT6jPCxueqyXyv2cLbQMe1H0U7klsxPmnnIYUqdwOmPxUspVEYP9oJb5y123mx0yj5JuYdZMjWbP3cRLox1RKIlFWgQqOn2yJiEoWzpqdbtb7sE3ggnbZKJED0ZxUZIakjnyMhX+GAEA8ZMZ6+HfDt1iHV8qHcYiLW5A3AQTxZwARAQABiQEfBBgBAgAJBQJZEsseAhsMAAoJEHWfX9CyMU78Ns4IAJRQ5UJ9KkeZClHm1EjYlgsAq1UJr9wgbyBFKTEkGZ/CAvVmgg+BUXcN/SPAkELbEAOJZTyv8C5cuJC49iFHOxUbRZXZ5eN2SvhZzl+5gep2uHwVLdqRIxFDTHbLWnmtHxPeU7IRA9u86q3wV1N0pD7kreNN7BWKY3/tI33hY2/XVVFy0MN5sutPn+lVK66MqAHqtode5xqqz9Z8LmS7LlqokQkAytcGd6Xqsx99NTk8kk3bnk9HWsAvDO8tRZroeseKeRNmbhGvCNUxPSB6bpYBJLvQtjA9ZVv6sNm0E+SuiXKizZkBGO5AH50pDoy0+MCGoOhwwXeY5+1kZAOzkMI="
make_oci_signature () {
DIGEST="$1"
REFERENCE="$2"
GPGARGS="${3:-${FL_GPGCMDARGS}}"
gpg ${GPGARGS} --sign - <<EOF
{
"critical": {
"type": "atomic container signature",
"image": {
"docker-manifest-digest": "$DIGEST"
},
"identity": {
"docker-reference": "$REFERENCE"
}
},
"optional": {}
}
EOF
}
make_runtime () {
REPONAME="$1"
COLLECTION_ID="$2"

View File

@ -54,6 +54,34 @@ def run_delete(args):
sys.exit(1)
def run_add_sig(args):
params = {"s": args.signature}
query = urllib.parse.urlencode(params)
conn = get_conn(args)
path = "/testing-sig/{repo}/{digest}?{query}".format(
repo=args.repo, digest=args.digest, query=query
)
conn.request("POST", path)
response = conn.getresponse()
if response.status != 200:
print(response.read(), file=sys.stderr)
print("Failed: status={}".format(response.status), file=sys.stderr)
sys.exit(1)
def run_delete_sig(args):
conn = get_conn(args)
path = "/testing-sig/{repo}/{digest}".format(
repo=args.repo, digest=args.digest
)
conn.request("DELETE", path)
response = conn.getresponse()
if response.status != 200:
print(response.read(), file=sys.stderr)
print("Failed: status={}".format(response.status), file=sys.stderr)
sys.exit(1)
parser = argparse.ArgumentParser()
parser.add_argument("--url", required=True)
parser.add_argument("--cacert")
@ -75,5 +103,16 @@ delete_parser.add_argument("repo")
delete_parser.add_argument("ref")
delete_parser.set_defaults(func=run_delete)
add_sig_parser = subparsers.add_parser("add-signature")
add_sig_parser.add_argument("repo")
add_sig_parser.add_argument("digest")
add_sig_parser.add_argument("signature")
add_sig_parser.set_defaults(func=run_add_sig)
delete_sig_parser = subparsers.add_parser("delete-signature")
delete_sig_parser.add_argument("repo")
delete_sig_parser.add_argument("digest")
delete_sig_parser.set_defaults(func=run_delete_sig)
args = parser.parse_args()
args.func(args)

View File

@ -13,6 +13,7 @@ import http.server as http_server
repositories = {}
icons = {}
signatures = {}
def get_index():
@ -116,6 +117,15 @@ class RequestHandler(http_server.BaseHTTPRequestHandler):
response = 200
response_string = icons[self.matches["filename"]]
response_content_type = "image/png"
elif self.check_route("/sig-lookaside/@ref/@sig"):
ref = self.matches["ref"]
sig = self.matches["sig"]
index = int(sig.removeprefix("signature-")) - 1
try:
response = 200
response_string = signatures[ref][index]
except (KeyError, IndexError):
response = 404
assert isinstance(response, int)
assert isinstance(response_string, bytes)
@ -212,6 +222,20 @@ class RequestHandler(http_server.BaseHTTPRequestHandler):
self.send_response(200)
self.end_headers()
return
elif self.check_route("/testing-sig/@repo_name/@digest"):
repo_name = self.matches["repo_name"]
digest = self.matches["digest"]
s = self.query["s"][0]
with open(s, "rb") as f:
signature_bytes = f.read()
digest = digest.replace(":", "=")
ref = f"{repo_name}@{digest}"
sigs = signatures.setdefault(ref, [])
sigs.append(signature_bytes)
self.send_response(200)
self.end_headers()
else:
self.send_response(404)
self.end_headers()
@ -244,6 +268,16 @@ class RequestHandler(http_server.BaseHTTPRequestHandler):
self.send_response(200)
self.end_headers()
return
elif self.check_route("/testing-sig/@repo_name/@digest"):
repo_name = self.matches["repo_name"]
digest = self.matches["digest"]
digest = digest.replace(":", "=")
ref = f"{repo_name}@{digest}"
signatures[ref] = list()
self.send_response(200)
self.end_headers()
return
else:
self.send_response(404)
self.end_headers()

View File

@ -23,7 +23,7 @@ set -euo pipefail
skip_without_bwrap
echo "1..17"
echo "1..18"
# Start the fake registry server
@ -361,3 +361,66 @@ if false && [ x${USE_SYSTEMDIR-} != xyes ]; then
else
ok "install image from registry # skip Not supported"
fi
# Test OCI signing
${FLATPAK} build-bundle --runtime --oci "${FL_GPGARGS}" repos/oci oci/platform-image org.test.Platform >&2
digest=$(jq -r '.manifests[0].digest' "$(pwd)/oci/platform-image/index.json")
make_oci_signature "${digest}" "127.0.0.1:${port}/platform:latest" > "$(pwd)/oci/platform-image-signature-1"
make_oci_signature "${digest}" "127.0.0.1:${port}/platform:latest" "${FL_GPGCMDARGS2}"> "$(pwd)/oci/platform-image-signature-2"
$client add platform latest "$(pwd)/oci/platform-image"
$client add-signature platform "${digest}" "$(pwd)/oci/platform-image-signature-1"
${FLATPAK} build-bundle --oci "${FL_GPGARGS}" repos/oci oci/app-image org.test.Hello >&2
digest=$(jq -r '.manifests[0].digest' "$(pwd)/oci/app-image/index.json")
make_oci_signature "${digest}" "127.0.0.1:${port}/hello:latest" > "$(pwd)/oci/app-image-signature-1"
make_oci_signature "${digest}" "127.0.0.1:${port}/hello:latest" "$FL_GPGCMDARGS2" > "$(pwd)/oci/app-image-signature-2"
$client add hello latest "$(pwd)/oci/app-image"
${FLATPAK} ${U} remote-add oci-registry-sig "oci+${scheme}://127.0.0.1:${port}" \
--signature-lookaside "${scheme}://127.0.0.1:${port}/sig-lookaside" \
--gpg-import=${FL_GPG_HOMEDIR}/pubring.gpg >&2
if ${FLATPAK} ${U} install -y oci-registry-sig org.test.Hello >&2; then
assert_not_reached "Should fail install due to missing signature key"
fi
$client add-signature hello "${digest}" "$(pwd)/oci/app-image-signature-2"
if ${FLATPAK} ${U} install -y oci-registry-sig org.test.Hello >&2; then
assert_not_reached "Should fail install due to wrong signature key"
fi
$client add-signature hello "${digest}" "$(pwd)/oci/app-image-signature-1"
${FLATPAK} ${U} install -y oci-registry-sig org.test.Hello >&2
${FLATPAK} build-bundle --oci "${FL_GPGARGS}" repos/oci oci/app-image org.test.Hello >&2
digest=$(jq -r '.manifests[0].digest' "$(pwd)/oci/app-image/index.json")
make_oci_signature "${digest}" "127.0.0.1:${port}/hello:latest" > "$(pwd)/oci/app-image-signature-1"
make_oci_signature "${digest}" "127.0.0.1:${port}/hello:latest" "$FL_GPGCMDARGS2" > "$(pwd)/oci/app-image-signature-2"
$client add hello latest "$(pwd)/oci/app-image"
if ${FLATPAK} update -y org.test.Hello >&2; then
assert_not_reached "Should fail install due to outdated signature key"
fi
$client add-signature hello "${digest}" "$(pwd)/oci/app-image-signature-1"
${FLATPAK} update -y org.test.Hello >&2
${FLATPAK} uninstall -y org.test.Hello >&2
${FLATPAK} ${U} remote-delete --force oci-registry-sig >&2
${FLATPAK} ${U} remote-add oci-registry-sig "oci+${scheme}://127.0.0.1:${port}" \
--signature-lookaside "${scheme}://127.0.0.1:${port}/sig-lookaside" \
--gpg-import=${FL_GPG_HOMEDIR2}/pubring.gpg >&2
if ${FLATPAK} ${U} install -y oci-registry-sig org.test.Hello >&2; then
assert_not_reached "Should fail install due to locally changed trusted key"
fi
$client add-signature hello "${digest}" "$(pwd)/oci/app-image-signature-2"
${FLATPAK} ${U} install -y oci-registry-sig org.test.Hello >&2
ok "signed images"