diff --git a/.github/actions/alerting-endpoint/action.yml b/.github/actions/alerting-endpoint/action.yml index 0bb4be13d..97e44cb07 100644 --- a/.github/actions/alerting-endpoint/action.yml +++ b/.github/actions/alerting-endpoint/action.yml @@ -13,7 +13,7 @@ runs: CONTAINER=$(docker container ls --no-trunc --format "{{json . }}" | jq ' . | select(.Image|match("alerting-endpoint"))') CONTAINER_ID=$(echo ${CONTAINER} | jq -r .ID) CONTAINER_NETWORK=$(echo ${CONTAINER} | jq -r .Networks) - SEARCH_PATH=.[0].NetworkSettings.Networks.${CONTAINER_NETWORK}.IPAddress + SEARCH_PATH=.[0].NetworkSettings.Networks."${CONTAINER_NETWORK}".IPAddress IP=$(docker container inspect ${CONTAINER_ID} | jq -r ${SEARCH_PATH}) echo IP=${IP} >> ${GITHUB_OUTPUT} shell: bash diff --git a/.github/actions/notary-server-ip/action.yml b/.github/actions/notary-server-ip/action.yml new file mode 100644 index 000000000..b6cbf32ad --- /dev/null +++ b/.github/actions/notary-server-ip/action.yml @@ -0,0 +1,19 @@ +name: get-ips-of-service-containers +description: 'Get notary server IP' +outputs: + notary_ip: + description: "IP address of the notary instance" + value: ${{ steps.get_notary_server_ip.outputs.notary_ip }} +runs: + using: "composite" + steps: + - name: Get IP + id: get_notary_server_ip + run: | + NOTARY_CONTAINER=$(docker container ls --no-trunc --format "{{json . }}" | jq ' . | select(.Image|match("notary:server"))') + NOTARY_CONTAINER_ID=$(echo ${NOTARY_CONTAINER} | jq -r .ID) + NOTARY_CONTAINER_NETWORK=$(echo ${NOTARY_CONTAINER} | jq -r .Networks) + NOTARY_SEARCH_PATH=.[0].NetworkSettings.Networks."${NOTARY_CONTAINER_NETWORK}".IPAddress + NOTARY_IP=$(docker container inspect ${NOTARY_CONTAINER_ID} | jq -r ${NOTARY_SEARCH_PATH}) + echo notary_ip=${NOTARY_IP} >> ${GITHUB_OUTPUT} + shell: bash diff --git a/.github/actions/notary-signer-ip/action.yaml b/.github/actions/notary-signer-ip/action.yaml new file mode 100644 index 000000000..95f22785e --- /dev/null +++ b/.github/actions/notary-signer-ip/action.yaml @@ -0,0 +1,19 @@ +name: get-ip-of-notary-signer-container +description: 'Get notary-signer IP' +outputs: + notary_signer_ip: + description: "IP address of the notary signer instance" + value: ${{ steps.get_signer_ip.outputs.notary_signer_ip }} +runs: + using: "composite" + steps: + - name: Get notary signer IP + id: get_signer_ip + run: | + NOTARY_CONTAINER=$(docker container ls --no-trunc --format "{{json . }}" | jq ' . | select(.Image|match("notary:signer"))') + NOTARY_CONTAINER_ID=$(echo ${NOTARY_CONTAINER} | jq -r .ID) + NOTARY_CONTAINER_NETWORK=$(echo ${NOTARY_CONTAINER} | jq -r .Networks) + NOTARY_SEARCH_PATH=.[0].NetworkSettings.Networks."${NOTARY_CONTAINER_NETWORK}".IPAddress + NOTARY_IP=$(docker container inspect ${NOTARY_CONTAINER_ID} | jq -r ${NOTARY_SEARCH_PATH}) + echo notary_signer_ip=${NOTARY_IP} >> ${GITHUB_OUTPUT} + shell: bash diff --git a/.github/actions/setup-notary/action.yaml b/.github/actions/setup-notary/action.yaml new file mode 100644 index 000000000..17017eff0 --- /dev/null +++ b/.github/actions/setup-notary/action.yaml @@ -0,0 +1,32 @@ +name: setup-notary-trust-data +description: 'Initialize trust data in running notary service container for existing test image' +outputs: + root_key: + description: "Root key of the securesystemsengineering repository in the ephemeral self-hosted notary instance" + value: ${{ steps.get_root_key.outputs.root_key }} +runs: + using: "composite" + steps: + - name: Install notary and docker client and expect + run: | + sudo apt install docker + sudo apt install notary + sudo apt install expect + shell: bash + - name: Trust root cert of notary instance + run: | + sudo cp ./tests/data/notary_service_container/server/ca.crt /usr/local/share/ca-certificates/notary_root_ca.crt + sudo update-ca-certificates + shell: bash + - name: Append notary ip to /etc/hosts + run: | + sudo -- sh -c "echo '${NOTARY_IP} notary.server' >> /etc/hosts" + shell: bash + - name: Configure notary client + run: | + ./tests/integration/notary_init.sh + docker pull docker.io/securesystemsengineering/testimage:self-hosted-notary-signed + DIGEST=$(docker images --digests | grep self-hosted-notary-signed | awk '{print $3}') + export DIGEST_WITHOUT_PREFIX=$(echo ${DIGEST#sha256:}) + ./tests/integration/notary_addhash.sh ${DIGEST_WITHOUT_PREFIX} + shell: bash diff --git a/.github/workflows/.reusable-integration-test.yml b/.github/workflows/.reusable-integration-test.yml index 5df9db09b..e07bdf040 100644 --- a/.github/workflows/.reusable-integration-test.yml +++ b/.github/workflows/.reusable-integration-test.yml @@ -75,13 +75,13 @@ jobs: with: k8s-version: v1.25 - name: Get alerting endpoint IP - id: get_ip + id: get_alerting_endpoint_ip uses: ./.github/actions/alerting-endpoint - name: Run test run: | bash tests/integration/integration-test.sh "${{ matrix.integration-test-arg }}" env: - ALERTING_ENDPOINT_IP: ${{ steps.get_ip.outputs.ip }} + ALERTING_ENDPOINT_IP: ${{ steps.get_alerting_endpoint_ip.outputs.ip }} - name: Display Connaisseur configuration if: always() run: | @@ -139,15 +139,15 @@ jobs: - uses: ./.github/actions/k8s-version-config name: Setup k8s cluster with: - k8s-version: v1.25 + k8s-version: v1.28 - name: Get alerting endpoint IP - id: get_ip + id: get_alerting_endpoint_ip uses: ./.github/actions/alerting-endpoint - name: Run test run: | bash tests/integration/integration-test.sh "${{ matrix.integration-test-arg }}" env: - ALERTING_ENDPOINT_IP: ${{ steps.get_ip.outputs.ip }} + ALERTING_ENDPOINT_IP: ${{ steps.get_alerting_endpoint_ip.outputs.ip }} - name: Display Connaisseur configuration if: always() run: | @@ -164,6 +164,81 @@ jobs: run: | kubectl logs -n connaisseur -lapp.kubernetes.io/name=connaisseur --prefix=true --tail=-1 + self-hosted-notary: + name: self-hosted-notary + runs-on: ubuntu-latest + if: | + inputs.skip_integration_tests != 'self-hosted-notary' && + inputs.skip_integration_tests != 'non-required' && + inputs.skip_integration_tests != 'all' + permissions: + packages: read + env: + IMAGE: ${{ inputs.build_image_repository }} + TAG: ${{ inputs.build_tag }} + COSIGN_PUBLIC_KEY: ${{ inputs.cosign_public_key }} + strategy: + fail-fast: false + matrix: + integration-test-arg: + [ + "self-hosted-notary" + ] + steps: + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Login with registry + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ inputs.build_registry }} + username: ${{ inputs.repo_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Install yq + run: | + sudo snap install yq + - name: Setup notary signer instance + run: | + docker run -d -p 7899:7899 -v ./tests/data/notary_service_container/signer:/etc/docker/notary-signer/ notary:signer -config=/etc/docker/notary-signer/config.json + - name: Get notary signer instance IP + id: get_notary_signer_ip + uses: ./.github/actions/notary-signer-ip + - name: Setup notary server instance + run: | + docker run -d -p 4443:4443 --add-host notary.signer:${{ steps.get_notary_signer_ip.outputs.notary_signer_ip }} -v ./tests/data/notary_service_container/server:/etc/docker/notary-server notary:server -config=/etc/docker/notary-server/config.json -logf=json + - name: Get container IPs + id: get_notary_server_ip + uses: ./.github/actions/notary-server-ip + - name: Populate notary instance with trust data + uses: ./.github/actions/setup-notary + id: setup_notary + env: + NOTARY_IP: ${{ steps.get_notary_server_ip.outputs.notary_ip }} + - uses: ./.github/actions/k8s-version-config + name: Setup k8s cluster + with: + k8s-version: v1.28 + - name: Run test + run: | + bash tests/integration/integration-test.sh "${{ matrix.integration-test-arg }}" + env: + NOTARY_IP: ${{ steps.get_notary_server_ip.outputs.notary_ip }} + - name: Display Connaisseur configuration + if: always() + run: | + echo "::group::values.yaml" + yq e '... comments=""' helm/values.yaml + echo "::endgroup::" + - name: Display k8s state if integration test failed + if: failure() + run: | + kubectl describe deployments.apps -n connaisseur -lapp.kubernetes.io/name=connaisseur + kubectl describe pods -n connaisseur -lapp.kubernetes.io/name=connaisseur + - name: Display logs if integration test failed + if: failure() + run: | + kubectl logs -n connaisseur -lapp.kubernetes.io/name=connaisseur --prefix=true --tail=-1 + + k8s-versions: name: k8s versions runs-on: ubuntu-latest @@ -186,6 +261,7 @@ jobs: "v1.25", "v1.26", "v1.27", + "v1.28", ] steps: - name: Checkout code diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index c7455a681..cb4886d8c 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -17,7 +17,7 @@ defaults: shell: bash env: - SKIP_INTEGRATION_TESTS: 'none' # 'none', 'non-required', 'all' + SKIP_INTEGRATION_TESTS: 'self-hosted-notary' # 'none', 'non-required', 'all', 'self-hosted-notary' jobs: conditionals: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d874a2ada..ae231aa74 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -12,7 +12,7 @@ defaults: shell: bash env: - SKIP_INTEGRATION_TESTS: 'non-required' # 'none', 'non-required', 'all' + SKIP_INTEGRATION_TESTS: 'none' # 'none', 'non-required', 'all', 'self-hosted-notary' jobs: conditionals: diff --git a/connaisseur/validators/notaryv1/notary.py b/connaisseur/validators/notaryv1/notary.py index 9bc557804..ebfc0bf91 100644 --- a/connaisseur/validators/notaryv1/notary.py +++ b/connaisseur/validators/notaryv1/notary.py @@ -89,24 +89,19 @@ def healthy(self): except Exception: return False - async def get_trust_data( - self, - session: aiohttp.ClientSession, - image: Image, - role: TUFRole, - token: str = None, - ): - im_repo = f"{image.repository}/" if image.repository else "" - url = ( - f"https://{self.host}/v2/{image.registry}/{im_repo}" - f"{image.name}/_trust/tuf/{str(role)}.json" - ) + # This function uses the endpoint to get the root trust data either to receive the root trust data + # directly in case the notary instance has no auth configured or to obtain the notary's auth + # endpoint (and then the auth token) first, and then use it for getting the trust data. + # To save network calls this is done in one function. + + async def get_root_trust_data_and_auth( + self, session: aiohttp.ClientSession, image: Image, token: str = None + ) -> (TrustData, str): + request_kwargs = self.__build_args(image, "root") + + if token: + request_kwargs.update({"headers": ({"Authorization": f"Bearer {token}"})}) - request_kwargs = { - "url": url, - "ssl": self.cert, - "headers": ({"Authorization": f"Bearer {token}"} if token else None), - } async with session.get(**request_kwargs) as response: status = response.status if ( @@ -119,9 +114,38 @@ async def get_trust_data( "www-authenticate" ] ) - token = await self.__get_auth_token(session, auth_url) - return await self.get_trust_data(session, image, role, token) + repo_token = await self.__get_auth_token(session, auth_url) + root_trust_data, _ = await self.get_root_trust_data_and_auth( + session, image, repo_token + ) + return root_trust_data, repo_token + elif status == 401 and not token: + msg = ( + "Unable to find authorization endpoint in" + "'www-authenticate' header for notary {notary_name}." + ) + raise NotFoundException(message=msg, notary_name=self.name) + else: + response.raise_for_status() + data = await response.text() + return TrustData(json.loads(data), "root"), token + async def get_trust_data( + self, + session: aiohttp.ClientSession, + image: Image, + role: TUFRole, + repo_token: str, + ): + request_kwargs = self.__build_args(image, str(role)) + + if repo_token: + request_kwargs.update( + {"headers": ({"Authorization": f"Bearer {repo_token}"})} + ) + + async with session.get(**request_kwargs) as response: + status = response.status if status == 404: msg = "Unable to get {tuf_role} trust data from {notary_name}." raise NotFoundException( @@ -137,10 +161,10 @@ async def get_delegation_trust_data( session: aiohttp.ClientSession, image: Image, role: TUFRole, - token: str = None, + repo_token: str, ): try: - return await self.get_trust_data(session, image, role, token) + return await self.get_trust_data(session, image, role, repo_token) except Exception as ex: if ConnaisseurLoggingWrapper.is_debug_level(): raise ex @@ -222,6 +246,7 @@ async def __get_auth_token(self, session: aiohttp.ClientSession, url: str): "ssl": self.cert, "auth": (aiohttp.BasicAuth(**self.auth) if self.auth else None), } + async with session.get(**request_kwargs) as response: if response.status >= 500: msg = "Unable to get authentication token from {auth_url}." @@ -233,7 +258,7 @@ async def __get_auth_token(self, session: aiohttp.ClientSession, url: str): try: token_key = "access_token" if self.is_acr else "token" - token = (await response.json(content_type=None))[token_key] + repo_token = (await response.json(content_type=None))[token_key] except KeyError as err: msg = ( "Unable to retrieve authentication token from {auth_url} response." @@ -246,7 +271,7 @@ async def __get_auth_token(self, session: aiohttp.ClientSession, url: str): r"^[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*$" # nosec ) - if not re.match(token_re, token): + if not re.match(token_re, repo_token): msg = "{validation_kind} has an invalid format." raise InvalidFormatException( message=msg, @@ -254,4 +279,13 @@ async def __get_auth_token(self, session: aiohttp.ClientSession, url: str): notary_name=self.name, auth_url=url, ) - return token + return repo_token + + def __build_args(self, image: Image, role: str): + im_repo = f"{image.repository}/" if image.repository else "" + url = ( + f"https://{self.host}/v2/{image.registry}/{im_repo}" + f"{image.name}/_trust/tuf/{role}.json" + ) + + return {"url": url, "ssl": self.cert} diff --git a/connaisseur/validators/notaryv1/notaryv1_validator.py b/connaisseur/validators/notaryv1/notaryv1_validator.py index a966ef69d..9a6e7528c 100644 --- a/connaisseur/validators/notaryv1/notaryv1_validator.py +++ b/connaisseur/validators/notaryv1/notaryv1_validator.py @@ -118,12 +118,20 @@ async def __process_chain_of_trust( # load all trust data t_start = dt.datetime.now() + + # even if a registry is public like e.g. docker.io, it might still + # require a token on repository level, specifying the scope (e.g. pull) + root_trust_data, repo_token = await self.notary.get_root_trust_data_and_auth( + session, image + ) trust_data_list = await asyncio.gather( *[ - self.notary.get_trust_data(session, image, TUFRole(role)) + self.notary.get_trust_data(session, image, TUFRole(role), repo_token) for role in tuf_roles + if role != "root" ] ) + trust_data_list[:0] = [root_trust_data] duration = (dt.datetime.now() - t_start).total_seconds() logging.debug("Pulled trust data for image %s in %s seconds.", image, duration) trust_data = {tuf_roles[i]: trust_data_list[i] for i in range(len(tuf_roles))} @@ -185,7 +193,7 @@ async def __process_chain_of_trust( # download only the required delegation files await self.__update_with_delegation_trust_data( - session, trust_data, req_delegations, key_store, image + session, trust_data, req_delegations, key_store, image, repo_token ) # if certain delegations are required, then only take the targets fields of the @@ -263,12 +271,12 @@ def __search_image_targets(trust_data: dict, image: Image): return None async def __update_with_delegation_trust_data( - self, session: ClientSession, trust_data, delegations, key_store, image + self, session: ClientSession, trust_data, delegations, key_store, image, token ): delegation_trust_data_list = await asyncio.gather( *[ self.notary.get_delegation_trust_data( - session, image, TUFRole(delegation) + session, image, TUFRole(delegation), token ) for delegation in delegations ] diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index bb7822eba..a64aeeed1 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -50,10 +50,9 @@ A simple starting point may be a minikube cluster with e.g. a [Docker Hub](https In case you make changes to the Connaisseur container image itself or code for that matter, you need to re-build the image and install it locally for testing. This requires a few steps: -1. In `helm/values.yaml`, set `imagePullPolicy` to `IfNotPresent`. -2. Configure your local environment to use the Kubernetes Docker daemon. In minikube, this can be done via `eval (minikube docker-env)`. -3. Build the Connaisseur container image via `make docker`. -4. Install Connaisseur as usual via `make install`. +1. Configure your local environment to use the Kubernetes Docker daemon. In minikube, this can be done via `eval $(minikube docker-env)`. +2. Build the Connaisseur container image via `make docker`. +3. Install Connaisseur via `make dev-install`. ### Test changes Tests and linting are important to ensure code quality, functionality and security. diff --git a/helm/Chart.yaml b/helm/Chart.yaml index e6aa9a80f..f3660c9fd 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: connaisseur description: Helm chart for Connaisseur - a Kubernetes admission controller to integrate container image signature verification and trust pinning into a cluster. type: application -version: 2.3.0 -appVersion: 3.3.0 +version: 2.3.1 +appVersion: 3.3.1 keywords: - container image - signature diff --git a/scripts/get_root_key.py b/scripts/get_root_key.py index 5179fba4b..f22b26261 100644 --- a/scripts/get_root_key.py +++ b/scripts/get_root_key.py @@ -15,7 +15,8 @@ async def get_pub_root_key(host: str, image: Image): notary = Notary("no", host, ["not_empty"]) async with (aiohttp.ClientSession()) as session: - root_td = await notary.get_trust_data(session, image, TUFRole("root")) + token = await notary.get_auth(session, image) + root_td = await notary.get_trust_data(session, image, TUFRole("root"), token) root_key_id = root_td.signatures[0].get("keyid") root_cert_base64 = ( diff --git a/tests/conftest.py b/tests/conftest.py index 535674f41..f0237c4ee 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -142,7 +142,16 @@ def mock_request_notary(match: re.Match, **kwargs): match.group(5), ) - if registry == "auth.io" and not kwargs.get("headers"): + if kwargs.get("headers") and kwargs.get("headers").get("Authorization"): + if registry == "empty.io": + return MockResponse({}, status_code=404) + + return MockResponse(get_td(f"{image}/{role}")) + elif registry == "empty.io": + return MockResponse({}, status_code=401) + elif registry == "notary_wo_auth.io": + return MockResponse(get_td(f"{image}/{role}")) + else: return MockResponse( {}, headers={ @@ -153,10 +162,6 @@ def mock_request_notary(match: re.Match, **kwargs): }, status_code=401, ) - if registry == "empty.io": - return MockResponse({}, status_code=404) - - return MockResponse(get_td(f"{image}/{role}")) def mock_request_kube(match: re.Match, **kwargs): @@ -399,12 +404,12 @@ def m_alerting_without_send(monkeypatch, m_safe_path_func, mocker): @pytest.fixture def count_loaded_delegations(monkeypatch): - async def get_delegation_trust_data_counted(self, image, role, token=None): + async def get_delegation_trust_data_counted(self, session, image, role, token): monkeypatch.setenv( "DELEGATION_COUNT", str(int(os.getenv("DELEGATION_COUNT")) + 1) ) try: - return await no.Notary.get_trust_data(self, image, role, token) + return await no.Notary.get_trust_data(self, session, image, role, token) except Exception as ex: if os.environ.get("LOG_LEVEL", "INFO") == "DEBUG": raise ex diff --git a/tests/data/notary/hanswurstnotary.yaml b/tests/data/notary/hanswurstnotary.yaml new file mode 100644 index 000000000..fbbff5792 --- /dev/null +++ b/tests/data/notary/hanswurstnotary.yaml @@ -0,0 +1,32 @@ +name: harbor +host: notary.hans.io +cert: | + -----BEGIN CERTIFICATE----- + MIIDEzCCAfugAwIBAgIQEHy1Je1mbrt0RaLDjDajszANBgkqhkiG9w0BAQsFADAU + MRIwEAYDVQQDEwloYXJib3ItY2EwHhcNMjEwMTI2MTQyNTE5WhcNMjIwMTI2MTQy + NTE5WjAUMRIwEAYDVQQDEwloYXJib3ItY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IB + DwAwggEKAoIBAQCfy2A79g4KGx1BN8LgNwF34pSJaKqzV9hsanNKi5iU6Sn2Qrjx + a++HlCYK8TAZ54cacP1T+d+eqlDwgMlbkXsjSFiRr3Z+KxtrrFbM9yNrNzyUiDVW + czUQM+PFEETk2uwp7GSHFFBXeo+6p/cI2vqSqxpkVVojKmX6vEdEdPh9mwBt9nuk + MNfaJxzzjpAPdH9TkWME+J+GpxuLhtRnE0PStC6ioYI4FeH5MCwLKv7ZVyxWYDpY + f5qG2H00rGNOHsq9jidyLbp90jboMbVHMO6ragM6sqrjPF/cLE8oifuguCR6dbVk + yQuIacfG/vglnp5juvFLDmf0ZVBytazWMUQzAgMBAAGjYTBfMA4GA1UdDwEB/wQE + AwICpDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUw + AwEB/zAdBgNVHQ4EFgQUwtWtGfG+NU6ZcqhJI+lKRHOW/qQwDQYJKoZIhvcNAQEL + BQADggEBABiBHCuadw+SlmQHuK9egZSzIjvaLdKcTWdYwICtzuymZyyAWxWGeY8O + ZRZ9ZvsVX8jgTsSlFe+nV/+3MokYCvCaaDmyre7zZmRsq65ILSrwJMWjSqyvt8/X + s78uvGgi8/ooP7eldlduOA3AdV81Ty9GeCWWqEVIjEgfVQhpYquNTyOcUp8Tuks6 + 5OkY1pS58NRkoIM6/jfGtgbzsvvHooZwslmq8eaT+MucuzuGpY2GelEE5pI9Q7tf + hMC42zeU+yxxy3vukMa4xX2BGzyjAg+qaDh6YwWui80r2/BlYXvSsSl3dIgtVwL4 + DSo1s+3uJ4evVKDRf3vLwKLTtiYfd20= + -----END CERTIFICATE----- +trustRoots: + - name: library + key: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEH+G0yM9CQ8KjN2cF8iHpiTA9Q69q + 3brrzLkY1kjmRNOs0c2sx2nm8j2hFZRbyaVsd52Mkw0k5WrX+9vBfbjtdQ== + -----END PUBLIC KEY----- +auth: + username: hans + password: wurst diff --git a/tests/data/notary_service_container/config/client_config.json b/tests/data/notary_service_container/config/client_config.json new file mode 100644 index 000000000..b98c43a2b --- /dev/null +++ b/tests/data/notary_service_container/config/client_config.json @@ -0,0 +1,6 @@ +{ + "remote_server": { + "url": "https://notary.server:4443", + "root_ca": "../server/ca.crt" + } +} diff --git a/tests/data/notary_service_container/server/ca.crt b/tests/data/notary_service_container/server/ca.crt new file mode 100644 index 000000000..43109fd65 --- /dev/null +++ b/tests/data/notary_service_container/server/ca.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDEzCCAfugAwIBAgIUDDvwp5mG7ckbcvJ0jg9he/rSGzAwDQYJKoZIhvcNAQEL +BQAwGTEXMBUGA1UEAwwOTm90YXJ5IFJvb3QgQ0EwHhcNMjMxMTIyMjIwOTM5WhcN +MjQxMTIxMjIwOTM5WjAZMRcwFQYDVQQDDA5Ob3RhcnkgUm9vdCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAO6IjUiNqWDrwO1Yn/Yfc5hSUdH3ZZ1i +SzbsYL4ZL3ZN/rXv8rS3JBeNpbcRs85lYIjC8prCh9tJ6FjnMqUVI5pGz9XZPLNO +hQA4eW4Z5lHDd1oWCT1Y66zwUYSD59xUzGWCZ5OhZlKanu8OfdGDyoPJxKYmhZQz +Vnnh1chZC5eBBzObufWO47aMg4rX7ZLCER/HxLHfSnCVMVwOa3KBHDK3yWy+y86y +7/0bl5iW/bR8OZ/cjH4eEN9QdbAYBsavEsJ6wxBDgdweUH+7fQfORn5/IJtqwGha +FAyfeTwRI8/8tA4J1U08/p8oIopbzcGTg1LjPHQ8s8RTtknzg+jTrWkCAwEAAaNT +MFEwHQYDVR0OBBYEFKkx59QcInNCTOluvzHt3frciFqtMB8GA1UdIwQYMBaAFKkx +59QcInNCTOluvzHt3frciFqtMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAKyPiDT0PXTHzpC7sLq7s2pUe3ezqg51m4NTgynO3u+vYftTCMVDIV0P +H7ZJNyHHYvglaLjRnsWdjGs4tcqjIYCRL8Tnc/CVorI0QGGwDDlvWXNXleAxjV3t +t5hy7t5JpnNgF2GYNnSeuYG21rDDcP9EQZKv1a2KaKcgFRtzktlegRX0kGjDxbYe +0jo5j4oJZ944W79GqcQLeRpOU8PnbSaBtRPSeX1VhzpkR3pQy5FgBikQkvRP/dJw +H5Ck1P7fRzDnlv4cypSk6v9PWSQMrSYlyp1a40b9U1bIF2cQ+HVDwUg+56OPAey6 +wBELObfROXagGdjRP+OQJUJgFHS3Uss= +-----END CERTIFICATE----- diff --git a/tests/data/notary_service_container/server/config.json b/tests/data/notary_service_container/server/config.json new file mode 100644 index 000000000..fc2820d15 --- /dev/null +++ b/tests/data/notary_service_container/server/config.json @@ -0,0 +1,19 @@ +{ + "server": { + "http_addr": ":4443", + "tls_key_file": "./notary-server.key", + "tls_cert_file": "./notary-server.crt" + }, + "trust_service": { + "type": "remote", + "hostname": "notary.signer", + "port": "7899", + "tls_ca_file": "./ca.crt", + "key_algorithm": "ecdsa", + "tls_client_cert": "./notary-server.crt", + "tls_client_key": "./notary-server.key" + }, + "storage": { + "backend": "memory" + } +} diff --git a/tests/data/notary_service_container/server/notary-server.crt b/tests/data/notary_service_container/server/notary-server.crt new file mode 100644 index 000000000..fe41f71e8 --- /dev/null +++ b/tests/data/notary_service_container/server/notary-server.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC6jCCAdKgAwIBAgIURG6fK3XvIY0hybWB9UUl016hmOEwDQYJKoZIhvcNAQEL +BQAwGTEXMBUGA1UEAwwOTm90YXJ5IFJvb3QgQ0EwHhcNMjMxMTIyMjIxMzE0WhcN +MjQxMTIxMjIxMzE0WjAYMRYwFAYDVQQDDA1ub3Rhcnkuc2VydmVyMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU7Oxo0F4s4m8okLxbjM7oYuSlbSf8JC +CUge9PRMf9fV/xGKz4mpphy5Gb0c84RV774HL+Qv+3lvdMN41RdLlopmoIsK2sJY +SZB1j44ywQGFXmYRJf2hpeNXG8nC52iWZaFgg4HZFlRvjD8dJc7CTv/XX2AdYS5q +16Y1AAfg7eAK4FOyScBaHGb6SQOuEdjPBDckYH0ZR6bz/Ihj3PR2oSg5IYQEth20 +zSMvI8A2N9b780+9C9WCBq63Wf4Qlblm2s4UyinsOyv7wpTo+ZzyCQrcPQ7aynPE +JwDing57uM6WLy6s7kMQzORGsvyOUqz1RLHQAUhqt994P5Vn7u3JPQIDAQABoysw +KTAnBgNVHREEIDAegg1ub3Rhcnktc2VydmVygg1ub3Rhcnkuc2VydmVyMA0GCSqG +SIb3DQEBCwUAA4IBAQBXHYBY8dfZ8q9DG6TDFWXAT7Lll7akRWo0zDCLoEHAnIJ4 +tVd5MmEySDCd9Z8/YPZPxX+3p1q5fbquDdD6dlNip98Db1h+n/JreqZBpa4Jbzef +/dhKhnxD1Mf9FXsLse1lb0YqJGNUXIIBWUGPInIKPiL0SjCI7wuvKPVtMLUoa86Q +rYsMWmiWsGXMySK89Yf+egrTLT0c7Jv+A5dfoYJlouhGxlUksrgJj38SBgNHdqiY +IYqrcurSvaEpWHRQTdbBsYQXrzhaIPTGes3n3kY5ZJG1K1+Z5RnFVkQJizAyGXwq +RLnRS3Ajc8Ntq7FdpIuzZNxJwWVFhTs5jJpDW4l6 +-----END CERTIFICATE----- diff --git a/tests/data/notary_service_container/server/notary-server.key b/tests/data/notary_service_container/server/notary-server.key new file mode 100644 index 000000000..4b36a0ad0 --- /dev/null +++ b/tests/data/notary_service_container/server/notary-server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCtTs7GjQXiziby +iQvFuMzuhi5KVtJ/wkIJSB709Ex/19X/EYrPiammHLkZvRzzhFXvvgcv5C/7eW90 +w3jVF0uWimagiwrawlhJkHWPjjLBAYVeZhEl/aGl41cbycLnaJZloWCDgdkWVG+M +Px0lzsJO/9dfYB1hLmrXpjUAB+Dt4ArgU7JJwFocZvpJA64R2M8ENyRgfRlHpvP8 +iGPc9HahKDkhhAS2HbTNIy8jwDY31vvzT70L1YIGrrdZ/hCVuWbazhTKKew7K/vC +lOj5nPIJCtw9DtrKc8QnAOKeDnu4zpYvLqzuQxDM5Eay/I5SrPVEsdABSGq333g/ +lWfu7ck9AgMBAAECggEAFuEskzHS8RpeMe69yyCWjXbRbacAqzUuGqOW0qfd4ZLR +AZeDR9rYtsFb/nXk+JEj9z6THFN3si9Z84RK03b716f13IP+rtqxZSTZIFaZhLl6 +rfezk9xkFs5olZaRUJOSJ2CiqTnfOv9yqoU6h98/78X+3OyHdGj4WffrT1G69CxD +8Z87HRXaSc3JTOSLqoRBpQYX7CJF91zfYfnwoj0O0tp6OF+ip8Q59ia/uaXNvPrA +6Jpc/0BW06DC4O/r4qEtF4VyRMHqTB106ZHj8gEhfrdIzi/9+uYEmA0jav6ywpTF +1P3wz0UXdy2LxJ8eMF+b44uSiVXUmKsKHAtbaNy3UQKBgQDlcpsU9jXlOh9DT8jS +KQLxHeBW/8st63Mc4nDrM/1ycOe1i1GpbxQsigWQvNXjUPs4E0N4vfdHq8ERPjkJ +HAaKu7eSooE5msmiFUM0f+RtqsDOQjVwrPMRii59VYrl2H6j9MPAVxzZ55RN6JDL +cBAfFTe2pLg14gPOOybWi9oc9wKBgQDBXQ7BLaOQ8an5pWFtChhghlyS2Ek7cACC +/HbJm+VZpI49rGRy90y0z7MLZPn2+gRsT+oD3yrcJXHGuVWiNQWY/ncmmhob5s/q +c/PsdciCeqvUTEDZ39ccenKpsCEuZZfGYovkRRLXfo0S4gGh/3AXTiWIWdSO1DY9 +wBkh5BRCawKBgQCfe56h3lUt5M1wKxfKRGlgEUUrE5c1bs/PhvG3+qYzEplsDc+Q +Nr5OCw29QhRlcZd8rZ8bYOdtcMu84YQhedJuQfZiPQQXdyipuZ/B8RdkxuhHNawR +ipVVXrfbtEbcZjP4YJxjp+lM6POjh4CFd3otMMFN+YZ1JYlBosnnHMRZlwKBgH2Y +PCUtx7g1v/nvecChdgP8QdT/t1FsBmkOIvoA0I/RWrKkbvpdtu0am2kjRVkuPAE+ +RvoM0oH7sFMrvnuFhQVDA5GHNr44xYO7nQxR1NMrasCSZu0df1N4FVIynNrOEi9B +gyvZ0cs239sMAZN/nwcEM0zFTFMZc8HYLHre8mnRAoGAc32wPM3dZHw1PtgmYf2l +WU3CCCm0PdTkQrC4lFyCATWzEwA9MOhvxlCctNnjyj+nRn+SLCos1Ygy+gSnIAnj +gfKJpavtG0KITS7rtCV8DukLDYXApksL/WpEVerA2F2spOtv+MhCIxJ+dSvO5q3S +FRd6yZrHlagfJwXN/ZuXxq4= +-----END PRIVATE KEY----- diff --git a/tests/data/notary_service_container/signer/ca.crt b/tests/data/notary_service_container/signer/ca.crt new file mode 100644 index 000000000..43109fd65 --- /dev/null +++ b/tests/data/notary_service_container/signer/ca.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDEzCCAfugAwIBAgIUDDvwp5mG7ckbcvJ0jg9he/rSGzAwDQYJKoZIhvcNAQEL +BQAwGTEXMBUGA1UEAwwOTm90YXJ5IFJvb3QgQ0EwHhcNMjMxMTIyMjIwOTM5WhcN +MjQxMTIxMjIwOTM5WjAZMRcwFQYDVQQDDA5Ob3RhcnkgUm9vdCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAO6IjUiNqWDrwO1Yn/Yfc5hSUdH3ZZ1i +SzbsYL4ZL3ZN/rXv8rS3JBeNpbcRs85lYIjC8prCh9tJ6FjnMqUVI5pGz9XZPLNO +hQA4eW4Z5lHDd1oWCT1Y66zwUYSD59xUzGWCZ5OhZlKanu8OfdGDyoPJxKYmhZQz +Vnnh1chZC5eBBzObufWO47aMg4rX7ZLCER/HxLHfSnCVMVwOa3KBHDK3yWy+y86y +7/0bl5iW/bR8OZ/cjH4eEN9QdbAYBsavEsJ6wxBDgdweUH+7fQfORn5/IJtqwGha +FAyfeTwRI8/8tA4J1U08/p8oIopbzcGTg1LjPHQ8s8RTtknzg+jTrWkCAwEAAaNT +MFEwHQYDVR0OBBYEFKkx59QcInNCTOluvzHt3frciFqtMB8GA1UdIwQYMBaAFKkx +59QcInNCTOluvzHt3frciFqtMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAKyPiDT0PXTHzpC7sLq7s2pUe3ezqg51m4NTgynO3u+vYftTCMVDIV0P +H7ZJNyHHYvglaLjRnsWdjGs4tcqjIYCRL8Tnc/CVorI0QGGwDDlvWXNXleAxjV3t +t5hy7t5JpnNgF2GYNnSeuYG21rDDcP9EQZKv1a2KaKcgFRtzktlegRX0kGjDxbYe +0jo5j4oJZ944W79GqcQLeRpOU8PnbSaBtRPSeX1VhzpkR3pQy5FgBikQkvRP/dJw +H5Ck1P7fRzDnlv4cypSk6v9PWSQMrSYlyp1a40b9U1bIF2cQ+HVDwUg+56OPAey6 +wBELObfROXagGdjRP+OQJUJgFHS3Uss= +-----END CERTIFICATE----- diff --git a/tests/data/notary_service_container/signer/config.json b/tests/data/notary_service_container/signer/config.json new file mode 100644 index 000000000..dfe7bc2b6 --- /dev/null +++ b/tests/data/notary_service_container/signer/config.json @@ -0,0 +1,11 @@ +{ + "server": { + "grpc_addr": ":7899", + "tls_cert_file": "./notary-signer.crt", + "tls_key_file": "./notary-signer.key", + "client_ca_file": "./ca.crt" + }, + "storage": { + "backend": "memory" + } + } diff --git a/tests/data/notary_service_container/signer/notary-signer.crt b/tests/data/notary_service_container/signer/notary-signer.crt new file mode 100644 index 000000000..e8eb44b5f --- /dev/null +++ b/tests/data/notary_service_container/signer/notary-signer.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC6jCCAdKgAwIBAgIURG6fK3XvIY0hybWB9UUl016hmOIwDQYJKoZIhvcNAQEL +BQAwGTEXMBUGA1UEAwwOTm90YXJ5IFJvb3QgQ0EwHhcNMjMxMTIyMjIxMzM2WhcN +MjQxMTIxMjIxMzM2WjAYMRYwFAYDVQQDDA1ub3Rhcnkuc2lnbmVyMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnpgKhkqK2+R2ln3OFPOmOzt/19aoMYVR +gibtecfXzysFNTeF/OBIq9vO6oP1aYrKNfednZWs3VupQQn0cQ0tN3eAtA6Gl6bh +BToAQnh5ii8WLsi8gg1K5Plvyd2oCYBGxx3Z2FYPMtd6iJ2kjAo+DoL4/LdxgbaX +x3b0blYn0fyo2CUy8mLS3Swnuk1j1YpRaYKMtTRpq3OOrrLaCasOHY037aCQ5G4W +JeIFuLFsvp/Ym0PiwQhh9kuPLfjwZts7tDTyXIq6GrrlNWm3q0cVUDZ8Rqca0EL2 +EgstMACzMh+3mx0uW9LVWnib1kYprwLt/ghIFQoJvCf63BfiRZdZwwIDAQABoysw +KTAnBgNVHREEIDAegg1ub3Rhcnktc2lnbmVygg1ub3Rhcnkuc2lnbmVyMA0GCSqG +SIb3DQEBCwUAA4IBAQC0SGmLJJVOCYqSd3GV5JKEIZhu6hIf9MWbd6OsOJt/lX33 +QmhjiV+yL/uZKANyMCDqtpQwKKhr28i2pHhD+5ThikxkPD82cYsAc4SkuotUT08f +CZU/Tn4621Ft23ANtV/tOrU2b6vBjJCmHH2OJHF+T69Jc6x5RqNthWyqRa9WAW4R +kWui4POWRpkddI4UAr7wCnWW7PFBPSlN8tHOanRG2ry+DQYAqK6HzxdLfItQ4s4v +DgQPpXyaG7XsaEs7y/DjhX//GQg/VUceYvbs5UX+eV0c5IGeklWemzKlqDdHh5Oo +0JPPsuLayS3EKJoikAmJzVtRNt49aEyv+2tEy+Aj +-----END CERTIFICATE----- diff --git a/tests/data/notary_service_container/signer/notary-signer.key b/tests/data/notary_service_container/signer/notary-signer.key new file mode 100644 index 000000000..d5c1879b3 --- /dev/null +++ b/tests/data/notary_service_container/signer/notary-signer.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCemAqGSorb5HaW +fc4U86Y7O3/X1qgxhVGCJu15x9fPKwU1N4X84Eir287qg/Vpiso1952dlazdW6lB +CfRxDS03d4C0DoaXpuEFOgBCeHmKLxYuyLyCDUrk+W/J3agJgEbHHdnYVg8y13qI +naSMCj4Ogvj8t3GBtpfHdvRuVifR/KjYJTLyYtLdLCe6TWPVilFpgoy1NGmrc46u +stoJqw4djTftoJDkbhYl4gW4sWy+n9ibQ+LBCGH2S48t+PBm2zu0NPJciroauuU1 +aberRxVQNnxGpxrQQvYSCy0wALMyH7ebHS5b0tVaeJvWRimvAu3+CEgVCgm8J/rc +F+JFl1nDAgMBAAECggEAI0SQYpjFFG1T6deEMqiUOOvcXDVCJfEN1TGu8bv9Q/a/ +K7xzMW72+jDhbMl3k8bnfOTZyid0z60IkIwULefOPF1445GvYJ4dwDnLwPxlmtMp +zbGaKhmmpBDMSUnfim1aUAFVZoSM0LOWbjcQVowYjMWXdSueNEhUrGjJppfGNy3s +8XLZIhmENNlsJsgbdgszZ8qPwK6npmeafmMc2lS44hxwr7nj0Y082QohXnLhcqoh +gmJ9aSpAD6Nsavp2kdZ1bF0AqJNppIHCCeg9toMmpgjMKggW6/Y1BHpXP+ONB7zd +cdnggfWbPSZYoAI4bLWLvUcyW3yqey7QKqubPyN+8QKBgQDSsDwZhL2fDfXSyUhZ +fhmczFNwA2NsEzyhVGHtS5MB05hWr6XQedtCnldPDXHSJwsKK98faAi8YQIxzBWc +pwcmc5wDgLeu2M0uJCxgMCecQOmZ3W1zkzagmYIwtkuP3rXPCJG2kcTMAGwDwZTO +zgSRY9qmCoae71BoAU0L9xD1XwKBgQDAs6phUy4jSplZQk7zrevZe/Nct3cemWMS +NJefLtZC5y4PJ8060pz43nBbq1l2T0n5ZC/S/K4fCH2IjSdrSCdtRrzjqMcYd92C +lmGjhsVyXTITcH4tXjErTTHUs/nWk7STiu7q6qeJxxFyNAzLJDMn5CXXHLF5fNNw +imqdWKEyHQKBgQCpOH+tB16+B5pv6NdBefTcaYiCqVYLkg/ajEnzLAYxK2BqD74g +ih8/jKoXhnbrEgzd6IrXUNnjZA89K+wX3Ffz3FtsvM/LkbqK2ucBguvtpn155c1p +TM5Ng757nY9nSLvCQ+G1P7NPHu+ivLLmv7YPiKIvRrkForV0M8dMWu62BQKBgAqF +RJYProMqvXiMEDdplWjIRZ8YPR6kjS4fRO/h5Ly+VltpduDxQrSroELA9h9pcMZ2 +282PEgqLsh7UZgSLaeujYwii3EvPr35Dq4z7/KejwuogyCK9871Dd6b/NHKsmb08 +ZpLYwNDa127+vHwSu+A/qnk2DdJuKDUKuYthnVtZAoGBALtfB7eC6nDWOm2mv20h +r7sp4wUlhBEliN0vg+0YQaV2qVeMJ3uXTZBSSKftdKElfQCXbt7i2bDwNCwK5UdC ++kqS2ISwtUcgF4dyCi5HO59nvqv5uHZRZalHVnkT/1t6XSM1wSvi7AURWZW+wEQ7 +mIjnfjw9jNfMAxxAjlGEcYVv +-----END PRIVATE KEY----- diff --git a/tests/integration/cases.yaml b/tests/integration/cases.yaml index 62f1cf2b5..006bb1927 100644 --- a/tests/integration/cases.yaml +++ b/tests/integration/cases.yaml @@ -77,6 +77,23 @@ test_cases: namespace: default expected_msg: Defaulting debug container name to debugger- expected_result: VALID + self-hosted-notary: + # "shnu" is exactly the same test case as "ru" from the regular test + # to ensure the new settings did not render connaisseur dysfunctional + - id: shnu + txt: Testing unsigned image... + type: deploy + ref: securesystemsengineering/testimage:unsigned + namespace: default + expected_msg: Unable to find signed digest for image docker.io/securesystemsengineering/testimage:unsigned. + expected_result: INVALID + - id: shnsi + txt: Testing signed image with trust data in self-hosted notary... + type: deploy + ref: securesystemsengineering/testimage:self-hosted-notary-signed + namespace: default + expected_msg: pod/pod-shnsi-${RAND} created + expected_result: VALID cosign: - id: cu txt: Testing unsigned cosign image... diff --git a/tests/integration/integration-test.sh b/tests/integration/integration-test.sh index f6d30cfd4..ad9bb64d0 100755 --- a/tests/integration/integration-test.sh +++ b/tests/integration/integration-test.sh @@ -357,12 +357,33 @@ update_helm_for_workloads() { rm update } +update_for_self_hosted_notary() { + envsubst update + yq '. *+ load("ghcr-values")' -i update + yq eval-all --inplace 'select(fileIndex == 0) * select(fileIndex == 1)' helm/values.yaml update + rm update + curl "https://notary.server:4443/v2/docker.io/securesystemsengineering/testimage/_trust/tuf/root.json" > root.json + SELF_HOSTED_NOTARY_PUBLIC_ROOT_KEY_ID=$(cat root.json | jq -r .signatures[0].keyid) + cat root.json | jq -r --arg KEYID $SELF_HOSTED_NOTARY_PUBLIC_ROOT_KEY_ID '.signed.keys | .[$KEYID] | .keyval.public' | base64 -d | openssl x509 -pubkey -noout > self_hosted_notary_root_key.pub + yq -i '(.application.validators.[] | select(.name == "self-hosted-notary") | .trustRoots.[] | select(.name=="default") | .key) |= load_str("self_hosted_notary_root_key.pub")' helm/values.yaml + rm self_hosted_notary_root_key.pub root.json +} + enable_alerting() { envsubst update yq eval-all --inplace 'select(fileIndex == 0) * select(fileIndex == 1)' helm/values.yaml update rm update } +render_notary_host() { + envsubst update_deployment_with_host_alias.yaml + # It is not possible to yq the deployment.yaml as it's not valid yaml due to the helm variables, thus the awk magic here + HOST_ALIAS=$(cat update_deployment_with_host_alias.yaml) + awk -v host_alias="${HOST_ALIAS}" '/ spec:/ { print; print host_alias; next }1' helm/templates/deployment.yaml > deployment.yaml + mv deployment.yaml helm/templates/ + rm update_deployment_with_host_alias.yaml +} + debug_values() { #PATH echo "::group::values.yaml" yq '... comments=""' "$1" @@ -410,6 +431,11 @@ regular_int_test() { rm diff.log } +### RUN SELF-HOSTED NOTARY INTEGRATION TEST #################################### +self_hosted_notary_int_test() { + multi_test "self-hosted-notary" +} + ### COSIGN TEST #################################### cosign_int_test() { multi_test "cosign" @@ -553,6 +579,12 @@ case $1 in helm_repo_install pre_config_int_test ;; +"self-hosted-notary") + update_for_self_hosted_notary + render_notary_host + make_install + self_hosted_notary_int_test + ;; "complexity") update_values_minimal update_values '.kubernetes.deployment.replicasCount=3' '.kubernetes.deployment.resources= {"limits": {"cpu":"1000m", "memory":"512Mi"},"requests": {"cpu":"500m", "memory":"512Mi"}}' diff --git a/tests/integration/notary_addhash.sh b/tests/integration/notary_addhash.sh new file mode 100755 index 000000000..f2e232ba5 --- /dev/null +++ b/tests/integration/notary_addhash.sh @@ -0,0 +1,8 @@ +#!/usr/bin/expect + +spawn notary addhash docker.io/securesystemsengineering/testimage self-hosted-notary-signed 528 --sha256 [lindex $argv 0] --publish -c ./tests/data/notary_service_container/config/client_config.json -s https://notary.server:4443 -D +expect "Enter passphrase for targets key with ID*\r" +send -- "0123456789\r" +expect "Enter passphrase for snapshot key with ID*\r" +send -- "0123456789\r" +expect eof diff --git a/tests/integration/notary_init.sh b/tests/integration/notary_init.sh new file mode 100755 index 000000000..8dfaf2e19 --- /dev/null +++ b/tests/integration/notary_init.sh @@ -0,0 +1,16 @@ +#!/usr/bin/expect + +spawn notary init docker.io/securesystemsengineering/testimage --publish -c ./tests/data/notary_service_container/config/client_config.json -s https://notary.server:4443 -D +expect "Enter passphrase for new root key with ID*\r" +send -- "0123456789\r" +expect "Repeat passphrase for new root key with ID*\r" +send -- "0123456789\r" +expect "Enter passphrase for new targets key with ID*\r" +send -- "0123456789\r" +expect "Repeat passphrase for new targets key with ID*\r" +send -- "0123456789\r" +expect "Enter passphrase for new snapshot key with ID*\r" +send -- "0123456789\r" +expect "Repeat passphrase for new snapshot key with ID*\r" +send -- "0123456789\r" +expect eof diff --git a/tests/integration/update-self-hosted-notary.yaml b/tests/integration/update-self-hosted-notary.yaml new file mode 100644 index 000000000..6df5ed882 --- /dev/null +++ b/tests/integration/update-self-hosted-notary.yaml @@ -0,0 +1,66 @@ +kubernetes: + deployment: + replicasCount: 1 +application: + validators: + - name: allow + type: static + approve: true + - name: deny + type: static + approve: false + - name: dockerhub-basics + type: notaryv1 + host: notary.docker.io + trustRoots: + - name: docker-official + key: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOXYta5TgdCwXTCnLU09W5T4M4r9f + QQrqJuADP6U7g5r9ICgPSmZuRHP/1AYUfOQW3baveKsT969EfELKj1lfCA== + -----END PUBLIC KEY----- + - name: securesystemsengineering-official + key: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsx28WV7BsQfnHF1kZmpdCTTLJaWe + d0CA+JOi8H4REuBaWSZ5zPDe468WuOJ6f71E7WFg3CVEVYHuoZt2UYbN/Q== + -----END PUBLIC KEY----- + - name: self-hosted-notary + type: notaryv1 + host: notary.server:4443 + trustRoots: + - name: default + key: self_hosted_notary_root_key.pub + # CA cert signing the cert of the self-hosted notary + # (tests/data/notary_service_container/certs/root/ca.crt) + cert: | + -----BEGIN CERTIFICATE----- + MIIDEzCCAfugAwIBAgIUDDvwp5mG7ckbcvJ0jg9he/rSGzAwDQYJKoZIhvcNAQEL + BQAwGTEXMBUGA1UEAwwOTm90YXJ5IFJvb3QgQ0EwHhcNMjMxMTIyMjIwOTM5WhcN + MjQxMTIxMjIwOTM5WjAZMRcwFQYDVQQDDA5Ob3RhcnkgUm9vdCBDQTCCASIwDQYJ + KoZIhvcNAQEBBQADggEPADCCAQoCggEBAO6IjUiNqWDrwO1Yn/Yfc5hSUdH3ZZ1i + SzbsYL4ZL3ZN/rXv8rS3JBeNpbcRs85lYIjC8prCh9tJ6FjnMqUVI5pGz9XZPLNO + hQA4eW4Z5lHDd1oWCT1Y66zwUYSD59xUzGWCZ5OhZlKanu8OfdGDyoPJxKYmhZQz + Vnnh1chZC5eBBzObufWO47aMg4rX7ZLCER/HxLHfSnCVMVwOa3KBHDK3yWy+y86y + 7/0bl5iW/bR8OZ/cjH4eEN9QdbAYBsavEsJ6wxBDgdweUH+7fQfORn5/IJtqwGha + FAyfeTwRI8/8tA4J1U08/p8oIopbzcGTg1LjPHQ8s8RTtknzg+jTrWkCAwEAAaNT + MFEwHQYDVR0OBBYEFKkx59QcInNCTOluvzHt3frciFqtMB8GA1UdIwQYMBaAFKkx + 59QcInNCTOluvzHt3frciFqtMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL + BQADggEBAKyPiDT0PXTHzpC7sLq7s2pUe3ezqg51m4NTgynO3u+vYftTCMVDIV0P + H7ZJNyHHYvglaLjRnsWdjGs4tcqjIYCRL8Tnc/CVorI0QGGwDDlvWXNXleAxjV3t + t5hy7t5JpnNgF2GYNnSeuYG21rDDcP9EQZKv1a2KaKcgFRtzktlegRX0kGjDxbYe + 0jo5j4oJZ944W79GqcQLeRpOU8PnbSaBtRPSeX1VhzpkR3pQy5FgBikQkvRP/dJw + H5Ck1P7fRzDnlv4cypSk6v9PWSQMrSYlyp1a40b9U1bIF2cQ+HVDwUg+56OPAey6 + wBELObfROXagGdjRP+OQJUJgFHS3Uss= + -----END CERTIFICATE----- + policy: + - pattern: "*:*" + validator: dockerhub-basics + with: + trustRoot: securesystemsengineering-official + - pattern: "k8s.gcr.io/*:*" + validator: allow + - pattern: docker.io/securesystemsengineering/connaisseur:* + validator: allow + - pattern: "docker.io/securesystemsengineering/testimage:self-hosted-notary-signed" + validator: self-hosted-notary diff --git a/tests/integration/update_deployment_notary_test.yaml b/tests/integration/update_deployment_notary_test.yaml new file mode 100644 index 000000000..412e31b8c --- /dev/null +++ b/tests/integration/update_deployment_notary_test.yaml @@ -0,0 +1,4 @@ + hostAliases: + - ip: ${NOTARY_IP} + hostnames: + - "notary.server" diff --git a/tests/validators/notaryv1/test_notary.py b/tests/validators/notaryv1/test_notary.py index 157d42345..be2353d97 100644 --- a/tests/validators/notaryv1/test_notary.py +++ b/tests/validators/notaryv1/test_notary.py @@ -18,7 +18,7 @@ def sample_notaries(): notary.Notary.CERT_PATH = "tests/data/notary/{}.cert" li = [] - for file_name in ("notary1", "notary2", "unhealthy_notary"): + for file_name in ("notary1", "notary2", "unhealthy_notary", "hanswurstnotary"): with open(f"tests/data/notary/{file_name}.yaml") as file: li.append(yaml.safe_load(file)) return li @@ -122,6 +122,49 @@ def test_healthy(sample_notaries, m_request, index, host, health): assert no.healthy == health +@pytest.mark.asyncio +@pytest.mark.parametrize( + "index, image, output, exception", + [ + ( + 3, + "alice-image", + (fix.get_td("alice-image/root"), "a.valid.token"), + fix.no_exc(), + ), + (0, "empty.io/alice-image", {}, pytest.raises(exc.NotFoundException)), + ( + 0, + "notary_wo_auth.io/alice-image", + (fix.get_td("alice-image/root"), None), + fix.no_exc(), + ), + ], +) +async def test_get_root_trust_data_and_auth( + monkeypatch, + sample_notaries, + m_request, + index, + image, + output, + exception, +): + async with aiohttp.ClientSession() as session: + with exception: + with aioresponses() as aio: + aio.get(re.compile(r".*"), callback=fix.async_callback, repeat=True) + no = notary.Notary(**sample_notaries[index]) + trust_data, token = await no.get_root_trust_data_and_auth( + session, + Image(image), + ) + (expected_trust_data, expected_token) = output + assert token == expected_token + assert trust_data.signed == expected_trust_data["signed"] + assert trust_data.signatures == expected_trust_data["signatures"] + + @pytest.mark.asyncio @pytest.mark.parametrize( "index, image, role, output, exception", @@ -151,9 +194,10 @@ async def test_get_trust_data( async with aiohttp.ClientSession() as session: with exception: with aioresponses() as aio: + token = "***" aio.get(re.compile(r".*"), callback=fix.async_callback, repeat=True) no = notary.Notary(**sample_notaries[index]) - td = await no.get_trust_data(session, Image(image), role) + td = await no.get_trust_data(session, Image(image), role, token) assert td.signed == output["signed"] assert td.signatures == output["signatures"] @@ -190,10 +234,11 @@ async def test_get_delegation_trust_data( async with aiohttp.ClientSession() as session: with exception: with aioresponses() as aio: + token = "***" aio.get(re.compile(r".*"), callback=fix.async_callback) no = notary.Notary(**sample_notaries[index]) td = await no.get_delegation_trust_data( - session, Image(image), "targets/phbelitz" + session, Image(image), "targets/phbelitz", token ) assert output is bool(td) diff --git a/tests/validators/notaryv1/test_notaryv1_validator.py b/tests/validators/notaryv1/test_notaryv1_validator.py index 6cfb89e7a..7138f3086 100644 --- a/tests/validators/notaryv1/test_notaryv1_validator.py +++ b/tests/validators/notaryv1/test_notaryv1_validator.py @@ -424,6 +424,7 @@ async def test_update_with_delegation_trust_data( delegations, expected_trust_data_keys, ): + token = "ey123" async with aiohttp.ClientSession() as session: with aioresponses() as aio: aio.get(re.compile(r".*"), callback=fix.async_callback, repeat=True) @@ -435,6 +436,7 @@ async def test_update_with_delegation_trust_data( delegations, alice_key_store, Image("alice-image"), + token, ) is None )