diff --git a/cmd/osbuild-auth-tests/main_test.go b/cmd/osbuild-auth-tests/main_test.go index 79ca912b6..1e975b953 100644 --- a/cmd/osbuild-auth-tests/main_test.go +++ b/cmd/osbuild-auth-tests/main_test.go @@ -181,7 +181,7 @@ func TestKojiAPIAuth(t *testing.T) { subj string success bool }{ - {"valid CN 1", "/CN=worker.osbuild.org", true}, + {"valid CN 1", "/CN=client.osbuild.org", true}, {"valid CN 2", "/CN=localhost", true}, {"invalid CN", "/CN=example.com", false}, } diff --git a/osbuild-composer.spec b/osbuild-composer.spec index e375e3f28..0454acda3 100644 --- a/osbuild-composer.spec +++ b/osbuild-composer.spec @@ -188,8 +188,16 @@ install -m 0755 -vd %{buildroot}%{_d install -m 0644 -vp test/data/azure/* %{buildroot}%{_datadir}/tests/osbuild-composer/azure/ install -m 0755 -vd %{buildroot}%{_datadir}/tests/osbuild-composer/ca -install -m 0644 -vp test/data/ca/*-crt.pem %{buildroot}%{_datadir}/tests/osbuild-composer/ca/ -install -m 0600 -vp test/data/ca/*-key.pem %{buildroot}%{_datadir}/tests/osbuild-composer/ca/ +install -m 0755 -vp test/data/ca/ca-crt.pem %{buildroot}%{_datadir}/tests/osbuild-composer/ca/ +install -m 0600 -vp test/data/ca/ca-key.pem %{buildroot}%{_datadir}/tests/osbuild-composer/ca/ +install -m 0755 -vp test/data/ca/composer-crt.pem %{buildroot}%{_datadir}/tests/osbuild-composer/ca/ +install -m 0600 -vp test/data/ca/composer-key.pem %{buildroot}%{_datadir}/tests/osbuild-composer/ca/ +install -m 0755 -vp test/data/ca/worker-crt.pem %{buildroot}%{_datadir}/tests/osbuild-composer/ca/ +install -m 0600 -vp test/data/ca/worker-key.pem %{buildroot}%{_datadir}/tests/osbuild-composer/ca/ + +# Client keys are used by tests to access the composer APIs. Allow all users access. +install -m 0755 -vp test/data/ca/client-crt.pem %{buildroot}%{_datadir}/tests/osbuild-composer/ca/ +install -m 0755 -vp test/data/ca/client-key.pem %{buildroot}%{_datadir}/tests/osbuild-composer/ca/ install -m 0755 -vd %{buildroot}%{_datadir}/tests/osbuild-composer/cases install -m 0644 -vp test/data/cases/* %{buildroot}%{_datadir}/tests/osbuild-composer/cases/ diff --git a/schutzbot/Jenkinsfile b/schutzbot/Jenkinsfile index 8e1f198f0..81b8c3f84 100644 --- a/schutzbot/Jenkinsfile +++ b/schutzbot/Jenkinsfile @@ -628,6 +628,12 @@ void run_tests(test_type) { label: "Integration test: AWS", script: "/usr/libexec/tests/osbuild-composer/aws.sh" ) + + // Run the API test. + sh ( + label: "Integration test: API", + script: "/usr/libexec/tests/osbuild-composer/api.sh" + ) } } diff --git a/test/cmd/api.sh b/test/cmd/api.sh new file mode 100644 index 000000000..d5b49dab5 --- /dev/null +++ b/test/cmd/api.sh @@ -0,0 +1,165 @@ +#!/usr/bin/bash + +# +# Test osbuild-composer's main API endpoint by building a sample image and +# uploading it to AWS. +# +# This script sets `-x` and is meant to always be run like that. This is +# simpler than adding extensive error reporting, which would make this script +# considerably more complex. Also, the full trace this produces is very useful +# for the primary audience: developers of osbuild-composer looking at the log +# from a run on a remote continuous integration system. +# + +set -euxo pipefail + + +# +# Verify that this script is running in the right environment. In particular, +# it needs variables is set to access AWS. +# + +printenv AWS_REGION AWS_BUCKET AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY > /dev/null + + +# +# Create a temporary directory and ensure it gets deleted when this script +# terminates in any way. +# + +WORKDIR=$(mktemp -d) +function cleanup() { + rm -rf "$WORKDIR" +} +trap cleanup EXIT + + +# +# Install the aws client from the upstream release, because it doesn't seem to +# be available as a RHEL package. +# + +if ! hash aws; then + mkdir "$WORKDIR/aws" + pushd "$WORKDIR/aws" + curl -Ls --retry 5 --output awscliv2.zip \ + https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip + unzip awscliv2.zip > /dev/null + sudo ./aws/install > /dev/null + aws --version + popd +fi + +AWS_CMD="aws --region $AWS_REGION --output json --color on" + + +# +# Prepare a request to be sent to the composer API. +# + +REQUEST_FILE="${WORKDIR}/request.json" +ARCH=$(uname -m) +SNAPSHOT_NAME=$(uuidgen) + +case $(set +x; . /etc/os-release; echo "$ID-$VERSION_ID") in + "rhel-8.2" | "rhel-8.3") + DISTRO="rhel-8" + ;; + "fedora-31") + DISTRO="fedora-31" + ;; + "fedora-32") + DISTRO="fedora-32" + ;; + "fedora-33") + DISTRO="fedora-33" + ;; +esac + +cat > "$REQUEST_FILE" << EOF +{ + "distribution": "$DISTRO", + "image_requests": [ + { + "architecture": "$ARCH", + "image_type": "ami", + "repositories": $(jq ".\"$ARCH\"" /etc/tests/osbuild-composer/repositories/"$DISTRO".json), + "upload_requests": [ + { + "type": "aws", + "options": { + "region": "${AWS_REGION}", + "s3": { + "access_key_id": "${AWS_ACCESS_KEY_ID}", + "secret_access_key": "${AWS_SECRET_ACCESS_KEY}", + "bucket": "${AWS_BUCKET}" + }, + "ec2": { + "access_key_id": "${AWS_ACCESS_KEY_ID}", + "secret_access_key": "${AWS_SECRET_ACCESS_KEY}", + "snapshot_name": "${SNAPSHOT_NAME}" + } + } + } + ] + } + ] +} +EOF + + +# +# Send the request and wait for the job to finish. +# +# Separate `curl` and `jq` commands here, because piping them together hides +# the server's response in case of an error. +# + +OUTPUT=$(curl \ + --silent \ + --show-error \ + --cacert /etc/osbuild-composer/ca-crt.pem \ + --key /etc/osbuild-composer/client-key.pem \ + --cert /etc/osbuild-composer/client-crt.pem \ + --header 'Content-Type: application/json' \ + --request POST \ + --data @"$REQUEST_FILE" \ + https://localhost/api/composer/v1/compose) + +COMPOSE_ID=$(echo "$OUTPUT" | jq -r '.id') + +while true +do + OUTPUT=$(curl \ + --silent \ + --show-error \ + --cacert /etc/osbuild-composer/ca-crt.pem \ + --key /etc/osbuild-composer/client-key.pem \ + --cert /etc/osbuild-composer/client-crt.pem \ + https://localhost/api/composer/v1/compose/"$COMPOSE_ID") + + COMPOSE_STATUS=$(echo "$OUTPUT" | jq -r '.status') + + if [[ "$COMPOSE_STATUS" != "WAITING" && "$COMPOSE_STATUS" != "RUNNING" ]]; then + test "$COMPOSE_STATUS" = "FINISHED" + break + fi + + sleep 30 +done + + +# +# Verify the image landed in EC2, and delete it. +# + +$AWS_CMD ec2 describe-images \ + --owners self \ + --filters Name=name,Values="$SNAPSHOT_NAME" \ + > "$WORKDIR/ami.json" + +AMI_IMAGE_ID=$(jq -r '.Images[].ImageId' "$WORKDIR/ami.json") +SNAPSHOT_ID=$(jq -r '.Images[].BlockDeviceMappings[].Ebs.SnapshotId' "$WORKDIR/ami.json") + +$AWS_CMD ec2 deregister-image --image-id "$AMI_IMAGE_ID" +$AWS_CMD ec2 delete-snapshot --snapshot-id "$SNAPSHOT_ID" diff --git a/test/data/ca/client-crt.pem b/test/data/ca/client-crt.pem new file mode 100644 index 000000000..ddcb1c2f1 --- /dev/null +++ b/test/data/ca/client-crt.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICujCCAaICFC4RtelruuDmzcZBjnKYe8QCuenxMA0GCSqGSIb3DQEBCwUAMBYx +FDASBgNVBAMMC29zYnVpbGQub3JnMB4XDTIwMTAwNzIxMDUzNloXDTIwMTEwNjIx +MDUzNlowHTEbMBkGA1UEAwwSY2xpZW50Lm9zYnVpbGQub3JnMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmIpbWfEt5soZNPUJ3FXZgeJQNvA/i4aCOP0/ +FSWD36dWqYNyeZBrTdxYBSO44158Tzo4L0ETkdoBxXww3tZhN2t79r6KMdBRCHNG +hUY0C5uNmi+cfUvsh6jRg4VDmwk8DOiqVbLQ2rI36GyNfJy7MvDMjD3RbLfhFW7v +SUQQnqMRDi/uu107HrCD/O/YN288yul/2EhFTds0rFYKojybKFQxz9o1eWqW31ca +83NU5WqQUTSZ+NwnBXC/TrpNNIC6kzVgelDElL9NJU/dK++9vPkGZY5YGc44OiaG +wA21hypC2xJpL9FLiQ9jBaN/i935oKyLsQdcqsm4DOllT4TiDwIDAQABMA0GCSqG +SIb3DQEBCwUAA4IBAQBfJStg9ofmJJYWfgZHntkhCftwXlBVQKQKz7UTN9ZM+6Uc +NlAg9nmkFpK8e9u1HknL4JcdyjYdKzURHMPquvaaRAeUaXeg7LmJmO62VK0HIVHe +RtN9XdkJ3YmOC8htMBiIuObq+DMQ20mSEtMpkah812F2gno+lc60G2jYlqi9/oac +frVWGulHjufFdkEpTcLB6tleEKgH0Qj9BZdkk4fCfXTSdWKRXx2j3yRKFjUy2bG3 +jY1Vrbc9lbQhtbvDwnQwVAdNjmdw0TPSBzDiN48vliG3WVAybhMYaxWHkmPz/SS7 +Quq7hcFfV1LPJWzC0H1GTynkT8kJyjmi81XeS7/z +-----END CERTIFICATE----- diff --git a/test/data/ca/client-key.pem b/test/data/ca/client-key.pem new file mode 100644 index 000000000..5b4d4cbe9 --- /dev/null +++ b/test/data/ca/client-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAmIpbWfEt5soZNPUJ3FXZgeJQNvA/i4aCOP0/FSWD36dWqYNy +eZBrTdxYBSO44158Tzo4L0ETkdoBxXww3tZhN2t79r6KMdBRCHNGhUY0C5uNmi+c +fUvsh6jRg4VDmwk8DOiqVbLQ2rI36GyNfJy7MvDMjD3RbLfhFW7vSUQQnqMRDi/u +u107HrCD/O/YN288yul/2EhFTds0rFYKojybKFQxz9o1eWqW31ca83NU5WqQUTSZ ++NwnBXC/TrpNNIC6kzVgelDElL9NJU/dK++9vPkGZY5YGc44OiaGwA21hypC2xJp +L9FLiQ9jBaN/i935oKyLsQdcqsm4DOllT4TiDwIDAQABAoIBAAgL3E+9Qh+Xb4b0 +mgWOXb/VMUgEmkWA3eOlsCssZG1qxU6ByYsSDCb6RYZX4QvVUxdWydnsQ90As/E3 +4NgQVOZ4e/yDBoUkKPIaKpEjJ+Go3epRMp8FXz+0rwCSCgPmk81WhI2qtgujNQHE +oB3/onxIaXHIXQCwHmZkCKlDtuC3Qnh5kimTrEOeSZQYBX28UTyIScBDEH7c6FfY +P49FnW1kEsCdEb6kr2eAbjoET8jXrNYWE8Zr9B/QDmf22mPUEJB3mYmcKUOTQabR +WBq3vBL0xw47Jpt5U/CQfNrwe3AyyqN6aP33GnuVTd4I2RaCaK/YwRs0HEIzaLCI +iXPLVXkCgYEAyGVTr9wvET6zrL7whvc5nOSzxDCvb58bvr3HPPlF1onPQkXSIfRY +BtNnv+7giAzOjktO7uGQYHkiZfmSxQvKbVbZuJZ/09L3O9EPLRgqBchlceyyWWym +z2LXQBdrNYA/8OZ+qxfuPNPklTYDsnbRi2y7AXpwe8RtduIcS03Xgo0CgYEAwt27 +GMw8K9pRndK5hDePOvoEZJoiEnw20XqyGLX7+Vgh17epQygtvInWMqBEK8UsbzG+ +Im1KjODcXaBMdTmi8eO0cdDhiZ8DJRh+FU14pSs9AvVApPPOEkgPaVuYU7FxCLKE +v8TK9QjcywvGq2+UAZ9vbtEfBvCasGYGyic83gsCgYAOdQDslv3uSI+9zqiblApb +/0PYy4pciyX9RMOy6mjXaWnCZjcaq/4NwAKkHh+ksQfVzCkNosg/rX2FzdOA07Du +4m0im/js1zNu5U4q+qtNb3+iEGlteiEupPrSbN4XJgF256oLvdY6HS9IdHUf0uKb +JGT5XlPvGeSrxvQzmpIJoQKBgFi8BV2mauQBN1cpxOarMiLGBMgW09sdCw1a1Myh +2grSEh8b+AynuCP5lDtbdY+E6tX7jbw5jlAWeOJ9gzOCOmvxp5KIbptveEwlGgzz +STPVO6QkL/qtNrJmc/YjCntZ+sHeIMr+fvkTvw8K3r3kQj527pREz98mIxqeawsU +0Qe/AoGAU0XRhm/yzsKxHauksqgx+p5qDgoUpiS+KEKU1gCzYkbjI3cg4SNopaCb +DfcRwcblwuLnq7BY4n44F08n94u09s0InJdO2xmGrjY5cw7QqiI50B4FSb/KCXB7 +CnkT7DznceY2guHFT9Rm8j+1Q6EsSwy2BO4/iX6xIaNcKcw5JZk= +-----END RSA PRIVATE KEY----- diff --git a/test/data/composer/osbuild-composer.toml b/test/data/composer/osbuild-composer.toml index df44527ce..3d3b573b5 100644 --- a/test/data/composer/osbuild-composer.toml +++ b/test/data/composer/osbuild-composer.toml @@ -1,5 +1,5 @@ [koji] -allowed_domains = [ "localhost", "worker.osbuild.org" ] +allowed_domains = [ "localhost", "client.osbuild.org" ] ca = "/etc/osbuild-composer/ca-crt.pem" [koji.servers.localhost.kerberos]