From 9d732c996e92f79330145bfb570507e224391e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Fri, 20 Jan 2023 15:44:38 +0100 Subject: [PATCH 01/60] Add initcontianer that extracts matching imagepullsecrets #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../secretExtractionInitContainer/dockerfile | 6 ++ .../requirements.txt | 17 ++++ .../secret_extraction.py | 97 +++++++++++++++++++ .../test_secret_extraction.py | 53 ++++++++++ .../test_secrets/secret_1/.dockerconfigjson | 7 ++ .../secret_1/not_a_docker_config_json | 1 + .../test_secrets/secret_2/.dockerconfigjson | 7 ++ 7 files changed, 188 insertions(+) create mode 100644 auto-discovery/kubernetes/secretExtractionInitContainer/dockerfile create mode 100644 auto-discovery/kubernetes/secretExtractionInitContainer/requirements.txt create mode 100644 auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py create mode 100644 auto-discovery/kubernetes/secretExtractionInitContainer/test_secret_extraction.py create mode 100644 auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_1/.dockerconfigjson create mode 100644 auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_1/not_a_docker_config_json create mode 100644 auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_2/.dockerconfigjson diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/dockerfile b/auto-discovery/kubernetes/secretExtractionInitContainer/dockerfile new file mode 100644 index 0000000000..aa2a44b85f --- /dev/null +++ b/auto-discovery/kubernetes/secretExtractionInitContainer/dockerfile @@ -0,0 +1,6 @@ +FROM python:3 + +COPY requirements.txt . +RUN pip install -r requirements.txt +COPY secret_extraction.py . +CMD ["python", secret_extraction.py] diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/requirements.txt b/auto-discovery/kubernetes/secretExtractionInitContainer/requirements.txt new file mode 100644 index 0000000000..798708a532 --- /dev/null +++ b/auto-discovery/kubernetes/secretExtractionInitContainer/requirements.txt @@ -0,0 +1,17 @@ +cachetools==5.2.1 +certifi==2022.12.7 +charset-normalizer==3.0.1 +google-auth==2.16.0 +idna==3.4 +kubernetes==25.3.0 +oauthlib==3.2.2 +pyasn1==0.4.8 +pyasn1-modules==0.2.8 +python-dateutil==2.8.2 +PyYAML==6.0 +requests==2.28.2 +requests-oauthlib==1.3.1 +rsa==4.9 +six==1.16.0 +urllib3==1.26.14 +websocket-client==1.4.2 diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py b/auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py new file mode 100644 index 0000000000..ed44003b84 --- /dev/null +++ b/auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py @@ -0,0 +1,97 @@ +import glob +import json +import sys +import base64 + +from kubernetes import client, config + + +def main(): + image_id = sys.argv[1] + temporary_secret_name = sys.argv[2] + namespace = sys.argv[3] + + raw_secrets = get_raw_secrets('/secrets') + correct_secret = get_correct_secret(image_id, raw_secrets) + username, password = get_user_and_password(correct_secret) + create_temporary_secret(username, password, temporary_secret_name, namespace) + + +def get_raw_secrets(base_path: str): + """Reads in files called '.dockerconfigjson' in the path given and return the content of all files called so + @:param base_path: Directory to search for dockerconfigjson files + @:returns List of secrets found in base_path + """ + raw_secrets = [] + for file_name in glob.glob(f'{base_path}/**/.dockerconfigjson', recursive=True): + with open(file_name) as file: + raw_secret = json.load(file) + raw_secrets.append(raw_secret) + return raw_secrets + + +def get_correct_secret(image_id, secrets) -> dict[str, str]: + """Iterates over given list of secrets to find the secret that machtes the URL in the given imageID + @:param: image_id: The imageID of which the correct secret needs to be identified + @:param: secrets: List of secrets + @:returns: Dict containing the secret matching the given imageID + """ + for secret in secrets: + for url, data in secret['auths'].items(): + if url in image_id: + return data + + +def get_user_and_password(raw_secret: dict[str, str]) -> tuple[str, str]: + """Extracts username and password from a given secret + @:param: raw_secret: Dict containing the secret. Should contain key 'auth' (where username and password are + base64 encoded in a single line like: username:password), or 'username' and 'password' as a separate key + (also base64) + @:returns: tuple containing username and password both base64 encoded + """ + if 'auth' in raw_secret: + # secret is in form "username:password" (base64 encoded) + username_password_combo = decode_base64(raw_secret['auth']) + tmp_list = username_password_combo.split(":") + + # k8s wants the secrets as base64, so the individual values are converted back to base64 + username = encode_base64(tmp_list[0]) + password = encode_base64(tmp_list[1]) + return username, password + + elif 'username' in raw_secret and 'password' in raw_secret: + # username and password are already separated and base64 encoded, no need to do more + username = raw_secret['username'] + password = raw_secret['password'] + return username, password + + else: + raise KeyError('dockerconfigjson secret does not contain known structure!') + + +def decode_base64(raw_string: str) -> str: + return base64.b64decode(raw_string).decode('utf-8') + + +def encode_base64(string: str) -> str: + return base64.b64encode(string.encode('utf-8')).decode('utf-8') + + +def create_temporary_secret(username: str, password: str, secret_name: str, namespace: str): + """Creates a secret with name 'secret_name' with 'username' and 'password' as data in given namespace + @:param: username: base64 encoded string representing the desired value of the 'username' field in the secret + @:param: password: base64 encoded string representing the desired value of the 'password' field in the secret + @:param: secret_name: Name of the newly created secret + @:param: namespace: Name of the namespace in which the new secret will be created + """ + config.load_incluster_config() + v1 = client.CoreV1Api() + + secret_data = {'username': username, 'password': password} + metadata = client.V1ObjectMeta(name=secret_name, namespace=namespace) + secret_body = client.V1Secret(api_version='v1', kind='Secret', metadata=metadata, data=secret_data, type='Opaque') + v1.create_namespaced_secret(namespace=namespace, body=secret_body) + + +if __name__ == '__main__': + main() diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/test_secret_extraction.py b/auto-discovery/kubernetes/secretExtractionInitContainer/test_secret_extraction.py new file mode 100644 index 0000000000..0bcf9a7a3a --- /dev/null +++ b/auto-discovery/kubernetes/secretExtractionInitContainer/test_secret_extraction.py @@ -0,0 +1,53 @@ +import unittest + +from secret_extraction import * + + +class MyTestCase(unittest.TestCase): + def test_get_raw_secrets(self): + actual = get_raw_secrets('test_secrets') + + with open('test_secrets/secret_1/.dockerconfigjson') as file: + expected = [json.load(file)] + + with open('test_secrets/secret_2/.dockerconfigjson') as file: + expected.append(json.load(file)) + + self.assertEqual(expected, actual) + + def test_get_correct_secret(self): + with open('test_secrets/secret_1/.dockerconfigjson') as file: + secret_list = [json.load(file)] + + with open('test_secrets/secret_2/.dockerconfigjson') as file: + secret_list.append(json.load(file)) + + actual = get_correct_secret('localhost:5000:12345/some/image@654654', secret_list) + + # testuser:testpassword base64 encoded + expected = {'auth': 'dGVzdHVzZXI6dGVzdHBhc3N3b3Jk'} + + self.assertEqual(expected, actual) + + def test_get_user_and_password_given_auth_string(self): + secret = {'auth': 'dGVzdHVzZXI6dGVzdHBhc3N3b3Jk'} + actual = get_user_and_password(secret) + + # testuser, testpassword base64 encoded + expected = ('dGVzdHVzZXI=', 'dGVzdHBhc3N3b3Jk') + + self.assertEqual(expected, actual) + + def test_get_and_password_given_username_and_password_as_separate_string(self): + secret = { + 'username': 'dGVzdHVzZXI=', + 'password': 'dGVzdHBhc3N3b3Jk' + } + actual = get_user_and_password(secret) + + expected = (secret['username'], secret['password']) + self.assertEqual(expected, actual) + + +if __name__ == '__main__': + unittest.main() diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_1/.dockerconfigjson b/auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_1/.dockerconfigjson new file mode 100644 index 0000000000..22be493640 --- /dev/null +++ b/auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_1/.dockerconfigjson @@ -0,0 +1,7 @@ +{ + "auths": { + "localhost:5000": { + "auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" + } + } +} \ No newline at end of file diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_1/not_a_docker_config_json b/auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_1/not_a_docker_config_json new file mode 100644 index 0000000000..95916494c6 --- /dev/null +++ b/auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_1/not_a_docker_config_json @@ -0,0 +1 @@ +This file should be ignored by the unit tests as its not called dockerconfigjson diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_2/.dockerconfigjson b/auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_2/.dockerconfigjson new file mode 100644 index 0000000000..4dbd82042c --- /dev/null +++ b/auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_2/.dockerconfigjson @@ -0,0 +1,7 @@ +{ + "auths": { + "random-registry.xyz": { + "auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" + } + } +} From 08ecdbc426b64cbca82dfad6cb2cfa6b0ec05487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Fri, 20 Jan 2023 17:13:41 +0100 Subject: [PATCH 02/60] Add ownerreference to temporary container autodiscovery secret #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../secret_extraction.py | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py b/auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py index ed44003b84..a8e16ca373 100644 --- a/auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py +++ b/auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py @@ -9,12 +9,11 @@ def main(): image_id = sys.argv[1] temporary_secret_name = sys.argv[2] - namespace = sys.argv[3] raw_secrets = get_raw_secrets('/secrets') correct_secret = get_correct_secret(image_id, raw_secrets) username, password = get_user_and_password(correct_secret) - create_temporary_secret(username, password, temporary_secret_name, namespace) + create_temporary_secret(username, password, temporary_secret_name) def get_raw_secrets(base_path: str): @@ -77,21 +76,39 @@ def encode_base64(string: str) -> str: return base64.b64encode(string.encode('utf-8')).decode('utf-8') -def create_temporary_secret(username: str, password: str, secret_name: str, namespace: str): +def create_temporary_secret(username: str, password: str, secret_name: str): """Creates a secret with name 'secret_name' with 'username' and 'password' as data in given namespace @:param: username: base64 encoded string representing the desired value of the 'username' field in the secret @:param: password: base64 encoded string representing the desired value of the 'password' field in the secret @:param: secret_name: Name of the newly created secret - @:param: namespace: Name of the namespace in which the new secret will be created """ config.load_incluster_config() v1 = client.CoreV1Api() + namespace = get_namespace() + + pod_name = get_pod_name() + pod = v1.read_namespaced_pod(name=pod_name, namespace=namespace) + secret_data = {'username': username, 'password': password} - metadata = client.V1ObjectMeta(name=secret_name, namespace=namespace) + owner_references = client.V1OwnerReference(api_version='v1', name=pod_name, uid=pod.metadata.uid, kind='Pod') + metadata = client.V1ObjectMeta(name=secret_name, namespace=namespace, owner_references=[owner_references]) secret_body = client.V1Secret(api_version='v1', kind='Secret', metadata=metadata, data=secret_data, type='Opaque') v1.create_namespaced_secret(namespace=namespace, body=secret_body) +def get_pod_name() -> str: + """Reads pod name from /etc/hostname""" + with open('/etc/hostname') as file: + return file.readline().strip() + + +def get_namespace() -> str: + """Reads namespace of pod in which this container runs from + /var/run/secrets/kubernetes.io/serviceaccount/namespace""" + with open('/var/run/secrets/kubernetes.io/serviceaccount/namespace') as file: + return file.readline().strip() + + if __name__ == '__main__': main() From 292e4a1a78a10ca4dc70c2821e920a86bcded1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sat, 21 Jan 2023 18:58:51 +0100 Subject: [PATCH 03/60] Fix python docstrings in secretextractioninitcontainer #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../secret_extraction.py | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py b/auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py index a8e16ca373..14a73508fa 100644 --- a/auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py +++ b/auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py @@ -18,8 +18,8 @@ def main(): def get_raw_secrets(base_path: str): """Reads in files called '.dockerconfigjson' in the path given and return the content of all files called so - @:param base_path: Directory to search for dockerconfigjson files - @:returns List of secrets found in base_path + :param base_path: Directory to search for dockerconfigjson files + :returns: List of secrets found in base_path """ raw_secrets = [] for file_name in glob.glob(f'{base_path}/**/.dockerconfigjson', recursive=True): @@ -29,11 +29,11 @@ def get_raw_secrets(base_path: str): return raw_secrets -def get_correct_secret(image_id, secrets) -> dict[str, str]: +def get_correct_secret(image_id: str, secrets) -> dict[str, str]: """Iterates over given list of secrets to find the secret that machtes the URL in the given imageID - @:param: image_id: The imageID of which the correct secret needs to be identified - @:param: secrets: List of secrets - @:returns: Dict containing the secret matching the given imageID + :param image_id: The imageID of which the correct secret needs to be identified + :param secrets: List of secrets + :returns: Dict containing the secret matching the given imageID """ for secret in secrets: for url, data in secret['auths'].items(): @@ -43,10 +43,11 @@ def get_correct_secret(image_id, secrets) -> dict[str, str]: def get_user_and_password(raw_secret: dict[str, str]) -> tuple[str, str]: """Extracts username and password from a given secret - @:param: raw_secret: Dict containing the secret. Should contain key 'auth' (where username and password are + :param raw_secret: Dict containing the secret. Should contain key 'auth' (where username and password are base64 encoded in a single line like: username:password), or 'username' and 'password' as a separate key (also base64) - @:returns: tuple containing username and password both base64 encoded + :returns: tuple containing username and password both base64 encoded + :raises KeyError: Structure of given secret does not contain expected structure. """ if 'auth' in raw_secret: # secret is in form "username:password" (base64 encoded) @@ -65,7 +66,7 @@ def get_user_and_password(raw_secret: dict[str, str]) -> tuple[str, str]: return username, password else: - raise KeyError('dockerconfigjson secret does not contain known structure!') + raise KeyError('dockerconfigjson secret does not contain expected structure!') def decode_base64(raw_string: str) -> str: @@ -77,10 +78,10 @@ def encode_base64(string: str) -> str: def create_temporary_secret(username: str, password: str, secret_name: str): - """Creates a secret with name 'secret_name' with 'username' and 'password' as data in given namespace - @:param: username: base64 encoded string representing the desired value of the 'username' field in the secret - @:param: password: base64 encoded string representing the desired value of the 'password' field in the secret - @:param: secret_name: Name of the newly created secret + """Creates a secret with name 'secret_name' with 'username' and 'password' as data in given namespace. The secret has an ownerreference to the pod this container is running in. + :param username: base64 encoded string representing the desired value of the 'username' field in the secret + :param password: base64 encoded string representing the desired value of the 'password' field in the secret + :param secret_name: Name of the newly created secret """ config.load_incluster_config() v1 = client.CoreV1Api() From 9294ecf61738fae664a76b94e6492a78cd9a5ed5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sat, 21 Jan 2023 19:29:11 +0100 Subject: [PATCH 04/60] Add readme to secretextractioninitcontainer explaining steps to create a local docker registry #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../secretExtractionInitContainer/readme.md | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 auto-discovery/kubernetes/secretExtractionInitContainer/readme.md diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/readme.md b/auto-discovery/kubernetes/secretExtractionInitContainer/readme.md new file mode 100644 index 0000000000..39344379a6 --- /dev/null +++ b/auto-discovery/kubernetes/secretExtractionInitContainer/readme.md @@ -0,0 +1,91 @@ +## Running a local docker registry +The easiest way to test the initContainer locally is to deploy a local registry using docker outside of the k8s cluster to be able to delete the cluster without recreating the registry every time. +### Creating a local registry with authentication +1. Create basic auth secrets +```bash + mkdir auth + docker run \ + --entrypoint htpasswd \ + httpd:2 -Bbn testuser testpassword > auth/htpasswd + +``` +2. Start registry container (Commands adapted from [Docker docs](https://docs.docker.com/registry/deploying/)) +```bash +docker run -d \ + -p "127.0.0.1:5000:5000" \ + --name registry \ + --restart=always \ + -v "$(pwd)"/auth:/auth \ + -e "REGISTRY_AUTH=htpasswd" \ + -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \ + -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ + registry:2 +``` +3. Login into newly created docker registry +```bash +docker login localhost:5000 -u testuser -p testpassword +``` +4. Create kind cluster with the local registry configured (Commands adapted from [kind docs](https://kind.sigs.k8s.io/docs/user/local-registry/)) +```bash +cat < Date: Sat, 21 Jan 2023 20:14:48 +0100 Subject: [PATCH 05/60] Mock kubernetes import in unit test to be able to run tests with the kubernetes packages installed #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../secretExtractionInitContainer/test_secret_extraction.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/test_secret_extraction.py b/auto-discovery/kubernetes/secretExtractionInitContainer/test_secret_extraction.py index 0bcf9a7a3a..2d9e1fe023 100644 --- a/auto-discovery/kubernetes/secretExtractionInitContainer/test_secret_extraction.py +++ b/auto-discovery/kubernetes/secretExtractionInitContainer/test_secret_extraction.py @@ -1,9 +1,15 @@ +import sys import unittest +from unittest.mock import MagicMock + +# mock kubernetes import so it doesnt need to be installed to run these tests +sys.modules['kubernetes'] = MagicMock() from secret_extraction import * class MyTestCase(unittest.TestCase): + def test_get_raw_secrets(self): actual = get_raw_secrets('test_secrets') From 22c26830928333d068b0564949333642947201e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sat, 21 Jan 2023 20:28:46 +0100 Subject: [PATCH 06/60] Add makefile #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../secretExtractionInitContainer/Makefile | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 auto-discovery/kubernetes/secretExtractionInitContainer/Makefile diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/Makefile b/auto-discovery/kubernetes/secretExtractionInitContainer/Makefile new file mode 100644 index 0000000000..fb121b6074 --- /dev/null +++ b/auto-discovery/kubernetes/secretExtractionInitContainer/Makefile @@ -0,0 +1,63 @@ + +# SPDX-FileCopyrightText: the secureCodeBox authors +# +# SPDX-License-Identifier: Apache-2.0 + +include ../../../prerequisites.mk + +IMG_NS ?= securecodebox + +# Image URL to use all building/pushing image targets +IMG ?= auto-discovery-secret-extraction-container + +# Tag used for the image +IMG_TAG ?= sha-$$(git rev-parse --short HEAD) + + +##@ General + +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk commands is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + + +##@ Development + +.PHONY: test +test: + $(PYTHON) -m unittest discover + +##@ Build + + +.PHONY: docker-build +docker-build: test ## Build docker image with the manager. + @echo ".: ⚙️ Build Container Images" + docker build -t ${IMG_NS}/${IMG}:${IMG_TAG} . + +.PHONY: docker-push +docker-push: ## Push docker image with the manager. + docker push ${IMG_NS}/${IMG}:${IMG_TAG} + +.PHONY: docker-export +docker-export: + @echo ".: 💾 Export Container Images" + docker save $(IMG_NS)/$(IMG):$(IMG_TAG) > $(IMG).tar + +##@ Deployment + +.PHONY: kind-import +kind-import: + @echo ".: 💾 Importing the image archive to local kind cluster." + kind load image-archive ./$(IMG).tar \ No newline at end of file From 0e7ae97ee55f470c817b1a11f9e57baf99ae698b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sat, 21 Jan 2023 20:37:41 +0100 Subject: [PATCH 07/60] Add secretextractioninitcontainer to ci workflow #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/ci.yaml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9c604dc960..5f5c7887fb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -123,6 +123,29 @@ jobs: name: auto-discovery-image path: ./auto-discovery/kubernetes/auto-discovery-kubernetes.tar retention-days: 1 + + auto-discovery-kubernetes-secret-extraction-container: + name: "Autodiscovery | Kubernetes | SecretExtractionInitContainer" + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Test + working-directory: ./autodiscovery/kubernetes/secretExtractionInitContainer + - name: Build Container Image + working-directory: ./auto-discovery/kubernetes/secretExtractionInitContainer + run: make docker-build + + - name: Export Container Image + working-directory: ./auto-discovery/kubernetes/secretExtractionInitContainer + run: make docker-export + + - name: Upload Image As Artifact + uses: actions/upload-artifact@v2 + with: + name: auto-discovery-image + path: ./auto-discovery/kubernetes/secretExtractionInitContainer/auto-discovery-secret-extraction-container.tar + retention-days: 1 # ---- Build Stage | SDK Matrix ---- From 5cf8d9e385e0488ccd90c12934252bd6a6dac819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sat, 21 Jan 2023 20:38:49 +0100 Subject: [PATCH 08/60] Update ci python version to 3.9 to support type hints used in secretextractioninitcontainer #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5f5c7887fb..bb1b42e24b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -19,7 +19,7 @@ env: # ---- Language Versions ---- GO_VERSION: "1.19" - PYTHON_VERSION: "3.8.10" + PYTHON_VERSION: "3.9.16" NODE_VERSION: "16" NPM_VERSION: "7" From be13ac07afe05bf10f369d695aef6a5320485def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sat, 21 Jan 2023 20:48:30 +0100 Subject: [PATCH 09/60] Fix type in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../controllers/container_scan_controller_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index 0108e24e5c..7f9b755d98 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -202,7 +202,7 @@ func checkScanGoTemplate(scan executionv1.ScheduledScan, scanSpec scanGoTemplate labels := scan.ObjectMeta.Labels parameters := scan.Spec.ScanSpec.Parameters - annotationsCorrect := reflect.DeepEqual(annotations, scanSpec.Annotaions) + annotationsCorrect := reflect.DeepEqual(annotations, scanSpec.Annotations) labelsCorrect := reflect.DeepEqual(labels, scanSpec.Labels) parametersCorrect := reflect.DeepEqual(parameters, scanSpec.Parameters) @@ -227,7 +227,7 @@ func checkScanGoTemplate(scan executionv1.ScheduledScan, scanSpec scanGoTemplate } type scanGoTemplate struct { - Annotaions map[string]string - Labels map[string]string - Parameters []string + Annotations map[string]string + Labels map[string]string + Parameters []string } From 2f75c8553a5a7c6f2d700544d8536cdc09208e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sat, 21 Jan 2023 21:40:22 +0100 Subject: [PATCH 10/60] Add unit tests for container autodiscovery with imagepullsecrets #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../container_scan_controller_test.go | 79 +++++++++++++++++-- .../service_scan_controller_test.go | 5 +- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index 7f9b755d98..ae44120d68 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -34,7 +34,7 @@ var _ = Describe("ContainerScan controller", func() { Context("Container autodiscovery for deployment", func() { - namespace := "default" + namespace := "container-auto-discovery" ctx := context.Background() nginxScanName := "nginx-at-0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31" @@ -46,6 +46,9 @@ var _ = Describe("ContainerScan controller", func() { "app.kubernetes.io/managed-by": "securecodebox-autodiscovery", }, []string{"-p", "default"}, + []corev1.Container{}, + []corev1.Volume{}, + []corev1.VolumeMount{}, } juiceShopScanName := "juice-shop-at-9342db143db5804dee3e64ff789be6ad8dd94f0491b2f50fa67c78be204081e2" @@ -57,6 +60,9 @@ var _ = Describe("ContainerScan controller", func() { "app.kubernetes.io/managed-by": "securecodebox-autodiscovery", }, []string{"-p", "default"}, + []corev1.Container{}, + []corev1.Volume{}, + []corev1.VolumeMount{}, } It("Should not create scans while the scan type is not installed", func() { @@ -116,9 +122,64 @@ var _ = Describe("ContainerScan controller", func() { }, timeout, interval).Should(BeTrue()) }) }) + Context("Container autodiscovery with imagePullSecrets", func() { + namespace := "container-autodiscovery-imagepullsecrets" + ctx := context.Background() + + nginxScanName := "nginx-at-0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31" + nginxScanName = nginxScanName[:62] + nginxScanGoTemplate := scanGoTemplate{ + map[string]string{"testAnnotation": "default"}, + map[string]string{ + "testLabel": "default", + "app.kubernetes.io/managed-by": "securecodebox-autodiscovery", + }, + []string{"-p", "default"}, + []corev1.Container{ + { + Name: "secret-extraction-to-env", + Image: "docker.io/securecodebox/auto-discovery-secret-extraction-container", + }, + }, + []corev1.Volume{ + { + Name: "test-pull-secret-volume", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test-pull-secret", + }, + }, + }, + }, + []corev1.VolumeMount{ + { + Name: "test-pull-secret-volume", + }, + }, + } + createNamespace(ctx, namespace) + createScanType(ctx, namespace) + + It("Should create a trivy scan with the secretExtractionInitContainer", func() { + fakeDeployment := map[string]string{"nginx": "0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31"} + imagePullSecrets := []corev1.LocalObjectReference{ + { + Name: "test-pull-secret", + }, + } + createPodWithMultipleContainersAndImagePullSecrets(ctx, "fake-deployment-pod1", namespace, fakeDeployment, imagePullSecrets) + + Eventually(func() bool { + return checkIfScanExists(ctx, nginxScanName, namespace, nginxScanGoTemplate) + }, timeout, interval).Should(BeTrue()) + }) + }) }) func createPodWithMultipleContainers(ctx context.Context, name string, namespace string, images map[string]string) { + createPodWithMultipleContainersAndImagePullSecrets(ctx, name, namespace, images, make([]corev1.LocalObjectReference, 0)) +} +func createPodWithMultipleContainersAndImagePullSecrets(ctx context.Context, name string, namespace string, images map[string]string, imagePullSecrets []corev1.LocalObjectReference) { pod := &corev1.Pod{ TypeMeta: metav1.TypeMeta{ APIVersion: "", @@ -132,7 +193,8 @@ func createPodWithMultipleContainers(ctx context.Context, name string, namespace }, }, Spec: corev1.PodSpec{ - Containers: getContainerSpec(name, images), + Containers: getContainerSpec(name, images), + ImagePullSecrets: imagePullSecrets, }, } @@ -205,10 +267,14 @@ func checkScanGoTemplate(scan executionv1.ScheduledScan, scanSpec scanGoTemplate annotationsCorrect := reflect.DeepEqual(annotations, scanSpec.Annotations) labelsCorrect := reflect.DeepEqual(labels, scanSpec.Labels) parametersCorrect := reflect.DeepEqual(parameters, scanSpec.Parameters) + volumesCorrect := reflect.DeepEqual(scan.Spec.ScanSpec.Volumes, scanSpec.Volumes) + volumeMountsCorrect := reflect.DeepEqual(scan.Spec.ScanSpec.VolumeMounts, scanSpec.VolumeMounts) Expect(annotationsCorrect).Should(BeTrue()) Expect(labelsCorrect).Should(BeTrue()) Expect(parametersCorrect).Should(BeTrue()) + Expect(volumesCorrect).Should(BeTrue()) + Expect(volumeMountsCorrect).Should(BeTrue()) Expect(scan.Spec.ScanSpec.HookSelector.MatchExpressions).To(ContainElement( metav1.LabelSelectorRequirement{ Operator: metav1.LabelSelectorOpIn, @@ -227,7 +293,10 @@ func checkScanGoTemplate(scan executionv1.ScheduledScan, scanSpec scanGoTemplate } type scanGoTemplate struct { - Annotations map[string]string - Labels map[string]string - Parameters []string + Annotations map[string]string + Labels map[string]string + Parameters []string + InitContainers []corev1.Container + Volumes []corev1.Volume + VolumeMounts []corev1.VolumeMount } diff --git a/auto-discovery/kubernetes/controllers/service_scan_controller_test.go b/auto-discovery/kubernetes/controllers/service_scan_controller_test.go index 6d74dbd26d..2b33937ea7 100644 --- a/auto-discovery/kubernetes/controllers/service_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/service_scan_controller_test.go @@ -184,7 +184,10 @@ func createNamespace(ctx context.Context, namespaceName string) { }, } - k8sClient.Create(ctx, namespace) + err := k8sClient.Create(ctx, namespace) + if err != nil { + panic(err) + } } func createPod(ctx context.Context, name string, namespace string, image string, imageDisgest string) { From 715a9ccb841680cb9b547e1294a6dbae756bafa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sat, 21 Jan 2023 21:53:16 +0100 Subject: [PATCH 11/60] Fix seg fault in test #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../kubernetes/controllers/container_scan_controller_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index ae44120d68..e762fb975c 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -157,10 +157,11 @@ var _ = Describe("ContainerScan controller", func() { }, }, } - createNamespace(ctx, namespace) - createScanType(ctx, namespace) It("Should create a trivy scan with the secretExtractionInitContainer", func() { + createNamespace(ctx, namespace) + createScanType(ctx, namespace) + fakeDeployment := map[string]string{"nginx": "0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31"} imagePullSecrets := []corev1.LocalObjectReference{ { From 229821db190c13b85426a971741a5cbadf5929f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sun, 22 Jan 2023 00:01:27 +0100 Subject: [PATCH 12/60] Fix bug with nil arrays in existing container autodiscovery tests #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../container_scan_controller_test.go | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index e762fb975c..ebbe5bf44a 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -40,29 +40,29 @@ var _ = Describe("ContainerScan controller", func() { nginxScanName := "nginx-at-0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31" nginxScanName = nginxScanName[:62] nginxScanGoTemplate := scanGoTemplate{ - map[string]string{"testAnnotation": "default"}, + map[string]string{"testAnnotation": namespace}, map[string]string{ - "testLabel": "default", + "testLabel": namespace, "app.kubernetes.io/managed-by": "securecodebox-autodiscovery", }, - []string{"-p", "default"}, - []corev1.Container{}, - []corev1.Volume{}, - []corev1.VolumeMount{}, + []string{"-p", namespace}, + nil, + nil, + nil, } juiceShopScanName := "juice-shop-at-9342db143db5804dee3e64ff789be6ad8dd94f0491b2f50fa67c78be204081e2" juiceShopScanName = juiceShopScanName[:62] juiceShopScanGoTemplate := scanGoTemplate{ - map[string]string{"testAnnotation": "default"}, + map[string]string{"testAnnotation": namespace}, map[string]string{ - "testLabel": "default", + "testLabel": namespace, "app.kubernetes.io/managed-by": "securecodebox-autodiscovery", }, - []string{"-p", "default"}, - []corev1.Container{}, - []corev1.Volume{}, - []corev1.VolumeMount{}, + []string{"-p", namespace}, + nil, + nil, + nil, } It("Should not create scans while the scan type is not installed", func() { @@ -129,12 +129,12 @@ var _ = Describe("ContainerScan controller", func() { nginxScanName := "nginx-at-0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31" nginxScanName = nginxScanName[:62] nginxScanGoTemplate := scanGoTemplate{ - map[string]string{"testAnnotation": "default"}, + map[string]string{"testAnnotation": namespace}, map[string]string{ - "testLabel": "default", + "testLabel": namespace, "app.kubernetes.io/managed-by": "securecodebox-autodiscovery", }, - []string{"-p", "default"}, + []string{"-p", namespace}, []corev1.Container{ { Name: "secret-extraction-to-env", @@ -178,7 +178,7 @@ var _ = Describe("ContainerScan controller", func() { }) func createPodWithMultipleContainers(ctx context.Context, name string, namespace string, images map[string]string) { - createPodWithMultipleContainersAndImagePullSecrets(ctx, name, namespace, images, make([]corev1.LocalObjectReference, 0)) + createPodWithMultipleContainersAndImagePullSecrets(ctx, name, namespace, images, []corev1.LocalObjectReference{}) } func createPodWithMultipleContainersAndImagePullSecrets(ctx context.Context, name string, namespace string, images map[string]string, imagePullSecrets []corev1.LocalObjectReference) { pod := &corev1.Pod{ @@ -264,12 +264,14 @@ func checkScanGoTemplate(scan executionv1.ScheduledScan, scanSpec scanGoTemplate annotations := scan.ObjectMeta.Annotations labels := scan.ObjectMeta.Labels parameters := scan.Spec.ScanSpec.Parameters + volumes := scan.Spec.ScanSpec.Volumes + volumeMounts := scan.Spec.ScanSpec.VolumeMounts annotationsCorrect := reflect.DeepEqual(annotations, scanSpec.Annotations) labelsCorrect := reflect.DeepEqual(labels, scanSpec.Labels) parametersCorrect := reflect.DeepEqual(parameters, scanSpec.Parameters) - volumesCorrect := reflect.DeepEqual(scan.Spec.ScanSpec.Volumes, scanSpec.Volumes) - volumeMountsCorrect := reflect.DeepEqual(scan.Spec.ScanSpec.VolumeMounts, scanSpec.VolumeMounts) + volumesCorrect := reflect.DeepEqual(volumes, scanSpec.Volumes) + volumeMountsCorrect := reflect.DeepEqual(volumeMounts, scanSpec.VolumeMounts) Expect(annotationsCorrect).Should(BeTrue()) Expect(labelsCorrect).Should(BeTrue()) From 04249297c58f9b3387420d564e73979ee14e6e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sun, 22 Jan 2023 00:06:32 +0100 Subject: [PATCH 13/60] Add switch to enable secret mapping to container autodiscovery config #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../kubernetes/api/v1/autodiscoveryconfig_types.go | 7 ++++--- auto-discovery/kubernetes/controllers/suite_test.go | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/auto-discovery/kubernetes/api/v1/autodiscoveryconfig_types.go b/auto-discovery/kubernetes/api/v1/autodiscoveryconfig_types.go index d6715389b8..fee99f9a98 100644 --- a/auto-discovery/kubernetes/api/v1/autodiscoveryconfig_types.go +++ b/auto-discovery/kubernetes/api/v1/autodiscoveryconfig_types.go @@ -30,9 +30,10 @@ type ServiceAutoDiscoveryConfig struct { } type ContainerAutoDiscoveryConfig struct { - Enabled bool `json:"enabled"` - PassiveReconcileInterval metav1.Duration `json:"passiveReconcileInterval"` - ScanConfig ScanConfig `json:"scanConfig"` + Enabled bool `json:"enabled"` + MapImagePullSecretsToEnvironmentVariables bool `json:"mapImagePullSecretsToEnvironmentVariables"` + PassiveReconcileInterval metav1.Duration `json:"passiveReconcileInterval"` + ScanConfig ScanConfig `json:"scanConfig"` } type ClusterConfig struct { diff --git a/auto-discovery/kubernetes/controllers/suite_test.go b/auto-discovery/kubernetes/controllers/suite_test.go index c8a44d2923..a4f97a3052 100644 --- a/auto-discovery/kubernetes/controllers/suite_test.go +++ b/auto-discovery/kubernetes/controllers/suite_test.go @@ -97,7 +97,8 @@ var _ = BeforeSuite(func() { }, }, ContainerAutoDiscoveryConfig: configv1.ContainerAutoDiscoveryConfig{ - PassiveReconcileInterval: metav1.Duration{Duration: 1 * time.Second}, + PassiveReconcileInterval: metav1.Duration{Duration: 1 * time.Second}, + MapImagePullSecretsToEnvironmentVariables: true, ScanConfig: configv1.ScanConfig{ RepeatInterval: metav1.Duration{Duration: time.Hour}, Annotations: map[string]string{"testAnnotation": "{{ .Namespace.Name }}"}, From cf3cbc80d88902878323d5617e0fdf5f9c937307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sun, 22 Jan 2023 00:48:57 +0100 Subject: [PATCH 14/60] Add imagepullsecret logic to container autodiscovery #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../controllers/container_scan_controller.go | 89 ++++++++++++++++++- .../container_scan_controller_test.go | 3 +- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index 504f60a82f..dab7a50002 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -177,11 +177,22 @@ func getScanName(imageID string) string { } func (r *ContainerScanReconciler) createScheduledScans(ctx context.Context, pod corev1.Pod, imageIDs []string) { + secretsDefined, secrets := checkForImagePullSecrets(pod) for _, imageID := range imageIDs { - r.createScheduledScan(ctx, pod, imageID) + if secretsDefined { + r.createScheduledScanWithImagePullSecrets(ctx, pod, imageID, secrets) + } else { + r.createScheduledScan(ctx, pod, imageID) + } } } +func checkForImagePullSecrets(pod corev1.Pod) (bool, []corev1.LocalObjectReference) { + imagePullSecrets := pod.Spec.ImagePullSecrets + secretsDefined := len(imagePullSecrets) > 0 + return secretsDefined, imagePullSecrets +} + func (r *ContainerScanReconciler) createScheduledScan(ctx context.Context, pod corev1.Pod, imageID string) { namespace := r.getNamespace(ctx, pod) @@ -194,6 +205,20 @@ func (r *ContainerScanReconciler) createScheduledScan(ctx context.Context, pod c r.Log.V(6).Info("Created scheduled scan", "pod", pod.Name, "namespace", pod.Namespace) } } + +func (r *ContainerScanReconciler) createScheduledScanWithImagePullSecrets(ctx context.Context, pod corev1.Pod, imageID string, secrets []corev1.LocalObjectReference) { + namespace := r.getNamespace(ctx, pod) + + newScheduledScan := r.generateScanWithVolumeMounts(pod, imageID, namespace, secrets) + err := r.Client.Create(ctx, &newScheduledScan) + + if err != nil { + r.Log.Error(err, "Failed to create scheduled scan", "scan", newScheduledScan, "namespace", namespace) + } else { + r.Log.V(6).Info("Created scheduled scan", "pod", pod.Name, "namespace", pod.Namespace) + } +} + func (r *ContainerScanReconciler) getNamespace(ctx context.Context, pod corev1.Pod) corev1.Namespace { var result corev1.Namespace err := r.Client.Get(ctx, types.NamespacedName{Name: pod.Namespace, Namespace: ""}, &result) @@ -228,6 +253,68 @@ func (r *ContainerScanReconciler) generateScan(pod corev1.Pod, imageID string, n return newScheduledScan } +func (r *ContainerScanReconciler) generateScanWithVolumeMounts(pod corev1.Pod, imageID string, namespace corev1.Namespace, secrets []corev1.LocalObjectReference) executionv1.ScheduledScan { + scanConfig := r.Config.ContainerAutoDiscoveryConfig.ScanConfig + templateArgs := ContainerAutoDiscoveryTemplateArgs{ + Config: r.Config, + ScanConfig: scanConfig, + Cluster: r.Config.Cluster, + Target: pod.ObjectMeta, + Namespace: namespace, + ImageID: imageID, + } + + scanConfig.Volumes = append(scanConfig.Volumes) + extraVolumes, extraMounts := getVolumesForSecrets(secrets) + scanConfig.Volumes = append(scanConfig.Volumes, extraVolumes...) + scanConfig.VolumeMounts = append(scanConfig.VolumeMounts, extraMounts...) + + scanSpec := util.GenerateScanSpec(scanConfig, templateArgs) + scanSpec.ScanSpec.InitContainers = append(scanSpec.ScanSpec.InitContainers, getSecretExtractionInitContainer()) + + newScheduledScan := executionv1.ScheduledScan{ + ObjectMeta: metav1.ObjectMeta{ + Name: getScanName(imageID), + Namespace: pod.Namespace, + Annotations: getScanAnnotations(scanConfig, templateArgs), + Labels: getScanLabels(scanConfig, templateArgs), + }, + Spec: scanSpec, + } + return newScheduledScan +} + +func getVolumesForSecrets(secrets []corev1.LocalObjectReference) ([]corev1.Volume, []corev1.VolumeMount) { + var volumes []corev1.Volume + var mounts []corev1.VolumeMount + for _, secret := range secrets { + volume := corev1.Volume{ + Name: secret.Name + "-volume", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: secret.Name, + }, + }, + } + + mount := corev1.VolumeMount{ + Name: secret.Name + "-volume", + MountPath: "/secrets/" + secret.Name, + } + + volumes = append(volumes, volume) + mounts = append(mounts, mount) + } + return volumes, mounts +} + +func getSecretExtractionInitContainer() corev1.Container { + return corev1.Container{ + Name: "secret-extraction-to-env", + Image: "docker.io/securecodebox/auto-discovery-secret-extraction-container", + } +} + func (r *ContainerScanReconciler) checkForScanType(ctx context.Context, pod corev1.Pod) bool { namespace := r.getNamespace(ctx, pod) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index ebbe5bf44a..1408883ba3 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -153,7 +153,8 @@ var _ = Describe("ContainerScan controller", func() { }, []corev1.VolumeMount{ { - Name: "test-pull-secret-volume", + Name: "test-pull-secret-volume", + MountPath: "/secrets/test-pull-secret", }, }, } From c331c4715983b47222f3d58a6395c0de3eb42275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sun, 22 Jan 2023 00:57:51 +0100 Subject: [PATCH 15/60] Fix docker export name of secretextractioncontainer in ci #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bb1b42e24b..5a5ec817a9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -143,7 +143,7 @@ jobs: - name: Upload Image As Artifact uses: actions/upload-artifact@v2 with: - name: auto-discovery-image + name: auto-discovery-secret-extraction-container-image path: ./auto-discovery/kubernetes/secretExtractionInitContainer/auto-discovery-secret-extraction-container.tar retention-days: 1 From 3850793b8cc3f98aa0e6e9476aa89513fc1d8260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sun, 22 Jan 2023 01:13:48 +0100 Subject: [PATCH 16/60] Add args to initcontainer in container autodiscovery #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../controllers/container_scan_controller.go | 13 +++++++++---- .../controllers/container_scan_controller_test.go | 3 ++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index dab7a50002..1e9912dabc 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -265,12 +265,12 @@ func (r *ContainerScanReconciler) generateScanWithVolumeMounts(pod corev1.Pod, i } scanConfig.Volumes = append(scanConfig.Volumes) - extraVolumes, extraMounts := getVolumesForSecrets(secrets) + extraVolumes, extraMounts := getVolumesForSecrets(secrets, imageID) scanConfig.Volumes = append(scanConfig.Volumes, extraVolumes...) scanConfig.VolumeMounts = append(scanConfig.VolumeMounts, extraMounts...) scanSpec := util.GenerateScanSpec(scanConfig, templateArgs) - scanSpec.ScanSpec.InitContainers = append(scanSpec.ScanSpec.InitContainers, getSecretExtractionInitContainer()) + scanSpec.ScanSpec.InitContainers = append(scanSpec.ScanSpec.InitContainers, getSecretExtractionInitContainer(imageID)) newScheduledScan := executionv1.ScheduledScan{ ObjectMeta: metav1.ObjectMeta{ @@ -284,7 +284,7 @@ func (r *ContainerScanReconciler) generateScanWithVolumeMounts(pod corev1.Pod, i return newScheduledScan } -func getVolumesForSecrets(secrets []corev1.LocalObjectReference) ([]corev1.Volume, []corev1.VolumeMount) { +func getVolumesForSecrets(secrets []corev1.LocalObjectReference, imageID string) ([]corev1.Volume, []corev1.VolumeMount) { var volumes []corev1.Volume var mounts []corev1.VolumeMount for _, secret := range secrets { @@ -308,10 +308,15 @@ func getVolumesForSecrets(secrets []corev1.LocalObjectReference) ([]corev1.Volum return volumes, mounts } -func getSecretExtractionInitContainer() corev1.Container { +func getSecretExtractionInitContainer(imageID string) corev1.Container { + temporarySecretName := "trivy-secret-" + getScanName(imageID) + //limit name to kubernetes max length + temporarySecretName = temporarySecretName[:62] + return corev1.Container{ Name: "secret-extraction-to-env", Image: "docker.io/securecodebox/auto-discovery-secret-extraction-container", + Args: []string{imageID, temporarySecretName}, } } diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index 1408883ba3..780aadc5aa 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -126,6 +126,7 @@ var _ = Describe("ContainerScan controller", func() { namespace := "container-autodiscovery-imagepullsecrets" ctx := context.Background() + fakeDeployment := map[string]string{"nginx": "0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31"} nginxScanName := "nginx-at-0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31" nginxScanName = nginxScanName[:62] nginxScanGoTemplate := scanGoTemplate{ @@ -139,6 +140,7 @@ var _ = Describe("ContainerScan controller", func() { { Name: "secret-extraction-to-env", Image: "docker.io/securecodebox/auto-discovery-secret-extraction-container", + Args: []string{"nginx@" + fakeDeployment["nginx"], ("trivy-secret" + nginxScanName)[:62]}, }, }, []corev1.Volume{ @@ -163,7 +165,6 @@ var _ = Describe("ContainerScan controller", func() { createNamespace(ctx, namespace) createScanType(ctx, namespace) - fakeDeployment := map[string]string{"nginx": "0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31"} imagePullSecrets := []corev1.LocalObjectReference{ { Name: "test-pull-secret", From 4b78ac73258a485567541658d772a75319bbcfc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sun, 22 Jan 2023 01:20:22 +0100 Subject: [PATCH 17/60] Move secret volume mounts to initcontainer instead of scan #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../controllers/container_scan_controller.go | 12 ++++++------ .../controllers/container_scan_controller_test.go | 13 +++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index 1e9912dabc..a92dd8e356 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -267,10 +267,9 @@ func (r *ContainerScanReconciler) generateScanWithVolumeMounts(pod corev1.Pod, i scanConfig.Volumes = append(scanConfig.Volumes) extraVolumes, extraMounts := getVolumesForSecrets(secrets, imageID) scanConfig.Volumes = append(scanConfig.Volumes, extraVolumes...) - scanConfig.VolumeMounts = append(scanConfig.VolumeMounts, extraMounts...) scanSpec := util.GenerateScanSpec(scanConfig, templateArgs) - scanSpec.ScanSpec.InitContainers = append(scanSpec.ScanSpec.InitContainers, getSecretExtractionInitContainer(imageID)) + scanSpec.ScanSpec.InitContainers = append(scanSpec.ScanSpec.InitContainers, getSecretExtractionInitContainer(imageID, extraMounts)) newScheduledScan := executionv1.ScheduledScan{ ObjectMeta: metav1.ObjectMeta{ @@ -308,15 +307,16 @@ func getVolumesForSecrets(secrets []corev1.LocalObjectReference, imageID string) return volumes, mounts } -func getSecretExtractionInitContainer(imageID string) corev1.Container { +func getSecretExtractionInitContainer(imageID string, volumeMounts []corev1.VolumeMount) corev1.Container { temporarySecretName := "trivy-secret-" + getScanName(imageID) //limit name to kubernetes max length temporarySecretName = temporarySecretName[:62] return corev1.Container{ - Name: "secret-extraction-to-env", - Image: "docker.io/securecodebox/auto-discovery-secret-extraction-container", - Args: []string{imageID, temporarySecretName}, + Name: "secret-extraction-to-env", + Image: "docker.io/securecodebox/auto-discovery-secret-extraction-container", + Args: []string{imageID, temporarySecretName}, + VolumeMounts: volumeMounts, } } diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index 780aadc5aa..f2b0ce2b0b 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -141,6 +141,12 @@ var _ = Describe("ContainerScan controller", func() { Name: "secret-extraction-to-env", Image: "docker.io/securecodebox/auto-discovery-secret-extraction-container", Args: []string{"nginx@" + fakeDeployment["nginx"], ("trivy-secret" + nginxScanName)[:62]}, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "test-pull-secret-volume", + MountPath: "/secrets/test-pull-secret", + }, + }, }, }, []corev1.Volume{ @@ -153,12 +159,7 @@ var _ = Describe("ContainerScan controller", func() { }, }, }, - []corev1.VolumeMount{ - { - Name: "test-pull-secret-volume", - MountPath: "/secrets/test-pull-secret", - }, - }, + nil, } It("Should create a trivy scan with the secretExtractionInitContainer", func() { From 459af289d8ca08bf64d1bb31fc030ce80aa92509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sun, 22 Jan 2023 01:39:22 +0100 Subject: [PATCH 18/60] Add envvars with temporary secret to trivy container #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../controllers/container_scan_controller.go | 38 +++++++++++++++++-- .../container_scan_controller_test.go | 35 ++++++++++++++--- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index a92dd8e356..e8c1812534 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -271,6 +271,8 @@ func (r *ContainerScanReconciler) generateScanWithVolumeMounts(pod corev1.Pod, i scanSpec := util.GenerateScanSpec(scanConfig, templateArgs) scanSpec.ScanSpec.InitContainers = append(scanSpec.ScanSpec.InitContainers, getSecretExtractionInitContainer(imageID, extraMounts)) + scanSpec.ScanSpec.Env = append(scanSpec.ScanSpec.Env, getTemporarySecretEnvironmentVariableMount(imageID)...) + newScheduledScan := executionv1.ScheduledScan{ ObjectMeta: metav1.ObjectMeta{ Name: getScanName(imageID), @@ -308,9 +310,7 @@ func getVolumesForSecrets(secrets []corev1.LocalObjectReference, imageID string) } func getSecretExtractionInitContainer(imageID string, volumeMounts []corev1.VolumeMount) corev1.Container { - temporarySecretName := "trivy-secret-" + getScanName(imageID) - //limit name to kubernetes max length - temporarySecretName = temporarySecretName[:62] + temporarySecretName := getTemporarySecretName(imageID) return corev1.Container{ Name: "secret-extraction-to-env", @@ -320,6 +320,38 @@ func getSecretExtractionInitContainer(imageID string, volumeMounts []corev1.Volu } } +func getTemporarySecretName(imageID string) string { + //limit name to kubernetes max length + return ("trivy-secret-" + getScanName(imageID))[:62] +} + +func getTemporarySecretEnvironmentVariableMount(imageID string) []corev1.EnvVar { + return []corev1.EnvVar{ + { + Name: "username", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: getTemporarySecretName(imageID), + }, + Key: "username", + }, + }, + }, + { + Name: "password", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: getTemporarySecretName(imageID), + }, + Key: "password", + }, + }, + }, + } +} + func (r *ContainerScanReconciler) checkForScanType(ctx context.Context, pod corev1.Pod) bool { namespace := r.getNamespace(ctx, pod) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index f2b0ce2b0b..72025e1209 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -140,7 +140,7 @@ var _ = Describe("ContainerScan controller", func() { { Name: "secret-extraction-to-env", Image: "docker.io/securecodebox/auto-discovery-secret-extraction-container", - Args: []string{"nginx@" + fakeDeployment["nginx"], ("trivy-secret" + nginxScanName)[:62]}, + Args: []string{"nginx@" + fakeDeployment["nginx"], ("trivy-secret-" + nginxScanName)[:62]}, VolumeMounts: []corev1.VolumeMount{ { Name: "test-pull-secret-volume", @@ -159,7 +159,30 @@ var _ = Describe("ContainerScan controller", func() { }, }, }, - nil, + []corev1.EnvVar{ + { + Name: "username", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: ("trivy-secret-" + nginxScanName)[:62], + }, + Key: "username", + }, + }, + }, + { + Name: "password", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: ("trivy-secret-" + nginxScanName)[:62], + }, + Key: "password", + }, + }, + }, + }, } It("Should create a trivy scan with the secretExtractionInitContainer", func() { @@ -268,19 +291,19 @@ func checkScanGoTemplate(scan executionv1.ScheduledScan, scanSpec scanGoTemplate labels := scan.ObjectMeta.Labels parameters := scan.Spec.ScanSpec.Parameters volumes := scan.Spec.ScanSpec.Volumes - volumeMounts := scan.Spec.ScanSpec.VolumeMounts + envVars := scan.Spec.ScanSpec.Env annotationsCorrect := reflect.DeepEqual(annotations, scanSpec.Annotations) labelsCorrect := reflect.DeepEqual(labels, scanSpec.Labels) parametersCorrect := reflect.DeepEqual(parameters, scanSpec.Parameters) volumesCorrect := reflect.DeepEqual(volumes, scanSpec.Volumes) - volumeMountsCorrect := reflect.DeepEqual(volumeMounts, scanSpec.VolumeMounts) + envVarsCorrect := reflect.DeepEqual(envVars, scanSpec.EnvVars) Expect(annotationsCorrect).Should(BeTrue()) Expect(labelsCorrect).Should(BeTrue()) Expect(parametersCorrect).Should(BeTrue()) Expect(volumesCorrect).Should(BeTrue()) - Expect(volumeMountsCorrect).Should(BeTrue()) + Expect(envVarsCorrect).Should(BeTrue()) Expect(scan.Spec.ScanSpec.HookSelector.MatchExpressions).To(ContainElement( metav1.LabelSelectorRequirement{ Operator: metav1.LabelSelectorOpIn, @@ -304,5 +327,5 @@ type scanGoTemplate struct { Parameters []string InitContainers []corev1.Container Volumes []corev1.Volume - VolumeMounts []corev1.VolumeMount + EnvVars []corev1.EnvVar } From 3d29862568d998021c947b079221f09e9769dcf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sun, 22 Jan 2023 01:41:54 +0100 Subject: [PATCH 19/60] Rename temporary secret #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../kubernetes/controllers/container_scan_controller.go | 2 +- .../controllers/container_scan_controller_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index e8c1812534..c3bd4bfe4e 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -322,7 +322,7 @@ func getSecretExtractionInitContainer(imageID string, volumeMounts []corev1.Volu func getTemporarySecretName(imageID string) string { //limit name to kubernetes max length - return ("trivy-secret-" + getScanName(imageID))[:62] + return ("temporary-secret-" + getScanName(imageID))[:62] } func getTemporarySecretEnvironmentVariableMount(imageID string) []corev1.EnvVar { diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index 72025e1209..f12bf234ec 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -140,7 +140,7 @@ var _ = Describe("ContainerScan controller", func() { { Name: "secret-extraction-to-env", Image: "docker.io/securecodebox/auto-discovery-secret-extraction-container", - Args: []string{"nginx@" + fakeDeployment["nginx"], ("trivy-secret-" + nginxScanName)[:62]}, + Args: []string{"nginx@" + fakeDeployment["nginx"], ("temporary-secret-" + nginxScanName)[:62]}, VolumeMounts: []corev1.VolumeMount{ { Name: "test-pull-secret-volume", @@ -165,7 +165,7 @@ var _ = Describe("ContainerScan controller", func() { ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: ("trivy-secret-" + nginxScanName)[:62], + Name: ("temporary-secret-" + nginxScanName)[:62], }, Key: "username", }, @@ -176,7 +176,7 @@ var _ = Describe("ContainerScan controller", func() { ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: ("trivy-secret-" + nginxScanName)[:62], + Name: ("temporary-secret-" + nginxScanName)[:62], }, Key: "password", }, From 55f21c66d07cccd21885df0727d67dac39ddfa43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sun, 22 Jan 2023 01:44:24 +0100 Subject: [PATCH 20/60] Only map secrets to env when enabled #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../kubernetes/controllers/container_scan_controller.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index c3bd4bfe4e..694bfd11e5 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -178,8 +178,10 @@ func getScanName(imageID string) string { func (r *ContainerScanReconciler) createScheduledScans(ctx context.Context, pod corev1.Pod, imageIDs []string) { secretsDefined, secrets := checkForImagePullSecrets(pod) + mapSecretsToEnv := r.Config.ContainerAutoDiscoveryConfig.MapImagePullSecretsToEnvironmentVariables + for _, imageID := range imageIDs { - if secretsDefined { + if secretsDefined && mapSecretsToEnv { r.createScheduledScanWithImagePullSecrets(ctx, pod, imageID, secrets) } else { r.createScheduledScan(ctx, pod, imageID) From 4bb56fdb74804362bb79b62d8c8e8fd9c5a9df23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Sun, 22 Jan 2023 01:47:01 +0100 Subject: [PATCH 21/60] Remove redundant statement #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../kubernetes/controllers/container_scan_controller.go | 1 - 1 file changed, 1 deletion(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index 694bfd11e5..9892862fd0 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -266,7 +266,6 @@ func (r *ContainerScanReconciler) generateScanWithVolumeMounts(pod corev1.Pod, i ImageID: imageID, } - scanConfig.Volumes = append(scanConfig.Volumes) extraVolumes, extraMounts := getVolumesForSecrets(secrets, imageID) scanConfig.Volumes = append(scanConfig.Volumes, extraVolumes...) From 8200ec16931dc1414be399bebe7f59cc2ca44e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Tue, 24 Jan 2023 13:48:07 +0100 Subject: [PATCH 22/60] Rename secretextractioninitcontainer to pull-secret-extractor #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/ci.yaml | 4 ++-- .../kubernetes/controllers/container_scan_controller.go | 2 +- .../kubernetes/controllers/container_scan_controller_test.go | 2 +- .../Makefile | 0 .../dockerfile | 0 .../readme.md | 0 .../requirements.txt | 0 .../secret_extraction.py | 0 .../test_secret_extraction.py | 0 .../test_secrets/secret_1/.dockerconfigjson | 0 .../test_secrets/secret_1/not_a_docker_config_json | 0 .../test_secrets/secret_2/.dockerconfigjson | 0 12 files changed, 4 insertions(+), 4 deletions(-) rename auto-discovery/kubernetes/{secretExtractionInitContainer => pull-secret-extractor}/Makefile (100%) rename auto-discovery/kubernetes/{secretExtractionInitContainer => pull-secret-extractor}/dockerfile (100%) rename auto-discovery/kubernetes/{secretExtractionInitContainer => pull-secret-extractor}/readme.md (100%) rename auto-discovery/kubernetes/{secretExtractionInitContainer => pull-secret-extractor}/requirements.txt (100%) rename auto-discovery/kubernetes/{secretExtractionInitContainer => pull-secret-extractor}/secret_extraction.py (100%) rename auto-discovery/kubernetes/{secretExtractionInitContainer => pull-secret-extractor}/test_secret_extraction.py (100%) rename auto-discovery/kubernetes/{secretExtractionInitContainer => pull-secret-extractor}/test_secrets/secret_1/.dockerconfigjson (100%) rename auto-discovery/kubernetes/{secretExtractionInitContainer => pull-secret-extractor}/test_secrets/secret_1/not_a_docker_config_json (100%) rename auto-discovery/kubernetes/{secretExtractionInitContainer => pull-secret-extractor}/test_secrets/secret_2/.dockerconfigjson (100%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5a5ec817a9..649630e2f8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -143,8 +143,8 @@ jobs: - name: Upload Image As Artifact uses: actions/upload-artifact@v2 with: - name: auto-discovery-secret-extraction-container-image - path: ./auto-discovery/kubernetes/secretExtractionInitContainer/auto-discovery-secret-extraction-container.tar + name: auto-discovery-pull-secret-extractor + path: ./auto-discovery/kubernetes/secretExtractionInitContainer/auto-discovery-pull-secret-extractor.tar retention-days: 1 # ---- Build Stage | SDK Matrix ---- diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index 9892862fd0..d60e4f5fa8 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -315,7 +315,7 @@ func getSecretExtractionInitContainer(imageID string, volumeMounts []corev1.Volu return corev1.Container{ Name: "secret-extraction-to-env", - Image: "docker.io/securecodebox/auto-discovery-secret-extraction-container", + Image: "docker.io/securecodebox/auto-discovery-pull-secret-extractor", Args: []string{imageID, temporarySecretName}, VolumeMounts: volumeMounts, } diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index f12bf234ec..fbfdd7814e 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -139,7 +139,7 @@ var _ = Describe("ContainerScan controller", func() { []corev1.Container{ { Name: "secret-extraction-to-env", - Image: "docker.io/securecodebox/auto-discovery-secret-extraction-container", + Image: "docker.io/securecodebox/auto-discovery-pull-secret-extractor", Args: []string{"nginx@" + fakeDeployment["nginx"], ("temporary-secret-" + nginxScanName)[:62]}, VolumeMounts: []corev1.VolumeMount{ { diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/Makefile b/auto-discovery/kubernetes/pull-secret-extractor/Makefile similarity index 100% rename from auto-discovery/kubernetes/secretExtractionInitContainer/Makefile rename to auto-discovery/kubernetes/pull-secret-extractor/Makefile diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/dockerfile b/auto-discovery/kubernetes/pull-secret-extractor/dockerfile similarity index 100% rename from auto-discovery/kubernetes/secretExtractionInitContainer/dockerfile rename to auto-discovery/kubernetes/pull-secret-extractor/dockerfile diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/readme.md b/auto-discovery/kubernetes/pull-secret-extractor/readme.md similarity index 100% rename from auto-discovery/kubernetes/secretExtractionInitContainer/readme.md rename to auto-discovery/kubernetes/pull-secret-extractor/readme.md diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/requirements.txt b/auto-discovery/kubernetes/pull-secret-extractor/requirements.txt similarity index 100% rename from auto-discovery/kubernetes/secretExtractionInitContainer/requirements.txt rename to auto-discovery/kubernetes/pull-secret-extractor/requirements.txt diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py b/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py similarity index 100% rename from auto-discovery/kubernetes/secretExtractionInitContainer/secret_extraction.py rename to auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/test_secret_extraction.py b/auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py similarity index 100% rename from auto-discovery/kubernetes/secretExtractionInitContainer/test_secret_extraction.py rename to auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_1/.dockerconfigjson b/auto-discovery/kubernetes/pull-secret-extractor/test_secrets/secret_1/.dockerconfigjson similarity index 100% rename from auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_1/.dockerconfigjson rename to auto-discovery/kubernetes/pull-secret-extractor/test_secrets/secret_1/.dockerconfigjson diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_1/not_a_docker_config_json b/auto-discovery/kubernetes/pull-secret-extractor/test_secrets/secret_1/not_a_docker_config_json similarity index 100% rename from auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_1/not_a_docker_config_json rename to auto-discovery/kubernetes/pull-secret-extractor/test_secrets/secret_1/not_a_docker_config_json diff --git a/auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_2/.dockerconfigjson b/auto-discovery/kubernetes/pull-secret-extractor/test_secrets/secret_2/.dockerconfigjson similarity index 100% rename from auto-discovery/kubernetes/secretExtractionInitContainer/test_secrets/secret_2/.dockerconfigjson rename to auto-discovery/kubernetes/pull-secret-extractor/test_secrets/secret_2/.dockerconfigjson From efbd1640cb35d9f6868a154767280849a28ebd32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Tue, 24 Jan 2023 14:41:02 +0100 Subject: [PATCH 23/60] Read pod name and namespace from environment variables set by kubernets in pull secret extractor #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../pull-secret-extractor/secret_extraction.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py b/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py index 14a73508fa..e3447aef26 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py +++ b/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py @@ -2,6 +2,7 @@ import json import sys import base64 +import os from kubernetes import client, config @@ -99,16 +100,15 @@ def create_temporary_secret(username: str, password: str, secret_name: str): def get_pod_name() -> str: - """Reads pod name from /etc/hostname""" - with open('/etc/hostname') as file: - return file.readline().strip() + """Read pod name from environment variable called 'POD_NAME'. + Should be set like this: https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/""" + return os.environ['POD_NAME'] def get_namespace() -> str: - """Reads namespace of pod in which this container runs from - /var/run/secrets/kubernetes.io/serviceaccount/namespace""" - with open('/var/run/secrets/kubernetes.io/serviceaccount/namespace') as file: - return file.readline().strip() + """Read pod name from environment variable called 'NAMESPACE'. + Should be set like this: https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/""" + return os.environ['NAMESPACE'] if __name__ == '__main__': From eb625b954adb42930008f0ec2a1a72ad22715e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Tue, 24 Jan 2023 14:45:40 +0100 Subject: [PATCH 24/60] Add pod name and namespace varialbes to env vars of initcontainer using fieldrefs #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../controllers/container_scan_controller.go | 16 ++++++++++++++++ .../container_scan_controller_test.go | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index d60e4f5fa8..0d991654ba 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -350,6 +350,22 @@ func getTemporarySecretEnvironmentVariableMount(imageID string) []corev1.EnvVar }, }, }, + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }, } } diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index fbfdd7814e..cd0bd3ab5d 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -182,6 +182,22 @@ var _ = Describe("ContainerScan controller", func() { }, }, }, + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }, }, } From 5689edff9ea7edc899ba34febbfe32be628108aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Tue, 24 Jan 2023 14:53:36 +0100 Subject: [PATCH 25/60] Make env vars for pull-secret-extractor configurable #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../api/v1/autodiscoveryconfig_types.go | 14 ++++++++++---- .../kubernetes/api/v1/zz_generated.deepcopy.go | 16 ++++++++++++++++ .../controllers/container_scan_controller.go | 13 ++++++++----- .../kubernetes/controllers/suite_test.go | 8 ++++++-- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/auto-discovery/kubernetes/api/v1/autodiscoveryconfig_types.go b/auto-discovery/kubernetes/api/v1/autodiscoveryconfig_types.go index fee99f9a98..f8f5d9b729 100644 --- a/auto-discovery/kubernetes/api/v1/autodiscoveryconfig_types.go +++ b/auto-discovery/kubernetes/api/v1/autodiscoveryconfig_types.go @@ -30,10 +30,16 @@ type ServiceAutoDiscoveryConfig struct { } type ContainerAutoDiscoveryConfig struct { - Enabled bool `json:"enabled"` - MapImagePullSecretsToEnvironmentVariables bool `json:"mapImagePullSecretsToEnvironmentVariables"` - PassiveReconcileInterval metav1.Duration `json:"passiveReconcileInterval"` - ScanConfig ScanConfig `json:"scanConfig"` + Enabled bool `json:"enabled"` + ImagePullSecretConfig ImagePullSecretConfig `json:"imagePullSecretConfig"` + PassiveReconcileInterval metav1.Duration `json:"passiveReconcileInterval"` + ScanConfig ScanConfig `json:"scanConfig"` +} + +type ImagePullSecretConfig struct { + MapImagePullSecretsToEnvironmentVariables bool `json:"mapImagePullSecretsToEnvironmentVariables"` + UsernameEnvironmentVariableName string `json:"usernameEnvironmentVariableName"` + PasswordNameEnvironmentVariableName string `json:"passwordEnvironmentVariableName"` } type ClusterConfig struct { diff --git a/auto-discovery/kubernetes/api/v1/zz_generated.deepcopy.go b/auto-discovery/kubernetes/api/v1/zz_generated.deepcopy.go index da49765370..fe1b580a48 100644 --- a/auto-discovery/kubernetes/api/v1/zz_generated.deepcopy.go +++ b/auto-discovery/kubernetes/api/v1/zz_generated.deepcopy.go @@ -61,6 +61,7 @@ func (in *ClusterConfig) DeepCopy() *ClusterConfig { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ContainerAutoDiscoveryConfig) DeepCopyInto(out *ContainerAutoDiscoveryConfig) { *out = *in + out.ImagePullSecretConfig = in.ImagePullSecretConfig out.PassiveReconcileInterval = in.PassiveReconcileInterval in.ScanConfig.DeepCopyInto(&out.ScanConfig) } @@ -75,6 +76,21 @@ func (in *ContainerAutoDiscoveryConfig) DeepCopy() *ContainerAutoDiscoveryConfig return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImagePullSecretConfig) DeepCopyInto(out *ImagePullSecretConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImagePullSecretConfig. +func (in *ImagePullSecretConfig) DeepCopy() *ImagePullSecretConfig { + if in == nil { + return nil + } + out := new(ImagePullSecretConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceInclusionConfig) DeepCopyInto(out *ResourceInclusionConfig) { *out = *in diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index 0d991654ba..e7d92086e2 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -178,7 +178,7 @@ func getScanName(imageID string) string { func (r *ContainerScanReconciler) createScheduledScans(ctx context.Context, pod corev1.Pod, imageIDs []string) { secretsDefined, secrets := checkForImagePullSecrets(pod) - mapSecretsToEnv := r.Config.ContainerAutoDiscoveryConfig.MapImagePullSecretsToEnvironmentVariables + mapSecretsToEnv := r.Config.ContainerAutoDiscoveryConfig.ImagePullSecretConfig.MapImagePullSecretsToEnvironmentVariables for _, imageID := range imageIDs { if secretsDefined && mapSecretsToEnv { @@ -272,7 +272,10 @@ func (r *ContainerScanReconciler) generateScanWithVolumeMounts(pod corev1.Pod, i scanSpec := util.GenerateScanSpec(scanConfig, templateArgs) scanSpec.ScanSpec.InitContainers = append(scanSpec.ScanSpec.InitContainers, getSecretExtractionInitContainer(imageID, extraMounts)) - scanSpec.ScanSpec.Env = append(scanSpec.ScanSpec.Env, getTemporarySecretEnvironmentVariableMount(imageID)...) + pullSecretConfig := r.Config.ContainerAutoDiscoveryConfig.ImagePullSecretConfig + usernameEnvVarName := pullSecretConfig.UsernameEnvironmentVariableName + passwordEnvVarName := pullSecretConfig.PasswordNameEnvironmentVariableName + scanSpec.ScanSpec.Env = append(scanSpec.ScanSpec.Env, getTemporarySecretEnvironmentVariableMount(imageID, usernameEnvVarName, passwordEnvVarName)...) newScheduledScan := executionv1.ScheduledScan{ ObjectMeta: metav1.ObjectMeta{ @@ -326,10 +329,10 @@ func getTemporarySecretName(imageID string) string { return ("temporary-secret-" + getScanName(imageID))[:62] } -func getTemporarySecretEnvironmentVariableMount(imageID string) []corev1.EnvVar { +func getTemporarySecretEnvironmentVariableMount(imageID string, usernameEnvVarName string, passwordEnvVarName string) []corev1.EnvVar { return []corev1.EnvVar{ { - Name: "username", + Name: usernameEnvVarName, ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ @@ -340,7 +343,7 @@ func getTemporarySecretEnvironmentVariableMount(imageID string) []corev1.EnvVar }, }, { - Name: "password", + Name: passwordEnvVarName, ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ diff --git a/auto-discovery/kubernetes/controllers/suite_test.go b/auto-discovery/kubernetes/controllers/suite_test.go index a4f97a3052..40908d7c9b 100644 --- a/auto-discovery/kubernetes/controllers/suite_test.go +++ b/auto-discovery/kubernetes/controllers/suite_test.go @@ -97,8 +97,12 @@ var _ = BeforeSuite(func() { }, }, ContainerAutoDiscoveryConfig: configv1.ContainerAutoDiscoveryConfig{ - PassiveReconcileInterval: metav1.Duration{Duration: 1 * time.Second}, - MapImagePullSecretsToEnvironmentVariables: true, + PassiveReconcileInterval: metav1.Duration{Duration: 1 * time.Second}, + ImagePullSecretConfig: configv1.ImagePullSecretConfig{ + MapImagePullSecretsToEnvironmentVariables: true, + UsernameEnvironmentVariableName: "username", + PasswordNameEnvironmentVariableName: "password", + }, ScanConfig: configv1.ScanConfig{ RepeatInterval: metav1.Duration{Duration: time.Hour}, Annotations: map[string]string{"testAnnotation": "{{ .Namespace.Name }}"}, From 0956af214f90c169c3435d4499abec5ff4cb1820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Tue, 24 Jan 2023 15:00:33 +0100 Subject: [PATCH 26/60] Update values.yaml with imagepullsecret config #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- auto-discovery/kubernetes/auto-discovery-config.yaml | 6 +++++- auto-discovery/kubernetes/values.yaml | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/auto-discovery/kubernetes/auto-discovery-config.yaml b/auto-discovery/kubernetes/auto-discovery-config.yaml index 655c40b66e..786c221569 100644 --- a/auto-discovery/kubernetes/auto-discovery-config.yaml +++ b/auto-discovery/kubernetes/auto-discovery-config.yaml @@ -25,7 +25,7 @@ resourceInclusion: mode: enabled-per-namespace serviceAutoDiscovery: - enabled: true + enabled: false passiveReconcileInterval: 10s scanConfig: scanType: zap-advanced-scan @@ -66,3 +66,7 @@ containerAutoDiscovery: defectdojo.securecodebox.io/engagement-version: "{{if (index .Target.Labels `app.kubernetes.io/version`) }}{{ index .Target.Labels `app.kubernetes.io/version` }}{{end}}" # -- hookSelector allows to specify a LabelSelector with which the hooks are selected hookSelector: {} + imagePullSecretConfig: + mapImagePullSecretsToEnvironmentVariables: true + usernameEnvironmentVariableName: "TRIVY-USERNAME" + passwordEnvironmentVariableName: "TRIVY-PASSWORD" diff --git a/auto-discovery/kubernetes/values.yaml b/auto-discovery/kubernetes/values.yaml index 7f301db742..117d8627a3 100644 --- a/auto-discovery/kubernetes/values.yaml +++ b/auto-discovery/kubernetes/values.yaml @@ -84,6 +84,10 @@ config: # All values in the matchLabels map support templating. # MatchExpressions support templating in the `key` field and in every entry in the `values` list. If a value in the list renders to an empty string it is removed from the list. hookSelector: {} + imagePullSecretConfig: + mapImagePullSecretsToEnvironmentVariables: true + usernameEnvironmentVariableName: "TRIVY-USERNAME" + passwordEnvironmentVariableName: "TRIVY-PASSWORD" health: healthProbeBindAddress: :8081 From 0d539a4680014932f6606c53b38d61ce7acd4641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Tue, 24 Jan 2023 15:19:05 +0100 Subject: [PATCH 27/60] Fix bug where secret mapping toggle got ignored #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../kubernetes/controllers/container_scan_controller.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index e7d92086e2..583d539a80 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -181,8 +181,10 @@ func (r *ContainerScanReconciler) createScheduledScans(ctx context.Context, pod mapSecretsToEnv := r.Config.ContainerAutoDiscoveryConfig.ImagePullSecretConfig.MapImagePullSecretsToEnvironmentVariables for _, imageID := range imageIDs { - if secretsDefined && mapSecretsToEnv { - r.createScheduledScanWithImagePullSecrets(ctx, pod, imageID, secrets) + if secretsDefined { + if mapSecretsToEnv { + r.createScheduledScanWithImagePullSecrets(ctx, pod, imageID, secrets) + } } else { r.createScheduledScan(ctx, pod, imageID) } From 8af2928a3a88e950e93d1462d2947ada107a0640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Tue, 24 Jan 2023 15:29:38 +0100 Subject: [PATCH 28/60] Move env vars for pull-secret-extractor to correct location #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../controllers/container_scan_controller.go | 34 ++++++++++--------- .../container_scan_controller_test.go | 34 ++++++++++--------- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index 583d539a80..4132d47423 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -323,6 +323,24 @@ func getSecretExtractionInitContainer(imageID string, volumeMounts []corev1.Volu Image: "docker.io/securecodebox/auto-discovery-pull-secret-extractor", Args: []string{imageID, temporarySecretName}, VolumeMounts: volumeMounts, + Env: []corev1.EnvVar{ + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }, + }, } } @@ -355,22 +373,6 @@ func getTemporarySecretEnvironmentVariableMount(imageID string, usernameEnvVarNa }, }, }, - { - Name: "POD_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.name", - }, - }, - }, - { - Name: "NAMESPACE", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.namespace", - }, - }, - }, } } diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index cd0bd3ab5d..4ab9dfe16c 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -147,6 +147,24 @@ var _ = Describe("ContainerScan controller", func() { MountPath: "/secrets/test-pull-secret", }, }, + Env: []corev1.EnvVar{ + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }, + }, }, }, []corev1.Volume{ @@ -182,22 +200,6 @@ var _ = Describe("ContainerScan controller", func() { }, }, }, - { - Name: "POD_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.name", - }, - }, - }, - { - Name: "NAMESPACE", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.namespace", - }, - }, - }, }, } From 8fec7b355fef8307438632936da79cb81a1ebd9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Tue, 24 Jan 2023 15:31:03 +0100 Subject: [PATCH 29/60] Enable service autodiscovery in config.yaml that was disabled by mistake #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- auto-discovery/kubernetes/auto-discovery-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto-discovery/kubernetes/auto-discovery-config.yaml b/auto-discovery/kubernetes/auto-discovery-config.yaml index 786c221569..10c79156df 100644 --- a/auto-discovery/kubernetes/auto-discovery-config.yaml +++ b/auto-discovery/kubernetes/auto-discovery-config.yaml @@ -25,7 +25,7 @@ resourceInclusion: mode: enabled-per-namespace serviceAutoDiscovery: - enabled: false + enabled: true passiveReconcileInterval: 10s scanConfig: scanType: zap-advanced-scan From 33ecd37437b89ffe6f2eafaa0cfd76a33390abe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Tue, 24 Jan 2023 15:50:10 +0100 Subject: [PATCH 30/60] Fix command of pull secret extractor #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../kubernetes/controllers/container_scan_controller.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index 4132d47423..164b2f87eb 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -321,7 +321,8 @@ func getSecretExtractionInitContainer(imageID string, volumeMounts []corev1.Volu return corev1.Container{ Name: "secret-extraction-to-env", Image: "docker.io/securecodebox/auto-discovery-pull-secret-extractor", - Args: []string{imageID, temporarySecretName}, + Command: []string{"python"}, + Args: []string{"secret_extraction.py", imageID, temporarySecretName}, VolumeMounts: volumeMounts, Env: []corev1.EnvVar{ { From 9ddf33902d97ed198d1ee6f73238b8034149401d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 25 Jan 2023 15:58:40 +0100 Subject: [PATCH 31/60] Add title to ci workflow #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 649630e2f8..1f896679b3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -124,6 +124,7 @@ jobs: path: ./auto-discovery/kubernetes/auto-discovery-kubernetes.tar retention-days: 1 + # ---- Build Stage | AutoDiscovery | PullSecretExtractor ---- auto-discovery-kubernetes-secret-extraction-container: name: "Autodiscovery | Kubernetes | SecretExtractionInitContainer" runs-on: ubuntu-20.04 From 5dcdbfdd3ef70984f4929ed9cac6acdf7be3a88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 25 Jan 2023 17:48:48 +0100 Subject: [PATCH 32/60] Add integration test #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../pull-secret-extractor/.dockerignore | 2 + .../kubernetes/pull-secret-extractor/Makefile | 20 +- .../integration-test/package-lock.json | 1552 +++++++++++++++++ .../integration-test/package.json | 13 + .../pull-secret-extraction.test.js | 20 + .../integration-test/test-pod.sh | 82 + 6 files changed, 1687 insertions(+), 2 deletions(-) create mode 100644 auto-discovery/kubernetes/pull-secret-extractor/.dockerignore create mode 100644 auto-discovery/kubernetes/pull-secret-extractor/integration-test/package-lock.json create mode 100644 auto-discovery/kubernetes/pull-secret-extractor/integration-test/package.json create mode 100644 auto-discovery/kubernetes/pull-secret-extractor/integration-test/pull-secret-extraction.test.js create mode 100755 auto-discovery/kubernetes/pull-secret-extractor/integration-test/test-pod.sh diff --git a/auto-discovery/kubernetes/pull-secret-extractor/.dockerignore b/auto-discovery/kubernetes/pull-secret-extractor/.dockerignore new file mode 100644 index 0000000000..1c9d294316 --- /dev/null +++ b/auto-discovery/kubernetes/pull-secret-extractor/.dockerignore @@ -0,0 +1,2 @@ +integration-test/* +venv/* \ No newline at end of file diff --git a/auto-discovery/kubernetes/pull-secret-extractor/Makefile b/auto-discovery/kubernetes/pull-secret-extractor/Makefile index fb121b6074..a82afec81f 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/Makefile +++ b/auto-discovery/kubernetes/pull-secret-extractor/Makefile @@ -13,6 +13,8 @@ IMG ?= auto-discovery-secret-extraction-container # Tag used for the image IMG_TAG ?= sha-$$(git rev-parse --short HEAD) +JEST_VERSION ?= 27.0.6 + ##@ General @@ -35,14 +37,28 @@ help: ## Display this help. ##@ Development .PHONY: test -test: +test: unit-test integration-test + +.PHONY: unit-test +unit-test: $(PYTHON) -m unittest discover +.PHONY: integration-test +integration-test: docker-build docker-export kind-import + @echo ".: 🩺 Starting integration test in kind namespace 'integration-tests'." + kubectl delete namespace integration-test --wait || true + kubectl create namespace integration-test + + ./integration-test/test-pod.sh ${IMG_NS}/${IMG}:${IMG_TAG} + kubectl wait --for=condition=ready --timeout=60s -n integration-test pod/init-container-test + + cd integration-test && npx --yes --package jest@$(JEST_VERSION) jest --ci --colors --coverage --passWithNoTests + ##@ Build .PHONY: docker-build -docker-build: test ## Build docker image with the manager. +docker-build: ## Build docker image with the manager. @echo ".: ⚙️ Build Container Images" docker build -t ${IMG_NS}/${IMG}:${IMG_TAG} . diff --git a/auto-discovery/kubernetes/pull-secret-extractor/integration-test/package-lock.json b/auto-discovery/kubernetes/pull-secret-extractor/integration-test/package-lock.json new file mode 100644 index 0000000000..2aad773833 --- /dev/null +++ b/auto-discovery/kubernetes/pull-secret-extractor/integration-test/package-lock.json @@ -0,0 +1,1552 @@ +{ + "name": "securecodebox-pull-secret-extractor-test", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "securecodebox-pull-secret-extractor-test", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@kubernetes/client-node": "^0.18.1" + } + }, + "node_modules/@kubernetes/client-node": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.18.1.tgz", + "integrity": "sha512-F3JiK9iZnbh81O/da1tD0h8fQMi/MDttWc/JydyUVnjPEom55wVfnpl4zQ/sWD4uKB8FlxYRPiLwV2ZXB+xPKw==", + "dependencies": { + "@types/js-yaml": "^4.0.1", + "@types/node": "^18.11.17", + "@types/request": "^2.47.1", + "@types/ws": "^8.5.3", + "byline": "^5.0.0", + "isomorphic-ws": "^5.0.0", + "js-yaml": "^4.1.0", + "jsonpath-plus": "^7.2.0", + "request": "^2.88.0", + "rfc4648": "^1.3.0", + "stream-buffers": "^3.0.2", + "tar": "^6.1.11", + "tmp-promise": "^3.0.2", + "tslib": "^2.4.1", + "underscore": "^1.13.6", + "ws": "^8.11.0" + }, + "optionalDependencies": { + "openid-client": "^5.3.0" + } + }, + "node_modules/@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" + }, + "node_modules/@types/js-yaml": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", + "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" + }, + "node_modules/@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" + }, + "node_modules/@types/request": { + "version": "2.48.8", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz", + "integrity": "sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ==", + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", + "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==" + }, + "node_modules/@types/ws": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, + "node_modules/jose": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.2.tgz", + "integrity": "sha512-njj0VL2TsIxCtgzhO+9RRobBvws4oYyCM8TpvoUQwl/MbIM3NFJRR9+e6x0sS5xXaP1t6OCBkaBME98OV9zU5A==", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "node_modules/jsonpath-plus": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz", + "integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", + "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/oidc-token-hash": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz", + "integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==", + "optional": true, + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/openid-client": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.3.2.tgz", + "integrity": "sha512-nXXt+cna0XHOw+WqjMZOmuXw/YZEMwfWD2lD7tCsFtsBjMQGVXA+NZABA3upYBET1suhIsmfd7GnxG4jCAnvYQ==", + "optional": true, + "dependencies": { + "jose": "^4.10.0", + "lru-cache": "^6.0.0", + "object-hash": "^2.0.1", + "oidc-token-hash": "^5.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/rfc4648": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/rfc4648/-/rfc4648-1.5.2.tgz", + "integrity": "sha512-tLOizhR6YGovrEBLatX1sdcuhoSCXddw3mqNVAcKxGJ+J0hFeJ+SjeWCv5UPA/WU3YzWPPuCVYgXBKZUPGpKtg==" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-buffers": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", + "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/tar": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^4.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + }, + "dependencies": { + "@kubernetes/client-node": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.18.1.tgz", + "integrity": "sha512-F3JiK9iZnbh81O/da1tD0h8fQMi/MDttWc/JydyUVnjPEom55wVfnpl4zQ/sWD4uKB8FlxYRPiLwV2ZXB+xPKw==", + "requires": { + "@types/js-yaml": "^4.0.1", + "@types/node": "^18.11.17", + "@types/request": "^2.47.1", + "@types/ws": "^8.5.3", + "byline": "^5.0.0", + "isomorphic-ws": "^5.0.0", + "js-yaml": "^4.1.0", + "jsonpath-plus": "^7.2.0", + "openid-client": "^5.3.0", + "request": "^2.88.0", + "rfc4648": "^1.3.0", + "stream-buffers": "^3.0.2", + "tar": "^6.1.11", + "tmp-promise": "^3.0.2", + "tslib": "^2.4.1", + "underscore": "^1.13.6", + "ws": "^8.11.0" + } + }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" + }, + "@types/js-yaml": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", + "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" + }, + "@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" + }, + "@types/request": { + "version": "2.48.8", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz", + "integrity": "sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ==", + "requires": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "@types/tough-cookie": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", + "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==" + }, + "@types/ws": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "requires": { + "@types/node": "*" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" + }, + "aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "requires": {} + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, + "jose": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.2.tgz", + "integrity": "sha512-njj0VL2TsIxCtgzhO+9RRobBvws4oYyCM8TpvoUQwl/MbIM3NFJRR9+e6x0sS5xXaP1t6OCBkaBME98OV9zU5A==", + "optional": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "jsonpath-plus": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz", + "integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==" + }, + "jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", + "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "optional": true + }, + "oidc-token-hash": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz", + "integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==", + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "openid-client": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.3.2.tgz", + "integrity": "sha512-nXXt+cna0XHOw+WqjMZOmuXw/YZEMwfWD2lD7tCsFtsBjMQGVXA+NZABA3upYBET1suhIsmfd7GnxG4jCAnvYQ==", + "optional": true, + "requires": { + "jose": "^4.10.0", + "lru-cache": "^6.0.0", + "object-hash": "^2.0.1", + "oidc-token-hash": "^5.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" + }, + "qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, + "rfc4648": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/rfc4648/-/rfc4648-1.5.2.tgz", + "integrity": "sha512-tLOizhR6YGovrEBLatX1sdcuhoSCXddw3mqNVAcKxGJ+J0hFeJ+SjeWCv5UPA/WU3YzWPPuCVYgXBKZUPGpKtg==" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stream-buffers": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", + "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==" + }, + "tar": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^4.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "requires": { + "rimraf": "^3.0.0" + } + }, + "tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "requires": { + "tmp": "^0.2.0" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, + "underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "ws": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "requires": {} + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/auto-discovery/kubernetes/pull-secret-extractor/integration-test/package.json b/auto-discovery/kubernetes/pull-secret-extractor/integration-test/package.json new file mode 100644 index 0000000000..a6fd956474 --- /dev/null +++ b/auto-discovery/kubernetes/pull-secret-extractor/integration-test/package.json @@ -0,0 +1,13 @@ +{ + "name": "securecodebox-pull-secret-extractor-test", + "version": "1.0.0", + "description": "", + "scripts": { + "test": "pull-secret-extraction.test.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@kubernetes/client-node": "^0.18.1" + } +} diff --git a/auto-discovery/kubernetes/pull-secret-extractor/integration-test/pull-secret-extraction.test.js b/auto-discovery/kubernetes/pull-secret-extractor/integration-test/pull-secret-extraction.test.js new file mode 100644 index 0000000000..b850ed509a --- /dev/null +++ b/auto-discovery/kubernetes/pull-secret-extractor/integration-test/pull-secret-extraction.test.js @@ -0,0 +1,20 @@ +const k8s = require("@kubernetes/client-node"); + +const kc = new k8s.KubeConfig(); +kc.loadFromDefault(); + +const k8sPodsApi = kc.makeApiClient(k8s.CoreV1Api); + +test( + "Test if initcontainer creates correct secrets", + async () => { + let secret; + try { + secret = await k8sPodsApi.readNamespacedSecret("test-secret", "integration-test"); + } finally { + expect(secret).toBeDefined(); + } + + }, + 60*1000 +) \ No newline at end of file diff --git a/auto-discovery/kubernetes/pull-secret-extractor/integration-test/test-pod.sh b/auto-discovery/kubernetes/pull-secret-extractor/integration-test/test-pod.sh new file mode 100755 index 0000000000..f240e246ef --- /dev/null +++ b/auto-discovery/kubernetes/pull-secret-extractor/integration-test/test-pod.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +cat < Date: Wed, 25 Jan 2023 17:55:34 +0100 Subject: [PATCH 33/60] Add pull secret extractor integration test to ci #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/ci.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1f896679b3..e82e339769 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -148,6 +148,20 @@ jobs: path: ./auto-discovery/kubernetes/secretExtractionInitContainer/auto-discovery-pull-secret-extractor.tar retention-days: 1 + - name: "Start kind cluster" + run: | + kind version + kind create cluster --wait 3m --image kindest/node:v1.24.0@sha256:0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8e + + - name: "Inspect kind cluster" + run: | + kubectl config current-context + kubectl get node + + - name: "Run integration tests" + run: | + make integration-test + # ---- Build Stage | SDK Matrix ---- sdk: From 8bd435613500d524ee55d1815d03307774ca023b Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Tue, 31 Jan 2023 13:28:32 +0100 Subject: [PATCH 34/60] Add temporarily hardcoded rbac configs --- scanners/trivy/templates/trivy-rbac.yaml | 49 +++++++++++++++++++ scanners/trivy/templates/trivy-scan-type.yaml | 1 + 2 files changed, 50 insertions(+) create mode 100644 scanners/trivy/templates/trivy-rbac.yaml diff --git a/scanners/trivy/templates/trivy-rbac.yaml b/scanners/trivy/templates/trivy-rbac.yaml new file mode 100644 index 0000000000..eac997f8db --- /dev/null +++ b/scanners/trivy/templates/trivy-rbac.yaml @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: the secureCodeBox authors +# +# SPDX-License-Identifier: Apache-2.0 + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: trivy-autodiscovery + namespace: {{.Release.Namespace}} +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: trivy-luker + namespace: {{.Release.Namespace}} +subjects: + - kind: ServiceAccount + name: trivy-autodiscovery + namespace: {{.Release.Namespace}} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: lurker +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: trivy-autodiscovery + namespace: {{.Release.Namespace}} +rules: + - apiGroups: [""] + resources: + - secrets + verbs: ["create"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: trivy-autodiscovery + namespace: {{.Release.Namespace}} +subjects: + - kind: ServiceAccount + name: trivy-autodiscovery + namespace: {{.Release.Namespace}} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: trivy-autodiscovery diff --git a/scanners/trivy/templates/trivy-scan-type.yaml b/scanners/trivy/templates/trivy-scan-type.yaml index b6e037e69c..a7388f1e13 100644 --- a/scanners/trivy/templates/trivy-scan-type.yaml +++ b/scanners/trivy/templates/trivy-scan-type.yaml @@ -32,6 +32,7 @@ spec: {{- end }} securityContext: {{- toYaml .Values.scanner.podSecurityContext | nindent 12 }} + serviceAccountName: trivy-autodiscovery containers: - name: trivy image: "{{ .Values.scanner.image.repository }}:{{ .Values.scanner.image.tag | default .Chart.AppVersion }}" From 16b225cdce11d442f804c569bcae0bdbf928f3e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Tue, 7 Feb 2023 09:21:48 +0100 Subject: [PATCH 35/60] Add specific trivy scan type for container autodiscovery with serviceaccount specified #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- scanners/trivy/templates/trivy-scan-type.yaml | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/scanners/trivy/templates/trivy-scan-type.yaml b/scanners/trivy/templates/trivy-scan-type.yaml index a7388f1e13..8d95610cfb 100644 --- a/scanners/trivy/templates/trivy-scan-type.yaml +++ b/scanners/trivy/templates/trivy-scan-type.yaml @@ -6,6 +6,63 @@ apiVersion: "execution.securecodebox.io/v1" kind: ScanType metadata: name: "trivy-image{{ .Values.scanner.nameAppend | default ""}}" +spec: + extractResults: + type: trivy-json + location: "/home/securecodebox/trivy-results.json" + jobTemplate: + spec: + {{- if .Values.scanner.ttlSecondsAfterFinished }} + ttlSecondsAfterFinished: {{ .Values.scanner.ttlSecondsAfterFinished }} + {{- end }} + backoffLimit: {{ .Values.scanner.backoffLimit }} + {{- if .Values.scanner.activeDeadlineSeconds }} + activeDeadlineSeconds: {{ .Values.scanner.activeDeadlineSeconds }} + {{- end }} + template: + spec: + restartPolicy: OnFailure + affinity: + {{- toYaml .Values.scanner.affinity | nindent 12 }} + tolerations: + {{- toYaml .Values.scanner.tolerations | nindent 12 }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 12 }} + {{- end }} + securityContext: + {{- toYaml .Values.scanner.podSecurityContext | nindent 12 }} + containers: + - name: trivy + image: "{{ .Values.scanner.image.repository }}:{{ .Values.scanner.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.scanner.image.pullPolicy }} + command: + - "trivy" + - "image" + # Suppress progress bar, as it pollutes non interactive terminal logs + - "--no-progress" + - "--format" + - "json" + - "--output" + - "/home/securecodebox/trivy-results.json" + resources: + {{- toYaml .Values.scanner.resources | nindent 16 }} + securityContext: + {{- toYaml .Values.scanner.securityContext | nindent 16 }} + env: + {{- toYaml .Values.scanner.env | nindent 16 }} + volumeMounts: + {{- toYaml .Values.scanner.extraVolumeMounts | nindent 16 }} + {{- if .Values.scanner.extraContainers }} + {{- toYaml .Values.scanner.extraContainers | nindent 12 }} + {{- end }} + volumes: + {{- toYaml .Values.scanner.extraVolumes | nindent 12 }} +--- +apiVersion: "execution.securecodebox.io/v1" +kind: ScanType +metadata: + name: "trivy-image-autodiscovery{{ .Values.scanner.nameAppend | default ""}}" spec: extractResults: type: trivy-json From 6f17072c1b1d17875bd7ff29ec01c1f9a5fe3a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Tue, 7 Feb 2023 09:29:20 +0100 Subject: [PATCH 36/60] Add new autodiscovery trivy scan type to autodiscovry config #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../kubernetes/auto-discovery-config.yaml | 2 +- auto-discovery/kubernetes/values.yaml | 2 +- operator/.vscode/launch.json | 23 +++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 operator/.vscode/launch.json diff --git a/auto-discovery/kubernetes/auto-discovery-config.yaml b/auto-discovery/kubernetes/auto-discovery-config.yaml index 10c79156df..5338f23222 100644 --- a/auto-discovery/kubernetes/auto-discovery-config.yaml +++ b/auto-discovery/kubernetes/auto-discovery-config.yaml @@ -50,7 +50,7 @@ containerAutoDiscovery: enabled: true passiveReconcileInterval: 10s scanConfig: - scanType: trivy-image + scanType: trivy-image-autodiscovery # -- parameters used for the scans created by the containerAutoDiscovery parameters: - "{{ .ImageID }}" diff --git a/auto-discovery/kubernetes/values.yaml b/auto-discovery/kubernetes/values.yaml index 117d8627a3..7d896620f6 100644 --- a/auto-discovery/kubernetes/values.yaml +++ b/auto-discovery/kubernetes/values.yaml @@ -59,7 +59,7 @@ config: # -- interval in which every pod is re-checked for updates, currently used to periodically check if the configured scantype is installed in the namespace of the pod passiveReconcileInterval: 1m scanConfig: - scanType: trivy-image + scanType: trivy-image-autodiscovery # -- parameters used for the scans created by the containerAutoDiscovery, all parameters support templating parameters: - "{{ .ImageID }}" diff --git a/operator/.vscode/launch.json b/operator/.vscode/launch.json new file mode 100644 index 0000000000..dc87a017da --- /dev/null +++ b/operator/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "main.go", + "env": { + "MINIO_ACCESS_KEY": "minioadmin", + "MINIO_SECRET_KEY": "minioadmin", + "S3_BUCKET": "test", + "S3_USE_SSL": "false", + "S3_ENDPOINT":"127.0.0.1:9000" + + } + } + ] +} \ No newline at end of file From df58863d823b5f194f4436ddfbe9ec68f7fd1444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Tue, 7 Feb 2023 10:58:30 +0100 Subject: [PATCH 37/60] Extract domain from docker image before checking secrets #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../pull-secret-extractor/docker_image.py | 29 +++++++++++++++++++ .../pull-secret-extractor/dockerfile | 1 + .../secret_extraction.py | 12 +++++--- .../test_docker_image.py | 25 ++++++++++++++++ .../test_secret_extraction.py | 2 +- 5 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 auto-discovery/kubernetes/pull-secret-extractor/docker_image.py create mode 100644 auto-discovery/kubernetes/pull-secret-extractor/test_docker_image.py diff --git a/auto-discovery/kubernetes/pull-secret-extractor/docker_image.py b/auto-discovery/kubernetes/pull-secret-extractor/docker_image.py new file mode 100644 index 0000000000..fd51559c23 --- /dev/null +++ b/auto-discovery/kubernetes/pull-secret-extractor/docker_image.py @@ -0,0 +1,29 @@ +legacyDefaultDomain = "index.docker.io" +defaultDomain = "docker.io" +officialRepoName = "library" +defaultTag = "latest" + + +def get_domain_from_docker_image(name: str) -> str: + """ + Extracts domain and image from a given docker image. Has the same defaulting behavior when it comes to docker.io image as containerd + Code adapted from https://github.com/containerd/containerd/blob/20de989afcd2fd4edc20e9b85312e49a8bbe152b/reference/docker/normalize.go#L102-L119 + :param name: docker image + :return: tuple container domain and image + """ + try: + i = name.index('/') + except ValueError: + i = -1 + + name_slice = name[:i] + if i == -1 or ':' not in name_slice and '.' not in name_slice and name_slice != 'localhost' and name_slice.lower() == name_slice: + domain = defaultDomain + else: + domain = name[:i] + + if domain == legacyDefaultDomain: + domain = defaultDomain + + return domain + diff --git a/auto-discovery/kubernetes/pull-secret-extractor/dockerfile b/auto-discovery/kubernetes/pull-secret-extractor/dockerfile index aa2a44b85f..c680a90cfd 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/dockerfile +++ b/auto-discovery/kubernetes/pull-secret-extractor/dockerfile @@ -2,5 +2,6 @@ FROM python:3 COPY requirements.txt . RUN pip install -r requirements.txt +COPY docker_image.py . COPY secret_extraction.py . CMD ["python", secret_extraction.py] diff --git a/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py b/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py index e3447aef26..a5318a0a95 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py +++ b/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py @@ -6,13 +6,17 @@ from kubernetes import client, config +from docker_image import get_domain_from_docker_image + def main(): image_id = sys.argv[1] temporary_secret_name = sys.argv[2] + domain = get_domain_from_docker_image(image_id) + raw_secrets = get_raw_secrets('/secrets') - correct_secret = get_correct_secret(image_id, raw_secrets) + correct_secret = get_correct_secret(domain, raw_secrets) username, password = get_user_and_password(correct_secret) create_temporary_secret(username, password, temporary_secret_name) @@ -30,15 +34,15 @@ def get_raw_secrets(base_path: str): return raw_secrets -def get_correct_secret(image_id: str, secrets) -> dict[str, str]: +def get_correct_secret(domain: str, secrets) -> dict[str, str]: """Iterates over given list of secrets to find the secret that machtes the URL in the given imageID - :param image_id: The imageID of which the correct secret needs to be identified + :param domain: The domain of the imageID of which the correct secret needs to be identified :param secrets: List of secrets :returns: Dict containing the secret matching the given imageID """ for secret in secrets: for url, data in secret['auths'].items(): - if url in image_id: + if url == domain: return data diff --git a/auto-discovery/kubernetes/pull-secret-extractor/test_docker_image.py b/auto-discovery/kubernetes/pull-secret-extractor/test_docker_image.py new file mode 100644 index 0000000000..38c5cd945e --- /dev/null +++ b/auto-discovery/kubernetes/pull-secret-extractor/test_docker_image.py @@ -0,0 +1,25 @@ +from unittest import TestCase + +from docker_image import get_domain_from_docker_image + + +class Test(TestCase): + def test_get_domain_from_docker_image_with_no_domain(self): + test_image = "foo/bar" + domain = get_domain_from_docker_image(test_image) + self.assertEqual("docker.io", domain) + + def test_get_domain_from_docker_image_with_dockerio_domain(self): + test_image = "docker.io/foo/bar" + domain = get_domain_from_docker_image(test_image) + self.assertEqual("docker.io", domain) + + def test_get_domain_from_docker_image_with_non_dockerio_domain(self): + test_image = "test.xyz/foo/bar" + domain = get_domain_from_docker_image(test_image) + self.assertEqual("test.xyz", domain) + + def test_get_domain_from_docker_image_with_single_world_image(self): + test_image = "ubuntu" + domain = get_domain_from_docker_image(test_image) + self.assertEqual("docker.io", domain) diff --git a/auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py b/auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py index 2d9e1fe023..b6c43796bf 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py +++ b/auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py @@ -28,7 +28,7 @@ def test_get_correct_secret(self): with open('test_secrets/secret_2/.dockerconfigjson') as file: secret_list.append(json.load(file)) - actual = get_correct_secret('localhost:5000:12345/some/image@654654', secret_list) + actual = get_correct_secret('localhost:5000', secret_list) # testuser:testpassword base64 encoded expected = {'auth': 'dGVzdHVzZXI6dGVzdHBhc3N3b3Jk'} From fdd8c5104db9368c0660d6659848199b63e8697b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Tue, 7 Feb 2023 11:04:53 +0100 Subject: [PATCH 38/60] Dont create temporary secret when no credentials are found #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../kubernetes/controllers/container_scan_controller.go | 3 +++ .../controllers/container_scan_controller_test.go | 4 ++++ .../kubernetes/pull-secret-extractor/secret_extraction.py | 8 ++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller.go b/auto-discovery/kubernetes/controllers/container_scan_controller.go index 164b2f87eb..71b3a8d3f7 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller.go @@ -351,11 +351,13 @@ func getTemporarySecretName(imageID string) string { } func getTemporarySecretEnvironmentVariableMount(imageID string, usernameEnvVarName string, passwordEnvVarName string) []corev1.EnvVar { + trueBool := true return []corev1.EnvVar{ { Name: usernameEnvVarName, ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ + Optional: &trueBool, LocalObjectReference: corev1.LocalObjectReference{ Name: getTemporarySecretName(imageID), }, @@ -367,6 +369,7 @@ func getTemporarySecretEnvironmentVariableMount(imageID string, usernameEnvVarNa Name: passwordEnvVarName, ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ + Optional: &trueBool, LocalObjectReference: corev1.LocalObjectReference{ Name: getTemporarySecretName(imageID), }, diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index 4ab9dfe16c..b5bfcee228 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -129,6 +129,8 @@ var _ = Describe("ContainerScan controller", func() { fakeDeployment := map[string]string{"nginx": "0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31"} nginxScanName := "nginx-at-0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31" nginxScanName = nginxScanName[:62] + + trueBool := true nginxScanGoTemplate := scanGoTemplate{ map[string]string{"testAnnotation": namespace}, map[string]string{ @@ -182,6 +184,7 @@ var _ = Describe("ContainerScan controller", func() { Name: "username", ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ + Optional: &trueBool, LocalObjectReference: corev1.LocalObjectReference{ Name: ("temporary-secret-" + nginxScanName)[:62], }, @@ -193,6 +196,7 @@ var _ = Describe("ContainerScan controller", func() { Name: "password", ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ + Optional: &trueBool, LocalObjectReference: corev1.LocalObjectReference{ Name: ("temporary-secret-" + nginxScanName)[:62], }, diff --git a/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py b/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py index a5318a0a95..357d2795ea 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py +++ b/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py @@ -17,8 +17,12 @@ def main(): raw_secrets = get_raw_secrets('/secrets') correct_secret = get_correct_secret(domain, raw_secrets) - username, password = get_user_and_password(correct_secret) - create_temporary_secret(username, password, temporary_secret_name) + + if correct_secret: + username, password = get_user_and_password(correct_secret) + create_temporary_secret(username, password, temporary_secret_name) + else: + print(f"No secrets found for domain 'f{domain}'") def get_raw_secrets(base_path: str): From d8f6c1f64f99250d9c81f3824247be60d8259857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 11:53:59 +0100 Subject: [PATCH 39/60] Add section in readme explaining how the initconainer works #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- auto-discovery/kubernetes/pull-secret-extractor/readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/auto-discovery/kubernetes/pull-secret-extractor/readme.md b/auto-discovery/kubernetes/pull-secret-extractor/readme.md index 39344379a6..9d88788e29 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/readme.md +++ b/auto-discovery/kubernetes/pull-secret-extractor/readme.md @@ -1,3 +1,6 @@ +## Usage +The auto-discovery-secret-extraction container should be used as an initContainer to enable Trivy (or other container scan tools) to scan images from private docker registries. The container expects the imageID for which it should find the corresponding secret and the name of the temporary secret as commandline arguments. The initContainer will then read secrets mounted as a volume under `/secrets` and check which secret belongs to the domain of the provided imageID. After the correct secret is identified it will create a temporary secret which will contain the credentials of the private registry of the provided imageID. The temporary secret will have an `ownerReference` to the pod in which this container is running in. This means that the temporary secret will be automatically removed when the scan of the pod is finished. + ## Running a local docker registry The easiest way to test the initContainer locally is to deploy a local registry using docker outside of the k8s cluster to be able to delete the cluster without recreating the registry every time. ### Creating a local registry with authentication From 59c3eb91e2b241c4dcef48382910b0908f486bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 13:28:09 +0100 Subject: [PATCH 40/60] Add decision to adr17 #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- docs/adr/adr_0017.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/adr/adr_0017.md b/docs/adr/adr_0017.md index ebaa3f31cb..b638d47c70 100644 --- a/docs/adr/adr_0017.md +++ b/docs/adr/adr_0017.md @@ -151,3 +151,10 @@ Pros: Cons: * Per default the container autodiscovery has read permissions on secrets. This can only be changed after installation by altering the clusterRole. + + +## Decision +It was decided to use an initContainer that reads the existing secrets through volumes and then it creates a temporary secret. That secret has a owner reference to the pod the initContainer is running in (which is the pod in which the scan is executed). This way the temporary secret gets automatically deleted after the scan is finished. + +## Consequences +The initContainer must be able to create secrets. This means that the pod in which the scan is executed must use a different serviceAccount than the standard lurker account. A new trivy scan type called `trivy-image-autodiscovery` was introduced that utilizes a serviceAccount with the same rights as the standard lurker serviceAccount plus the right to create secrets. \ No newline at end of file From ab78db57a3146e297b3a3ab158d40ed45cdba1eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 13:32:30 +0100 Subject: [PATCH 41/60] Fix path in ci yaml ##1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/ci.yaml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e82e339769..e3e8c9d998 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -131,21 +131,24 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 + - name: Test - working-directory: ./autodiscovery/kubernetes/secretExtractionInitContainer + working-directory: ./auto-discovery/kubernetes/pull-secret-extractor + run: make unit-test + - name: Build Container Image - working-directory: ./auto-discovery/kubernetes/secretExtractionInitContainer + working-directory: ./auto-discovery/kubernetes/pull-secret-extractor run: make docker-build - name: Export Container Image - working-directory: ./auto-discovery/kubernetes/secretExtractionInitContainer + working-directory: ./auto-discovery/kubernetes/pull-secret-extractor run: make docker-export - name: Upload Image As Artifact uses: actions/upload-artifact@v2 with: name: auto-discovery-pull-secret-extractor - path: ./auto-discovery/kubernetes/secretExtractionInitContainer/auto-discovery-pull-secret-extractor.tar + path: ./auto-discovery/kubernetes/pull-secret-extractor/auto-discovery-secret-extraction-container.tar retention-days: 1 - name: "Start kind cluster" From d00740ac2c396e46e01279f405e2376c83e71f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 13:36:37 +0100 Subject: [PATCH 42/60] Fix typo #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../kubernetes/controllers/container_scan_controller_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go index b5bfcee228..4a80238dbe 100644 --- a/auto-discovery/kubernetes/controllers/container_scan_controller_test.go +++ b/auto-discovery/kubernetes/controllers/container_scan_controller_test.go @@ -81,7 +81,7 @@ var _ = Describe("ContainerScan controller", func() { }) - It("Should create a single scheduledscan for every container with the same imageID in the deplyoment", func() { + It("Should create a single scheduledscan for every container with the same imageID in the deployment", func() { //install scantype, scans should be created now createScanType(ctx, namespace) From c9780362cb42a25ff8eb8333560db7e10fb8c4d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 13:41:37 +0100 Subject: [PATCH 43/60] Set correct python in ci unit test #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/ci.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e3e8c9d998..af1688f6dc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -132,7 +132,10 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Test + - name: Unit Test + uses: actions/setup-python@v4 + with: + python-version: '${{ env.PYTHON_VERSION }}' working-directory: ./auto-discovery/kubernetes/pull-secret-extractor run: make unit-test From bd03c562c8fe77ee3ae1c112f3d5a9b32dc17988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 14:06:51 +0100 Subject: [PATCH 44/60] Fix ci syntax #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/ci.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index af1688f6dc..3db2c82c1d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -132,10 +132,11 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Unit Test + - name: Setup Python Version uses: actions/setup-python@v4 with: python-version: '${{ env.PYTHON_VERSION }}' + - name: Unit Tests working-directory: ./auto-discovery/kubernetes/pull-secret-extractor run: make unit-test From acbaa50e2ca1568a6e6be88eb9e0148ad910eb0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 14:15:05 +0100 Subject: [PATCH 45/60] Replace assertEqual with assertCountEqual to ensure tests are independent of list order #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../kubernetes/pull-secret-extractor/test_secret_extraction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py b/auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py index b6c43796bf..28cc2a82ee 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py +++ b/auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py @@ -33,7 +33,7 @@ def test_get_correct_secret(self): # testuser:testpassword base64 encoded expected = {'auth': 'dGVzdHVzZXI6dGVzdHBhc3N3b3Jk'} - self.assertEqual(expected, actual) + self.assertCountEqual(expected, actual) def test_get_user_and_password_given_auth_string(self): secret = {'auth': 'dGVzdHVzZXI6dGVzdHBhc3N3b3Jk'} From bd06b0283b6f21d9088a5d4a2486fa50456cbb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 14:29:23 +0100 Subject: [PATCH 46/60] Fix python unit test where list assertion fails because list order is different #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .../pull-secret-extractor/test_secret_extraction.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py b/auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py index 28cc2a82ee..52cd45330e 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py +++ b/auto-discovery/kubernetes/pull-secret-extractor/test_secret_extraction.py @@ -14,12 +14,14 @@ def test_get_raw_secrets(self): actual = get_raw_secrets('test_secrets') with open('test_secrets/secret_1/.dockerconfigjson') as file: - expected = [json.load(file)] + expected_secret_1 = json.load(file) with open('test_secrets/secret_2/.dockerconfigjson') as file: - expected.append(json.load(file)) + expected_secret_2 = json.load(file) - self.assertEqual(expected, actual) + # for some reason assertCountEqual doesnt work here + self.assertIn(expected_secret_1, actual) + self.assertIn(expected_secret_2, actual) def test_get_correct_secret(self): with open('test_secrets/secret_1/.dockerconfigjson') as file: From 15826054e48e4d3b2798f105e773d4978f895b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 14:34:48 +0100 Subject: [PATCH 47/60] Add working dir to ci integration test for pull secret extractation container #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3db2c82c1d..3b7da47a26 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -166,6 +166,7 @@ jobs: kubectl get node - name: "Run integration tests" + working-directory: ./auto-discovery/kubernetes/pull-secret-extractor run: | make integration-test From 5fa3abeda66a11d903cb4a06b2e3e3e6d28a6f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 14:44:41 +0100 Subject: [PATCH 48/60] Add npm ci to makefile to install required modules before integration test #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- auto-discovery/kubernetes/pull-secret-extractor/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto-discovery/kubernetes/pull-secret-extractor/Makefile b/auto-discovery/kubernetes/pull-secret-extractor/Makefile index a82afec81f..96f4306180 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/Makefile +++ b/auto-discovery/kubernetes/pull-secret-extractor/Makefile @@ -52,7 +52,7 @@ integration-test: docker-build docker-export kind-import ./integration-test/test-pod.sh ${IMG_NS}/${IMG}:${IMG_TAG} kubectl wait --for=condition=ready --timeout=60s -n integration-test pod/init-container-test - cd integration-test && npx --yes --package jest@$(JEST_VERSION) jest --ci --colors --coverage --passWithNoTests + cd integration-test && npm ci && npx --yes --package jest@$(JEST_VERSION) jest --ci --colors --coverage --passWithNoTests ##@ Build From 39493907a5855192fe7f74a715756e7015d83639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 14:54:39 +0100 Subject: [PATCH 49/60] Add pull secret extractor to release yaml #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/release-build.yaml | 47 ++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/.github/workflows/release-build.yaml b/.github/workflows/release-build.yaml index 5187676ea4..80e5e19fde 100644 --- a/.github/workflows/release-build.yaml +++ b/.github/workflows/release-build.yaml @@ -118,6 +118,53 @@ jobs: repository: ${{ env.DOCKER_NAMESPACE }}/auto-discovery-kubernetes readme-filepath: ./auto-discovery/kubernetes/docs/README.DockerHub-Core.md + # ---- AutoDiscovery | PullSecretExtractor ---- + + auto-discovery-kubernetes-pull-secret-extractor: + name: "AutoDiscovery | Kubernetes" + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Docker Meta + id: docker_meta + uses: docker/metadata-action@v3 + with: + images: ${{ env.DOCKER_NAMESPACE }}/auto-discovery/kubernetes/pull-secret-extractor + tags: | + type=sha + type=semver,pattern={{version}} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: Build and Push + uses: docker/build-push-action@v2 + with: + context: ./auto-discovery/kubernetes/pull-secret-extractor + file: ./auto-discovery/kubernetes/pull-secret-extractor/Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} + + - name: Update Docker Hub Description + uses: peter-evans/dockerhub-description@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + repository: ${{ env.DOCKER_NAMESPACE }}/auto-discovery-secret-extraction-container + readme-filepath: ./auto-discovery/kubernetes/pull-secret-extractor/readme.md + # ---- SDK Matrix ---- sdk: From 718356d1e3808b05108c4a1f603ce52462fab493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 15:45:16 +0100 Subject: [PATCH 50/60] Add prereleased trigger to release build yaml #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/release-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-build.yaml b/.github/workflows/release-build.yaml index 80e5e19fde..3f0b6270ca 100644 --- a/.github/workflows/release-build.yaml +++ b/.github/workflows/release-build.yaml @@ -5,7 +5,7 @@ name: "Release Build" on: release: - types: [released] + types: [released, prereleased] env: # ---- Language Versions ---- From 1be050ab2bc547ea536c5ea301372bedc372cdf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 15:57:37 +0100 Subject: [PATCH 51/60] Fix dockerhub name of pull secret extractor #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/ci.yaml | 2 +- .github/workflows/release-build.yaml | 4 ++-- auto-discovery/kubernetes/pull-secret-extractor/Makefile | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3b7da47a26..b062c66fcc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -152,7 +152,7 @@ jobs: uses: actions/upload-artifact@v2 with: name: auto-discovery-pull-secret-extractor - path: ./auto-discovery/kubernetes/pull-secret-extractor/auto-discovery-secret-extraction-container.tar + path: ./auto-discovery/kubernetes/pull-secret-extractor/auto-discovery-secret-extractor.tar retention-days: 1 - name: "Start kind cluster" diff --git a/.github/workflows/release-build.yaml b/.github/workflows/release-build.yaml index 3f0b6270ca..4911e9f07a 100644 --- a/.github/workflows/release-build.yaml +++ b/.github/workflows/release-build.yaml @@ -131,7 +131,7 @@ jobs: id: docker_meta uses: docker/metadata-action@v3 with: - images: ${{ env.DOCKER_NAMESPACE }}/auto-discovery/kubernetes/pull-secret-extractor + images: ${{ env.DOCKER_NAMESPACE }}/auto-discovery-pull-secret-extractor tags: | type=sha type=semver,pattern={{version}} @@ -162,7 +162,7 @@ jobs: with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - repository: ${{ env.DOCKER_NAMESPACE }}/auto-discovery-secret-extraction-container + repository: ${{ env.DOCKER_NAMESPACE }}/auto-discovery-pull-secret-extractor readme-filepath: ./auto-discovery/kubernetes/pull-secret-extractor/readme.md # ---- SDK Matrix ---- diff --git a/auto-discovery/kubernetes/pull-secret-extractor/Makefile b/auto-discovery/kubernetes/pull-secret-extractor/Makefile index 96f4306180..92fe58d51f 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/Makefile +++ b/auto-discovery/kubernetes/pull-secret-extractor/Makefile @@ -8,7 +8,7 @@ include ../../../prerequisites.mk IMG_NS ?= securecodebox # Image URL to use all building/pushing image targets -IMG ?= auto-discovery-secret-extraction-container +IMG ?= auto-discovery-secret-extractor # Tag used for the image IMG_TAG ?= sha-$$(git rev-parse --short HEAD) From 37b37ee30e74d2e49839f27db17b460fa7a08952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=BClkenberg?= Date: Wed, 8 Feb 2023 16:21:47 +0100 Subject: [PATCH 52/60] Change name in release build to unique one #1189 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Hülkenberg --- .github/workflows/release-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-build.yaml b/.github/workflows/release-build.yaml index 4911e9f07a..f189aa407b 100644 --- a/.github/workflows/release-build.yaml +++ b/.github/workflows/release-build.yaml @@ -121,7 +121,7 @@ jobs: # ---- AutoDiscovery | PullSecretExtractor ---- auto-discovery-kubernetes-pull-secret-extractor: - name: "AutoDiscovery | Kubernetes" + name: "AutoDiscovery | Kubernetes | Pull Secret Extractor" runs-on: ubuntu-latest steps: - name: Checkout From 56178f37f4829da14be25073bce1dfcaad895acb Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Fri, 17 Mar 2023 10:40:50 +0100 Subject: [PATCH 53/60] Auto format test file --- .../pull-secret-extraction.test.js | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/auto-discovery/kubernetes/pull-secret-extractor/integration-test/pull-secret-extraction.test.js b/auto-discovery/kubernetes/pull-secret-extractor/integration-test/pull-secret-extraction.test.js index b850ed509a..2bcdca4c98 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/integration-test/pull-secret-extraction.test.js +++ b/auto-discovery/kubernetes/pull-secret-extractor/integration-test/pull-secret-extraction.test.js @@ -6,15 +6,17 @@ kc.loadFromDefault(); const k8sPodsApi = kc.makeApiClient(k8s.CoreV1Api); test( - "Test if initcontainer creates correct secrets", - async () => { - let secret; - try { - secret = await k8sPodsApi.readNamespacedSecret("test-secret", "integration-test"); - } finally { - expect(secret).toBeDefined(); - } - - }, - 60*1000 -) \ No newline at end of file + "Test if initcontainer creates correct secrets", + async () => { + let secret; + try { + secret = await k8sPodsApi.readNamespacedSecret( + "test-secret", + "integration-test" + ); + } finally { + expect(secret).toBeDefined(); + } + }, + 60 * 1000 +); From f5b1d2968b32870c53a623c0154a2b8bc038a218 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Fri, 17 Mar 2023 10:41:06 +0100 Subject: [PATCH 54/60] Fix author and license for package json in test dir --- .../pull-secret-extractor/integration-test/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/auto-discovery/kubernetes/pull-secret-extractor/integration-test/package.json b/auto-discovery/kubernetes/pull-secret-extractor/integration-test/package.json index a6fd956474..b3bc194378 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/integration-test/package.json +++ b/auto-discovery/kubernetes/pull-secret-extractor/integration-test/package.json @@ -5,8 +5,8 @@ "scripts": { "test": "pull-secret-extraction.test.js" }, - "author": "", - "license": "ISC", + "author": "iteratec GmbH", + "license": "Apache-2.0", "dependencies": { "@kubernetes/client-node": "^0.18.1" } From 57de9369e0384468888225ab49f70e323bb4647b Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Fri, 17 Mar 2023 10:41:37 +0100 Subject: [PATCH 55/60] Fix name of trivy environment variables --- auto-discovery/kubernetes/auto-discovery-config.yaml | 6 +++--- auto-discovery/kubernetes/values.yaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/auto-discovery/kubernetes/auto-discovery-config.yaml b/auto-discovery/kubernetes/auto-discovery-config.yaml index 5338f23222..a7b1d5bcc6 100644 --- a/auto-discovery/kubernetes/auto-discovery-config.yaml +++ b/auto-discovery/kubernetes/auto-discovery-config.yaml @@ -67,6 +67,6 @@ containerAutoDiscovery: # -- hookSelector allows to specify a LabelSelector with which the hooks are selected hookSelector: {} imagePullSecretConfig: - mapImagePullSecretsToEnvironmentVariables: true - usernameEnvironmentVariableName: "TRIVY-USERNAME" - passwordEnvironmentVariableName: "TRIVY-PASSWORD" + mapImagePullSecretsToEnvironmentVariables: true + usernameEnvironmentVariableName: "TRIVY_USERNAME" + passwordEnvironmentVariableName: "TRIVY_PASSWORD" diff --git a/auto-discovery/kubernetes/values.yaml b/auto-discovery/kubernetes/values.yaml index 7d896620f6..95b0a001a4 100644 --- a/auto-discovery/kubernetes/values.yaml +++ b/auto-discovery/kubernetes/values.yaml @@ -86,8 +86,8 @@ config: hookSelector: {} imagePullSecretConfig: mapImagePullSecretsToEnvironmentVariables: true - usernameEnvironmentVariableName: "TRIVY-USERNAME" - passwordEnvironmentVariableName: "TRIVY-PASSWORD" + usernameEnvironmentVariableName: "TRIVY_USERNAME" + passwordEnvironmentVariableName: "TRIVY_PASSWORD" health: healthProbeBindAddress: :8081 From 82e69c841eaa936efefc2961fa646cd3cbfdd2ce Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Fri, 17 Mar 2023 10:42:47 +0100 Subject: [PATCH 56/60] Only create trivy auto-discovery scanType when explicitly enabled Not all people have the permission ro create rbac rules. This ensures that they can still use trivy normally. --- scanners/trivy/templates/trivy-rbac.yaml | 3 ++- scanners/trivy/templates/trivy-scan-type.yaml | 2 ++ scanners/trivy/values.yaml | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/scanners/trivy/templates/trivy-rbac.yaml b/scanners/trivy/templates/trivy-rbac.yaml index eac997f8db..3920109f50 100644 --- a/scanners/trivy/templates/trivy-rbac.yaml +++ b/scanners/trivy/templates/trivy-rbac.yaml @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: the secureCodeBox authors # # SPDX-License-Identifier: Apache-2.0 - +{{ if .Values.createAutoDiscoveryScanType }} --- apiVersion: v1 kind: ServiceAccount @@ -47,3 +47,4 @@ roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: trivy-autodiscovery +{{ end }} diff --git a/scanners/trivy/templates/trivy-scan-type.yaml b/scanners/trivy/templates/trivy-scan-type.yaml index 8d95610cfb..d066f167f4 100644 --- a/scanners/trivy/templates/trivy-scan-type.yaml +++ b/scanners/trivy/templates/trivy-scan-type.yaml @@ -58,6 +58,7 @@ spec: {{- end }} volumes: {{- toYaml .Values.scanner.extraVolumes | nindent 12 }} +{{ if .Values.createAutoDiscoveryScanType }} --- apiVersion: "execution.securecodebox.io/v1" kind: ScanType @@ -116,6 +117,7 @@ spec: {{- end }} volumes: {{- toYaml .Values.scanner.extraVolumes | nindent 12 }} +{{ end }} --- apiVersion: "execution.securecodebox.io/v1" kind: ScanType diff --git a/scanners/trivy/values.yaml b/scanners/trivy/values.yaml index 45b909af8d..a278c3b93e 100644 --- a/scanners/trivy/values.yaml +++ b/scanners/trivy/values.yaml @@ -103,3 +103,7 @@ scanner: cascadingRules: # cascadingRules.enabled -- Enables or disables the installation of the default cascading rules for this scanner enabled: false + + +# -- Creates a `trivy-image-autodiscovery` scanType with its own ServiceAccount for the SCB AutoDiscovery, enabled to scan images from both public & private registries. +createAutoDiscoveryScanType: false \ No newline at end of file From 2c0da66366bbbec5f5bd2e27607dba38e09536fc Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Fri, 17 Mar 2023 11:13:29 +0100 Subject: [PATCH 57/60] Change baseimage for consistency with our other python containers --- auto-discovery/kubernetes/pull-secret-extractor/dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/auto-discovery/kubernetes/pull-secret-extractor/dockerfile b/auto-discovery/kubernetes/pull-secret-extractor/dockerfile index c680a90cfd..d36036d895 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/dockerfile +++ b/auto-discovery/kubernetes/pull-secret-extractor/dockerfile @@ -1,7 +1,6 @@ -FROM python:3 +FROM python:3.11-alpine COPY requirements.txt . RUN pip install -r requirements.txt -COPY docker_image.py . -COPY secret_extraction.py . +COPY docker_image.py secret_extraction.py . CMD ["python", secret_extraction.py] From 5064c6cc5051b497e4d68530ecd14dd2422b1665 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Fri, 17 Mar 2023 11:13:51 +0100 Subject: [PATCH 58/60] Fix message formatting and add success log --- .../kubernetes/pull-secret-extractor/secret_extraction.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py b/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py index 357d2795ea..1e9fa4ad81 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py +++ b/auto-discovery/kubernetes/pull-secret-extractor/secret_extraction.py @@ -21,8 +21,9 @@ def main(): if correct_secret: username, password = get_user_and_password(correct_secret) create_temporary_secret(username, password, temporary_secret_name) + print(f"Created temporary pull secret for domain: '{domain}'") else: - print(f"No secrets found for domain 'f{domain}'") + print(f"No secrets found for domain: '{domain}'") def get_raw_secrets(base_path: str): @@ -39,7 +40,7 @@ def get_raw_secrets(base_path: str): def get_correct_secret(domain: str, secrets) -> dict[str, str]: - """Iterates over given list of secrets to find the secret that machtes the URL in the given imageID + """Iterates over given list of secrets to find the secret that matches the URL in the given imageID :param domain: The domain of the imageID of which the correct secret needs to be identified :param secrets: List of secrets :returns: Dict containing the secret matching the given imageID @@ -87,7 +88,7 @@ def encode_base64(string: str) -> str: def create_temporary_secret(username: str, password: str, secret_name: str): - """Creates a secret with name 'secret_name' with 'username' and 'password' as data in given namespace. The secret has an ownerreference to the pod this container is running in. + """Creates a secret with name 'secret_name' with 'username' and 'password' as data in given namespace. The secret has an ownerReference to the pod this container is running in. :param username: base64 encoded string representing the desired value of the 'username' field in the secret :param password: base64 encoded string representing the desired value of the 'password' field in the secret :param secret_name: Name of the newly created secret From 17c61279cbe9b9a8d020af180faddfc023db696e Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Fri, 17 Mar 2023 11:41:09 +0100 Subject: [PATCH 59/60] Change casing of Dockerfile to match all our other dockerfile --- .../kubernetes/pull-secret-extractor/{dockerfile => Dockerfile} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename auto-discovery/kubernetes/pull-secret-extractor/{dockerfile => Dockerfile} (100%) diff --git a/auto-discovery/kubernetes/pull-secret-extractor/dockerfile b/auto-discovery/kubernetes/pull-secret-extractor/Dockerfile similarity index 100% rename from auto-discovery/kubernetes/pull-secret-extractor/dockerfile rename to auto-discovery/kubernetes/pull-secret-extractor/Dockerfile From 0849b0d1daa78e25f2e684452914b28e2bb6ba9e Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Fri, 17 Mar 2023 11:54:16 +0100 Subject: [PATCH 60/60] Explicitly copy files to a folder Signed-off-by: Jannik Hollenbach --- auto-discovery/kubernetes/pull-secret-extractor/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto-discovery/kubernetes/pull-secret-extractor/Dockerfile b/auto-discovery/kubernetes/pull-secret-extractor/Dockerfile index d36036d895..e2e60f8efa 100644 --- a/auto-discovery/kubernetes/pull-secret-extractor/Dockerfile +++ b/auto-discovery/kubernetes/pull-secret-extractor/Dockerfile @@ -2,5 +2,5 @@ FROM python:3.11-alpine COPY requirements.txt . RUN pip install -r requirements.txt -COPY docker_image.py secret_extraction.py . +COPY docker_image.py secret_extraction.py ./ CMD ["python", secret_extraction.py]