Compare commits
	
		
			39 Commits
		
	
	
		
			v0.5.2
			...
			8ec698f50e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 8ec698f50e | ||
|  | 9b8fe1d8ef | ||
|  | 516f2a34cf | ||
| 361d0866e9 | |||
| 9d4ade904e | |||
| 8c3714f7e0 | |||
| 36ae5cc602 | |||
| d908419b78 | |||
| 2d399ff8ce | |||
| c753737497 | |||
| 886fe3783d | |||
|  | 18a47f8ad2 | ||
|  | e405734e72 | ||
| 8bf4292991 | |||
|  | b149b26485 | ||
|  | 5263a811e1 | ||
|  | 4b59ff1aac | ||
|  | ad1cc9f646 | ||
| 0f518ab28d | |||
| 1bf2a24cf2 | |||
| a73e8ff982 | |||
|  | 54a4ffa212 | ||
| 16040bf87a | |||
|  | 9c1c7417e1 | ||
|  | 0f9ca0869c | ||
|  | 6fbf060044 | ||
|  | 3bb40cdcb4 | ||
|  | 219c9d0413 | ||
|  | 4f92bfe86a | ||
|  | 640333cfc7 | ||
|  | 6a907f149f | ||
|  | 3db74524ca | ||
|  | e49df1fb4d | ||
|  | bb3ca7573b | ||
| 097712c6c6 | |||
|  | 3845fd8045 | ||
|  | 3caacac98a | ||
|  | beeca5a6b6 | ||
|  | 2d4c8ec14b | 
							
								
								
									
										27
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -8,7 +8,9 @@ on: | |||||||
| jobs: | jobs: | ||||||
|   release: |   release: | ||||||
|     permissions: |     permissions: | ||||||
|  |       id-token: write | ||||||
|       contents: write |       contents: write | ||||||
|  |       packages: write | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
| @@ -34,15 +36,36 @@ jobs: | |||||||
|           CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" |           CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" | ||||||
|  |  | ||||||
|       - name: Get app version from chart |       - name: Get app version from chart | ||||||
|         uses: mikefarah/yq@v4.30.8 |         uses: mikefarah/yq@v4.33.3 | ||||||
|         id: app_version |         id: app_version | ||||||
|         with: |         with: | ||||||
|           cmd: yq '.appVersion' charts/bitwarden-crd-operator/Chart.yaml |           cmd: yq '.appVersion' charts/bitwarden-crd-operator/Chart.yaml | ||||||
|  |  | ||||||
|  |       - name: "GHCR Login" | ||||||
|  |         uses: docker/login-action@v2 | ||||||
|  |         with: | ||||||
|  |           registry: ghcr.io | ||||||
|  |           username: lerentis | ||||||
|  |           password: ${{ secrets.GITHUB_TOKEN }} | ||||||
|  |  | ||||||
|  |       - name: Set up QEMU | ||||||
|  |         uses: docker/setup-qemu-action@v2 | ||||||
|  |        | ||||||
|  |       - name: Set up Docker Buildx | ||||||
|  |         uses: docker/setup-buildx-action@v2 | ||||||
|  |  | ||||||
|  |       - name: "GHCR Build and Push" | ||||||
|  |         id: docker_build | ||||||
|  |         uses: docker/build-push-action@v4 | ||||||
|  |         with: | ||||||
|  |           push: true | ||||||
|  |           platforms: linux/amd64,linux/arm64 | ||||||
|  |           tags: ghcr.io/lerentis/bitwarden-crd-operator:${{ steps.app_version.outputs.result }} | ||||||
|  |  | ||||||
|       - name: Create SBOM |       - name: Create SBOM | ||||||
|         uses: anchore/sbom-action@v0 |         uses: anchore/sbom-action@v0 | ||||||
|         with: |         with: | ||||||
|           image: lerentis/bitwarden-crd-operator:${{ steps.app_version.outputs.result }} |           image: ghcr.io/lerentis/bitwarden-crd-operator:${{ steps.app_version.outputs.result }} | ||||||
|          |          | ||||||
|       - name: Publish SBOM |       - name: Publish SBOM | ||||||
|         uses: anchore/sbom-action/publish-sbom@v0 |         uses: anchore/sbom-action/publish-sbom@v0 | ||||||
|   | |||||||
							
								
								
									
										55
									
								
								.github/workflows/test-and-lint.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								.github/workflows/test-and-lint.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | |||||||
|  | name: Lint and Test | ||||||
|  |  | ||||||
|  | on: pull_request | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   lint-test: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - name: Checkout | ||||||
|  |         uses: actions/checkout@v3 | ||||||
|  |         with: | ||||||
|  |           fetch-depth: 0 | ||||||
|  |  | ||||||
|  |       - name: Set up Helm | ||||||
|  |         uses: azure/setup-helm@v3 | ||||||
|  |         with: | ||||||
|  |           version: v3.11.2 | ||||||
|  |  | ||||||
|  |       - uses: actions/setup-python@v4 | ||||||
|  |         with: | ||||||
|  |           python-version: '3.9' | ||||||
|  |           check-latest: true | ||||||
|  |  | ||||||
|  |       - name: Set up chart-testing | ||||||
|  |         uses: helm/chart-testing-action@v2.4.0 | ||||||
|  |  | ||||||
|  |       - name: Run chart-testing (list-changed) | ||||||
|  |         id: list-changed | ||||||
|  |         run: | | ||||||
|  |           changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }}) | ||||||
|  |           if [[ -n "$changed" ]]; then | ||||||
|  |             echo "changed=true" >> "$GITHUB_OUTPUT" | ||||||
|  |           fi | ||||||
|  |  | ||||||
|  |       - name: Run chart-testing (lint) | ||||||
|  |         if: steps.list-changed.outputs.changed == 'true' | ||||||
|  |         run: ct lint --target-branch ${{ github.event.repository.default_branch }} | ||||||
|  |  | ||||||
|  |   pr-build: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - name: Set up QEMU | ||||||
|  |         uses: docker/setup-qemu-action@v2 | ||||||
|  |        | ||||||
|  |       - name: Set up Docker Buildx | ||||||
|  |         uses: docker/setup-buildx-action@v2 | ||||||
|  |  | ||||||
|  |       - name: "GHCR Build" | ||||||
|  |         id: docker_build | ||||||
|  |         uses: docker/build-push-action@v4 | ||||||
|  |         with: | ||||||
|  |           push: false | ||||||
|  |           platforms: linux/amd64,linux/arm64 | ||||||
|  |           tags: ghcr.io/lerentis/bitwarden-crd-operator:dev | ||||||
|  |  | ||||||
							
								
								
									
										44
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -1,29 +1,43 @@ | |||||||
| FROM alpine:latest as builder | FROM alpine:3.17.3 | ||||||
|  |  | ||||||
| ARG BW_VERSION=2023.1.0 | LABEL org.opencontainers.image.source=https://github.com/Lerentis/bitwarden-crd-operator | ||||||
|  | LABEL org.opencontainers.image.description="Kubernetes Operator to create k8s secrets from bitwarden" | ||||||
|  | LABEL org.opencontainers.image.licenses=MIT | ||||||
|  |  | ||||||
| RUN apk add wget unzip | ARG PYTHON_VERSION=3.10.11-r0 | ||||||
|  |  | ||||||
| RUN cd /tmp && wget https://github.com/bitwarden/clients/releases/download/cli-v${BW_VERSION}/bw-linux-${BW_VERSION}.zip && \ |  | ||||||
|     unzip /tmp/bw-linux-${BW_VERSION}.zip |  | ||||||
|  |  | ||||||
| FROM alpine:3.17.2 |  | ||||||
|  |  | ||||||
| ARG PYTHON_VERSION=3.10.10-r0 |  | ||||||
| ARG PIP_VERSION=22.3.1-r1 | ARG PIP_VERSION=22.3.1-r1 | ||||||
| ARG GCOMPAT_VERSION=1.1.0-r0 | ARG GCOMPAT_VERSION=1.1.0-r0 | ||||||
|  | ARG LIBCRYPTO_VERSION=3.0.8-r4 | ||||||
|  | ARG BW_VERSION=2023.1.0 | ||||||
|  |  | ||||||
| COPY --from=builder /tmp/bw /usr/local/bin/bw | COPY requirements.txt /requirements.txt | ||||||
| COPY requirements.txt requirements.txt |  | ||||||
|  |  | ||||||
| RUN set -eux; \ | RUN set -eux; \ | ||||||
|  |     apk add --virtual build-dependencies wget unzip; \ | ||||||
|  |     ARCH="$(apk --print-arch)"; \ | ||||||
|  |     case "${ARCH}" in \ | ||||||
|  |        aarch64|arm64) \ | ||||||
|  |           apk add npm; \ | ||||||
|  |           npm install -g @bitwarden/cli@${BW_VERSION}; \ | ||||||
|  |          ;; \ | ||||||
|  |        amd64|x86_64) \ | ||||||
|  |           cd /tmp; \ | ||||||
|  |           wget https://github.com/bitwarden/clients/releases/download/cli-v${BW_VERSION}/bw-linux-${BW_VERSION}.zip; \ | ||||||
|  |           unzip /tmp/bw-linux-${BW_VERSION}.zip; \ | ||||||
|  |          ;; \ | ||||||
|  |        *) \ | ||||||
|  |          echo "Unsupported arch: ${ARCH}"; \ | ||||||
|  |          exit 1; \ | ||||||
|  |          ;; \ | ||||||
|  |     esac; \ | ||||||
|  |     apk del --purge build-dependencies; \ | ||||||
|     addgroup -S -g 1000 bw-operator; \ |     addgroup -S -g 1000 bw-operator; \ | ||||||
|     adduser -S -D -u 1000 -G bw-operator bw-operator; \ |     adduser -S -D -u 1000 -G bw-operator bw-operator; \ | ||||||
|     mkdir -p /home/bw-operator; \ |     mkdir -p /home/bw-operator; \ | ||||||
|     chown -R bw-operator /home/bw-operator; \ |     chown -R bw-operator /home/bw-operator; \ | ||||||
|     chmod +x /usr/local/bin/bw; \ |     apk add gcc musl-dev libstdc++ gcompat=${GCOMPAT_VERSION} python3=${PYTHON_VERSION} py3-pip=${PIP_VERSION} libcrypto3=${LIBCRYPTO_VERSION} libssl3=${LIBCRYPTO_VERSION}; \ | ||||||
|     apk add gcc musl-dev libstdc++ gcompat=${GCOMPAT_VERSION} python3=${PYTHON_VERSION} py3-pip=${PIP_VERSION}; \ |     pip install -r /requirements.txt --no-warn-script-location; \ | ||||||
|     pip install -r requirements.txt --no-warn-script-location; \ |     rm /requirements.txt; \ | ||||||
|     apk del --purge gcc musl-dev libstdc++; |     apk del --purge gcc musl-dev libstdc++; | ||||||
|  |  | ||||||
| COPY --chown=bw-operator:bw-operator src /home/bw-operator | COPY --chown=bw-operator:bw-operator src /home/bw-operator | ||||||
|   | |||||||
| @@ -4,9 +4,9 @@ description: Deploy the Bitwarden CRD Operator | |||||||
|  |  | ||||||
| type: application | type: application | ||||||
|  |  | ||||||
| version: "v0.5.2" | version: "v0.7.2" | ||||||
|  |  | ||||||
| appVersion: "0.5.2" | appVersion: "0.6.2" | ||||||
|  |  | ||||||
| keywords: | keywords: | ||||||
|   - operator |   - operator | ||||||
| @@ -20,7 +20,7 @@ home: https://lerentis.github.io/bitwarden-crd-operator/ | |||||||
| sources: | sources: | ||||||
|   - https://github.com/Lerentis/bitwarden-crd-operator |   - https://github.com/Lerentis/bitwarden-crd-operator | ||||||
|  |  | ||||||
| kubeVersion: '>= 1.23.0-0' | kubeVersion: ">= 1.23.0-0" | ||||||
|  |  | ||||||
| maintainers: | maintainers: | ||||||
|   - name: lerentis |   - name: lerentis | ||||||
| @@ -93,13 +93,10 @@ annotations: | |||||||
|                 enabled: true |                 enabled: true | ||||||
|   artifacthub.io/license: MIT |   artifacthub.io/license: MIT | ||||||
|   artifacthub.io/operator: "true" |   artifacthub.io/operator: "true" | ||||||
|  |   artifacthub.io/containsSecurityUpdates: "false" | ||||||
|   artifacthub.io/changes: | |   artifacthub.io/changes: | | ||||||
|     - kind: changed |     - kind: fixed | ||||||
|       description: "Bump alpine from 3.17.1 to 3.17.2" |       description: "Fallback to npm package on ARM as native bitwarden cli package is not compatible with ARM" | ||||||
|     - kind: changed |  | ||||||
|       description: "Bump bitwarden cli from 2022.11.0 to 2023.1.0" |  | ||||||
|     - kind: changed |  | ||||||
|       description: "Bump python version from 3.10.9-r1 to 3.10.10-r0" |  | ||||||
|   artifacthub.io/images: | |   artifacthub.io/images: | | ||||||
|     - name: bitwarden-crd-operator |     - name: bitwarden-crd-operator | ||||||
|       image: lerentis/bitwarden-crd-operator:0.5.1 |       image: ghcr.io/lerentis/bitwarden-crd-operator:0.6.2 | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| replicaCount: 1 | replicaCount: 1 | ||||||
|  |  | ||||||
| image: | image: | ||||||
|   repository: lerentis/bitwarden-crd-operator |   repository: ghcr.io/lerentis/bitwarden-crd-operator | ||||||
|   pullPolicy: IfNotPresent |   pullPolicy: IfNotPresent | ||||||
|   # Overrides the image tag whose default is the chart appVersion. |   # Overrides the image tag whose default is the chart appVersion. | ||||||
|   # tag: "0.1.0" |   # tag: "0.1.0" | ||||||
| @@ -14,7 +14,7 @@ imagePullSecrets: [] | |||||||
| nameOverride: "" | nameOverride: "" | ||||||
| fullnameOverride: "" | fullnameOverride: "" | ||||||
|  |  | ||||||
| #env: | # env: | ||||||
| #   - name: BW_HOST | #   - name: BW_HOST | ||||||
| #     value: "define_it" | #     value: "define_it" | ||||||
| #   - name: BW_CLIENTID | #   - name: BW_CLIENTID | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								example.yaml
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								example.yaml
									
									
									
									
									
								
							| @@ -16,3 +16,17 @@ spec: | |||||||
|   id: "88781348-c81c-4367-9801-550360c21295" |   id: "88781348-c81c-4367-9801-550360c21295" | ||||||
|   name: "test-secret" |   name: "test-secret" | ||||||
|   namespace: "default" |   namespace: "default" | ||||||
|  | --- | ||||||
|  | apiVersion: "lerentis.uploadfilter24.eu/v1beta4" | ||||||
|  | kind: BitwardenSecret | ||||||
|  | metadata: | ||||||
|  |   name: test-scope | ||||||
|  | spec: | ||||||
|  |   content: | ||||||
|  |     - element: | ||||||
|  |         secretName: public_key | ||||||
|  |         secretRef: pubKey  | ||||||
|  |         secretScope: fields | ||||||
|  |   id: "466fc4b0-ffca-4444-8d88-b59d4de3d928" | ||||||
|  |   name: "test-scope" | ||||||
|  |   namespace: "default" | ||||||
| @@ -1,3 +1,3 @@ | |||||||
| kopf==1.36.0 | kopf==1.36.1 | ||||||
| kubernetes==25.3.0 | kubernetes==26.1.0 | ||||||
| Jinja2==3.1.2 | Jinja2==3.1.2 | ||||||
|   | |||||||
| @@ -1,20 +1,20 @@ | |||||||
| #!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||||
| import kopf |  | ||||||
| import os | import os | ||||||
|  | import kopf | ||||||
|  |  | ||||||
| from utils.utils import command_wrapper, unlock_bw | from utils.utils import command_wrapper, unlock_bw | ||||||
|  |  | ||||||
|  |  | ||||||
| @kopf.on.startup() | @kopf.on.startup() | ||||||
| def bitwarden_signin(logger, **kwargs): | def bitwarden_signin(logger, **kwargs): | ||||||
|     if 'BW_HOST' in os.environ: |     if 'BW_HOST' in os.environ: | ||||||
|         try: |         try: | ||||||
|             command_wrapper(f"config server {os.getenv('BW_HOST')}") |             command_wrapper(logger, f"config server {os.getenv('BW_HOST')}") | ||||||
|         except: |         except BaseException: | ||||||
|             logger.warn("Revieved none zero exit code from server config") |             logger.warn("Received non-zero exit code from server config") | ||||||
|             logger.warn("This is expected from startup") |             logger.warn("This is expected from startup") | ||||||
|             pass |             pass | ||||||
|     else: |     else: | ||||||
|         logger.info(f"BW_HOST not set. Assuming SaaS installation") |         logger.info("BW_HOST not set. Assuming SaaS installation") | ||||||
|     command_wrapper("login --apikey") |     command_wrapper(logger, "login --apikey") | ||||||
|     unlock_bw(logger) |     unlock_bw(logger) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,7 +5,14 @@ import json | |||||||
|  |  | ||||||
| from utils.utils import unlock_bw, get_secret_from_bitwarden | from utils.utils import unlock_bw, get_secret_from_bitwarden | ||||||
|  |  | ||||||
| def create_dockerlogin(logger, secret, secret_json, username_ref, password_ref, registry): |  | ||||||
|  | def create_dockerlogin( | ||||||
|  |         logger, | ||||||
|  |         secret, | ||||||
|  |         secret_json, | ||||||
|  |         username_ref, | ||||||
|  |         password_ref, | ||||||
|  |         registry): | ||||||
|     secret.type = "dockerconfigjson" |     secret.type = "dockerconfigjson" | ||||||
|     secret.data = {} |     secret.data = {} | ||||||
|     auths_dict = {} |     auths_dict = {} | ||||||
| @@ -15,14 +22,19 @@ def create_dockerlogin(logger, secret, secret_json, username_ref, password_ref, | |||||||
|     _username = secret_json["login"][username_ref] |     _username = secret_json["login"][username_ref] | ||||||
|     logger.info(f"Creating login with username: {_username}") |     logger.info(f"Creating login with username: {_username}") | ||||||
|     _password = secret_json["login"][password_ref] |     _password = secret_json["login"][password_ref] | ||||||
|     cred_field = str(base64.b64encode(f"{_username}:{_password}".encode("utf-8")), "utf-8") |     cred_field = str( | ||||||
|  |         base64.b64encode( | ||||||
|  |             f"{_username}:{_password}".encode("utf-8")), | ||||||
|  |         "utf-8") | ||||||
|  |  | ||||||
|     reg_auth_dict["auth"] = cred_field |     reg_auth_dict["auth"] = cred_field | ||||||
|     registry_dict[registry] = reg_auth_dict |     registry_dict[registry] = reg_auth_dict | ||||||
|     auths_dict["auths"] = registry_dict |     auths_dict["auths"] = registry_dict | ||||||
|     secret.data[".dockerconfigjson"] = str(base64.b64encode(json.dumps(auths_dict).encode("utf-8")), "utf-8") |     secret.data[".dockerconfigjson"] = str(base64.b64encode( | ||||||
|  |         json.dumps(auths_dict).encode("utf-8")), "utf-8") | ||||||
|     return secret |     return secret | ||||||
|  |  | ||||||
|  |  | ||||||
| @kopf.on.create('registry-credential.lerentis.uploadfilter24.eu') | @kopf.on.create('registry-credential.lerentis.uploadfilter24.eu') | ||||||
| def create_managed_registry_secret(spec, name, namespace, logger, **kwargs): | def create_managed_registry_secret(spec, name, namespace, logger, **kwargs): | ||||||
|     username_ref = spec.get('usernameRef') |     username_ref = spec.get('usernameRef') | ||||||
| @@ -34,7 +46,7 @@ def create_managed_registry_secret(spec, name, namespace, logger, **kwargs): | |||||||
|  |  | ||||||
|     unlock_bw(logger) |     unlock_bw(logger) | ||||||
|     logger.info(f"Locking up secret with ID: {id}") |     logger.info(f"Locking up secret with ID: {id}") | ||||||
|     secret_json_object = json.loads(get_secret_from_bitwarden(id)) |     secret_json_object = get_secret_from_bitwarden(logger, id) | ||||||
|  |  | ||||||
|     api = kubernetes.client.CoreV1Api() |     api = kubernetes.client.CoreV1Api() | ||||||
|  |  | ||||||
| @@ -43,18 +55,34 @@ def create_managed_registry_secret(spec, name, namespace, logger, **kwargs): | |||||||
|         "managedObject": f"{namespace}/{name}" |         "managedObject": f"{namespace}/{name}" | ||||||
|     } |     } | ||||||
|     secret = kubernetes.client.V1Secret() |     secret = kubernetes.client.V1Secret() | ||||||
|     secret.metadata = kubernetes.client.V1ObjectMeta(name=secret_name, annotations=annotations) |     secret.metadata = kubernetes.client.V1ObjectMeta( | ||||||
|     secret = create_dockerlogin(logger, secret, secret_json_object, username_ref, password_ref, registry)    |         name=secret_name, annotations=annotations) | ||||||
|  |     secret = create_dockerlogin( | ||||||
|  |         logger, | ||||||
|  |         secret, | ||||||
|  |         secret_json_object["data"], | ||||||
|  |         username_ref, | ||||||
|  |         password_ref, | ||||||
|  |         registry) | ||||||
|  |  | ||||||
|     obj = api.create_namespaced_secret( |     obj = api.create_namespaced_secret( | ||||||
|         secret_namespace, secret |         secret_namespace, secret | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     logger.info(f"Registry Secret {secret_namespace}/{secret_name} has been created") |     logger.info( | ||||||
|  |         f"Registry Secret {secret_namespace}/{secret_name} has been created") | ||||||
|  |  | ||||||
|  |  | ||||||
| @kopf.on.update('registry-credential.lerentis.uploadfilter24.eu') | @kopf.on.update('registry-credential.lerentis.uploadfilter24.eu') | ||||||
| @kopf.timer('registry-credential.lerentis.uploadfilter24.eu', interval=900) | @kopf.timer('registry-credential.lerentis.uploadfilter24.eu', interval=900) | ||||||
| def update_managed_registry_secret(spec, status, name, namespace, logger, body, **kwargs): | def update_managed_registry_secret( | ||||||
|  |         spec, | ||||||
|  |         status, | ||||||
|  |         name, | ||||||
|  |         namespace, | ||||||
|  |         logger, | ||||||
|  |         body, | ||||||
|  |         **kwargs): | ||||||
|  |  | ||||||
|     username_ref = spec.get('usernameRef') |     username_ref = spec.get('usernameRef') | ||||||
|     password_ref = spec.get('passwordRef') |     password_ref = spec.get('passwordRef') | ||||||
| @@ -63,28 +91,34 @@ def update_managed_registry_secret(spec, status, name, namespace, logger, body, | |||||||
|     secret_name = spec.get('name') |     secret_name = spec.get('name') | ||||||
|     secret_namespace = spec.get('namespace') |     secret_namespace = spec.get('namespace') | ||||||
|  |  | ||||||
|  |  | ||||||
|     old_config = None |     old_config = None | ||||||
|     old_secret_name = None |     old_secret_name = None | ||||||
|     old_secret_namespace = None |     old_secret_namespace = None | ||||||
|     if 'kopf.zalando.org/last-handled-configuration' in body.metadata.annotations: |     if 'kopf.zalando.org/last-handled-configuration' in body.metadata.annotations: | ||||||
|         old_config = json.loads(body.metadata.annotations['kopf.zalando.org/last-handled-configuration']) |         old_config = json.loads( | ||||||
|  |             body.metadata.annotations['kopf.zalando.org/last-handled-configuration']) | ||||||
|         old_secret_name = old_config['spec'].get('name') |         old_secret_name = old_config['spec'].get('name') | ||||||
|         old_secret_namespace = old_config['spec'].get('namespace') |         old_secret_namespace = old_config['spec'].get('namespace') | ||||||
|     secret_name = spec.get('name') |     secret_name = spec.get('name') | ||||||
|     secret_namespace = spec.get('namespace') |     secret_namespace = spec.get('namespace') | ||||||
|  |  | ||||||
|     if old_config is not None and (old_secret_name != secret_name or old_secret_namespace != secret_namespace): |     if old_config is not None and ( | ||||||
|  |             old_secret_name != secret_name or old_secret_namespace != secret_namespace): | ||||||
|         # If the name of the secret or the namespace of the secret is different |         # If the name of the secret or the namespace of the secret is different | ||||||
|         # We have to delete the secret an recreate it |         # We have to delete the secret an recreate it | ||||||
|         logger.info("Secret name or namespace changed, let's recreate it") |         logger.info("Secret name or namespace changed, let's recreate it") | ||||||
|         delete_managed_secret(old_config['spec'], name, namespace, logger, **kwargs) |         delete_managed_secret( | ||||||
|  |             old_config['spec'], | ||||||
|  |             name, | ||||||
|  |             namespace, | ||||||
|  |             logger, | ||||||
|  |             **kwargs) | ||||||
|         create_managed_registry_secret(spec, name, namespace, logger, **kwargs) |         create_managed_registry_secret(spec, name, namespace, logger, **kwargs) | ||||||
|         return |         return | ||||||
|  |  | ||||||
|     unlock_bw(logger) |     unlock_bw(logger) | ||||||
|     logger.info(f"Locking up secret with ID: {id}") |     logger.info(f"Locking up secret with ID: {id}") | ||||||
|     secret_json_object = json.loads(get_secret_from_bitwarden(id)) |     secret_json_object = get_secret_from_bitwarden(logger, id) | ||||||
|  |  | ||||||
|     api = kubernetes.client.CoreV1Api() |     api = kubernetes.client.CoreV1Api() | ||||||
|  |  | ||||||
| @@ -93,15 +127,23 @@ def update_managed_registry_secret(spec, status, name, namespace, logger, body, | |||||||
|         "managedObject": f"{namespace}/{name}" |         "managedObject": f"{namespace}/{name}" | ||||||
|     } |     } | ||||||
|     secret = kubernetes.client.V1Secret() |     secret = kubernetes.client.V1Secret() | ||||||
|     secret.metadata = kubernetes.client.V1ObjectMeta(name=secret_name, annotations=annotations) |     secret.metadata = kubernetes.client.V1ObjectMeta( | ||||||
|     secret = create_dockerlogin(logger, secret, secret_json_object, username_ref, password_ref, registry) |         name=secret_name, annotations=annotations) | ||||||
|  |     secret = create_dockerlogin( | ||||||
|  |         logger, | ||||||
|  |         secret, | ||||||
|  |         secret_json_object["data"], | ||||||
|  |         username_ref, | ||||||
|  |         password_ref, | ||||||
|  |         registry) | ||||||
|     try: |     try: | ||||||
|         obj = api.replace_namespaced_secret( |         obj = api.replace_namespaced_secret( | ||||||
|             name=secret_name, |             name=secret_name, | ||||||
|             body=secret, |             body=secret, | ||||||
|             namespace="{}".format(secret_namespace)) |             namespace="{}".format(secret_namespace)) | ||||||
|         logger.info(f"Secret {secret_namespace}/{secret_name} has been updated") |         logger.info( | ||||||
|     except: |             f"Secret {secret_namespace}/{secret_name} has been updated") | ||||||
|  |     except BaseException: | ||||||
|         logger.warn( |         logger.warn( | ||||||
|             f"Could not update secret {secret_namespace}/{secret_name}!") |             f"Could not update secret {secret_namespace}/{secret_name}!") | ||||||
|  |  | ||||||
| @@ -114,6 +156,8 @@ def delete_managed_secret(spec, name, namespace, logger, **kwargs): | |||||||
|  |  | ||||||
|     try: |     try: | ||||||
|         api.delete_namespaced_secret(secret_name, secret_namespace) |         api.delete_namespaced_secret(secret_name, secret_namespace) | ||||||
|         logger.info(f"Secret {secret_namespace}/{secret_name} has been deleted") |         logger.info( | ||||||
|     except: |             f"Secret {secret_namespace}/{secret_name} has been deleted") | ||||||
|         logger.warn(f"Could not delete secret {secret_namespace}/{secret_name}!") |     except BaseException: | ||||||
|  |         logger.warn( | ||||||
|  |             f"Could not delete secret {secret_namespace}/{secret_name}!") | ||||||
|   | |||||||
							
								
								
									
										39
									
								
								src/kv.py
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								src/kv.py
									
									
									
									
									
								
							| @@ -21,13 +21,15 @@ def create_kv(secret, secret_json, content_def): | |||||||
|             if _secret_scope == "login": |             if _secret_scope == "login": | ||||||
|                 value = parse_login_scope(secret_json, _secret_key) |                 value = parse_login_scope(secret_json, _secret_key) | ||||||
|                 if value is None: |                 if value is None: | ||||||
|                     raise Exception(f"Field {_secret_key} has no value in bitwarden secret") |                     raise Exception( | ||||||
|  |                         f"Field {_secret_key} has no value in bitwarden secret") | ||||||
|                 secret.data[_secret_ref] = str(base64.b64encode( |                 secret.data[_secret_ref] = str(base64.b64encode( | ||||||
|                     value.encode("utf-8")), "utf-8") |                     value.encode("utf-8")), "utf-8") | ||||||
|             if _secret_scope == "fields": |             if _secret_scope == "fields": | ||||||
|                 value = parse_fields_scope(secret_json, _secret_key) |                 value = parse_fields_scope(secret_json, _secret_key) | ||||||
|                 if value is None: |                 if value is None: | ||||||
|                     raise Exception(f"Field {_secret_key} has no value in bitwarden secret") |                     raise Exception( | ||||||
|  |                         f"Field {_secret_key} has no value in bitwarden secret") | ||||||
|                 secret.data[_secret_ref] = str(base64.b64encode( |                 secret.data[_secret_ref] = str(base64.b64encode( | ||||||
|                     value.encode("utf-8")), "utf-8") |                     value.encode("utf-8")), "utf-8") | ||||||
|     return secret |     return secret | ||||||
| @@ -43,7 +45,7 @@ def create_managed_secret(spec, name, namespace, logger, body, **kwargs): | |||||||
|  |  | ||||||
|     unlock_bw(logger) |     unlock_bw(logger) | ||||||
|     logger.info(f"Locking up secret with ID: {id}") |     logger.info(f"Locking up secret with ID: {id}") | ||||||
|     secret_json_object = json.loads(get_secret_from_bitwarden(id)) |     secret_json_object = get_secret_from_bitwarden(logger, id) | ||||||
|  |  | ||||||
|     api = kubernetes.client.CoreV1Api() |     api = kubernetes.client.CoreV1Api() | ||||||
|  |  | ||||||
| @@ -66,7 +68,14 @@ def create_managed_secret(spec, name, namespace, logger, body, **kwargs): | |||||||
|  |  | ||||||
| @kopf.on.update('bitwarden-secret.lerentis.uploadfilter24.eu') | @kopf.on.update('bitwarden-secret.lerentis.uploadfilter24.eu') | ||||||
| @kopf.timer('bitwarden-secret.lerentis.uploadfilter24.eu', interval=900) | @kopf.timer('bitwarden-secret.lerentis.uploadfilter24.eu', interval=900) | ||||||
| def update_managed_secret(spec, status, name, namespace, logger, body, **kwargs): | def update_managed_secret( | ||||||
|  |         spec, | ||||||
|  |         status, | ||||||
|  |         name, | ||||||
|  |         namespace, | ||||||
|  |         logger, | ||||||
|  |         body, | ||||||
|  |         **kwargs): | ||||||
|  |  | ||||||
|     content_def = body['spec']['content'] |     content_def = body['spec']['content'] | ||||||
|     id = spec.get('id') |     id = spec.get('id') | ||||||
| @@ -74,23 +83,30 @@ def update_managed_secret(spec, status, name, namespace, logger, body, **kwargs) | |||||||
|     old_secret_name = None |     old_secret_name = None | ||||||
|     old_secret_namespace = None |     old_secret_namespace = None | ||||||
|     if 'kopf.zalando.org/last-handled-configuration' in body.metadata.annotations: |     if 'kopf.zalando.org/last-handled-configuration' in body.metadata.annotations: | ||||||
|         old_config = json.loads(body.metadata.annotations['kopf.zalando.org/last-handled-configuration']) |         old_config = json.loads( | ||||||
|  |             body.metadata.annotations['kopf.zalando.org/last-handled-configuration']) | ||||||
|         old_secret_name = old_config['spec'].get('name') |         old_secret_name = old_config['spec'].get('name') | ||||||
|         old_secret_namespace = old_config['spec'].get('namespace') |         old_secret_namespace = old_config['spec'].get('namespace') | ||||||
|     secret_name = spec.get('name') |     secret_name = spec.get('name') | ||||||
|     secret_namespace = spec.get('namespace') |     secret_namespace = spec.get('namespace') | ||||||
|  |  | ||||||
|     if old_config is not None and (old_secret_name != secret_name or old_secret_namespace != secret_namespace): |     if old_config is not None and ( | ||||||
|  |             old_secret_name != secret_name or old_secret_namespace != secret_namespace): | ||||||
|         # If the name of the secret or the namespace of the secret is different |         # If the name of the secret or the namespace of the secret is different | ||||||
|         # We have to delete the secret an recreate it |         # We have to delete the secret an recreate it | ||||||
|         logger.info("Secret name or namespace changed, let's recreate it") |         logger.info("Secret name or namespace changed, let's recreate it") | ||||||
|         delete_managed_secret(old_config['spec'], name, namespace, logger, **kwargs) |         delete_managed_secret( | ||||||
|  |             old_config['spec'], | ||||||
|  |             name, | ||||||
|  |             namespace, | ||||||
|  |             logger, | ||||||
|  |             **kwargs) | ||||||
|         create_managed_secret(spec, name, namespace, logger, body, **kwargs) |         create_managed_secret(spec, name, namespace, logger, body, **kwargs) | ||||||
|         return |         return | ||||||
|  |  | ||||||
|     unlock_bw(logger) |     unlock_bw(logger) | ||||||
|     logger.info(f"Locking up secret with ID: {id}") |     logger.info(f"Locking up secret with ID: {id}") | ||||||
|     secret_json_object = json.loads(get_secret_from_bitwarden(id)) |     secret_json_object = get_secret_from_bitwarden(logger, id) | ||||||
|  |  | ||||||
|     api = kubernetes.client.CoreV1Api() |     api = kubernetes.client.CoreV1Api() | ||||||
|  |  | ||||||
| @@ -109,8 +125,9 @@ def update_managed_secret(spec, status, name, namespace, logger, body, **kwargs) | |||||||
|             name=secret_name, |             name=secret_name, | ||||||
|             body=secret, |             body=secret, | ||||||
|             namespace="{}".format(secret_namespace)) |             namespace="{}".format(secret_namespace)) | ||||||
|         logger.info(f"Secret {secret_namespace}/{secret_name} has been updated") |         logger.info( | ||||||
|     except: |             f"Secret {secret_namespace}/{secret_name} has been updated") | ||||||
|  |     except BaseException: | ||||||
|         logger.warn( |         logger.warn( | ||||||
|             f"Could not update secret {secret_namespace}/{secret_name}!") |             f"Could not update secret {secret_namespace}/{secret_name}!") | ||||||
|  |  | ||||||
| @@ -125,6 +142,6 @@ def delete_managed_secret(spec, name, namespace, logger, **kwargs): | |||||||
|         api.delete_namespaced_secret(secret_name, secret_namespace) |         api.delete_namespaced_secret(secret_name, secret_namespace) | ||||||
|         logger.info( |         logger.info( | ||||||
|             f"Secret {secret_namespace}/{secret_name} has been deleted") |             f"Secret {secret_namespace}/{secret_name} has been deleted") | ||||||
|     except: |     except BaseException: | ||||||
|         logger.warn( |         logger.warn( | ||||||
|             f"Could not delete secret {secret_namespace}/{secret_name}!") |             f"Could not delete secret {secret_namespace}/{secret_name}!") | ||||||
|   | |||||||
| @@ -2,8 +2,9 @@ import json | |||||||
|  |  | ||||||
| from utils.utils import get_secret_from_bitwarden, parse_fields_scope, parse_login_scope | from utils.utils import get_secret_from_bitwarden, parse_fields_scope, parse_login_scope | ||||||
|  |  | ||||||
|  |  | ||||||
| def bitwarden_lookup(id, scope, field): | def bitwarden_lookup(id, scope, field): | ||||||
|     _secret_json = json.loads(get_secret_from_bitwarden(id)) |     _secret_json = get_secret_from_bitwarden(None, id) | ||||||
|     if scope == "login": |     if scope == "login": | ||||||
|         return parse_login_scope(_secret_json, field) |         return parse_login_scope(_secret_json, field) | ||||||
|     if scope == "fields": |     if scope == "fields": | ||||||
|   | |||||||
| @@ -12,17 +12,23 @@ lookup_func_dict = { | |||||||
|     "bitwarden_lookup": bitwarden_lookup, |     "bitwarden_lookup": bitwarden_lookup, | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| def render_template(template): | def render_template(template): | ||||||
|     jinja_template = Environment(loader=BaseLoader()).from_string(template) |     jinja_template = Environment(loader=BaseLoader()).from_string(template) | ||||||
|     jinja_template.globals.update(lookup_func_dict) |     jinja_template.globals.update(lookup_func_dict) | ||||||
|     return jinja_template.render() |     return jinja_template.render() | ||||||
|  |  | ||||||
|  |  | ||||||
| def create_template_secret(secret, filename, template): | def create_template_secret(secret, filename, template): | ||||||
|     secret.type = "Opaque" |     secret.type = "Opaque" | ||||||
|     secret.data = {} |     secret.data = {} | ||||||
|     secret.data[filename] = str(base64.b64encode(render_template(template).encode("utf-8")), "utf-8") |     secret.data[filename] = str( | ||||||
|  |         base64.b64encode( | ||||||
|  |             render_template(template).encode("utf-8")), | ||||||
|  |         "utf-8") | ||||||
|     return secret |     return secret | ||||||
|  |  | ||||||
|  |  | ||||||
| @kopf.on.create('bitwarden-template.lerentis.uploadfilter24.eu') | @kopf.on.create('bitwarden-template.lerentis.uploadfilter24.eu') | ||||||
| def create_managed_secret(spec, name, namespace, logger, body, **kwargs): | def create_managed_secret(spec, name, namespace, logger, body, **kwargs): | ||||||
|  |  | ||||||
| @@ -40,7 +46,8 @@ def create_managed_secret(spec, name, namespace, logger, body, **kwargs): | |||||||
|         "managedObject": f"{namespace}/{name}" |         "managedObject": f"{namespace}/{name}" | ||||||
|     } |     } | ||||||
|     secret = kubernetes.client.V1Secret() |     secret = kubernetes.client.V1Secret() | ||||||
|     secret.metadata = kubernetes.client.V1ObjectMeta(name=secret_name, annotations=annotations) |     secret.metadata = kubernetes.client.V1ObjectMeta( | ||||||
|  |         name=secret_name, annotations=annotations) | ||||||
|     secret = create_template_secret(secret, filename, template) |     secret = create_template_secret(secret, filename, template) | ||||||
|  |  | ||||||
|     obj = api.create_namespaced_secret( |     obj = api.create_namespaced_secret( | ||||||
| @@ -49,9 +56,17 @@ def create_managed_secret(spec, name, namespace, logger, body, **kwargs): | |||||||
|  |  | ||||||
|     logger.info(f"Secret {secret_namespace}/{secret_name} has been created") |     logger.info(f"Secret {secret_namespace}/{secret_name} has been created") | ||||||
|  |  | ||||||
|  |  | ||||||
| @kopf.on.update('bitwarden-template.lerentis.uploadfilter24.eu') | @kopf.on.update('bitwarden-template.lerentis.uploadfilter24.eu') | ||||||
| @kopf.timer('bitwarden-template.lerentis.uploadfilter24.eu', interval=900) | @kopf.timer('bitwarden-template.lerentis.uploadfilter24.eu', interval=900) | ||||||
| def update_managed_secret(spec, status, name, namespace, logger, body, **kwargs): | def update_managed_secret( | ||||||
|  |         spec, | ||||||
|  |         status, | ||||||
|  |         name, | ||||||
|  |         namespace, | ||||||
|  |         logger, | ||||||
|  |         body, | ||||||
|  |         **kwargs): | ||||||
|  |  | ||||||
|     template = spec.get('template') |     template = spec.get('template') | ||||||
|     filename = spec.get('filename') |     filename = spec.get('filename') | ||||||
| @@ -62,17 +77,24 @@ def update_managed_secret(spec, status, name, namespace, logger, body, **kwargs) | |||||||
|     old_secret_name = None |     old_secret_name = None | ||||||
|     old_secret_namespace = None |     old_secret_namespace = None | ||||||
|     if 'kopf.zalando.org/last-handled-configuration' in body.metadata.annotations: |     if 'kopf.zalando.org/last-handled-configuration' in body.metadata.annotations: | ||||||
|         old_config = json.loads(body.metadata.annotations['kopf.zalando.org/last-handled-configuration']) |         old_config = json.loads( | ||||||
|  |             body.metadata.annotations['kopf.zalando.org/last-handled-configuration']) | ||||||
|         old_secret_name = old_config['spec'].get('name') |         old_secret_name = old_config['spec'].get('name') | ||||||
|         old_secret_namespace = old_config['spec'].get('namespace') |         old_secret_namespace = old_config['spec'].get('namespace') | ||||||
|     secret_name = spec.get('name') |     secret_name = spec.get('name') | ||||||
|     secret_namespace = spec.get('namespace') |     secret_namespace = spec.get('namespace') | ||||||
|  |  | ||||||
|     if old_config is not None and (old_secret_name != secret_name or old_secret_namespace != secret_namespace): |     if old_config is not None and ( | ||||||
|  |             old_secret_name != secret_name or old_secret_namespace != secret_namespace): | ||||||
|         # If the name of the secret or the namespace of the secret is different |         # If the name of the secret or the namespace of the secret is different | ||||||
|         # We have to delete the secret an recreate it |         # We have to delete the secret an recreate it | ||||||
|         logger.info("Secret name or namespace changed, let's recreate it") |         logger.info("Secret name or namespace changed, let's recreate it") | ||||||
|         delete_managed_secret(old_config['spec'], name, namespace, logger, **kwargs) |         delete_managed_secret( | ||||||
|  |             old_config['spec'], | ||||||
|  |             name, | ||||||
|  |             namespace, | ||||||
|  |             logger, | ||||||
|  |             **kwargs) | ||||||
|         create_managed_secret(spec, name, namespace, logger, body, **kwargs) |         create_managed_secret(spec, name, namespace, logger, body, **kwargs) | ||||||
|         return |         return | ||||||
|  |  | ||||||
| @@ -85,7 +107,8 @@ def update_managed_secret(spec, status, name, namespace, logger, body, **kwargs) | |||||||
|         "managedObject": f"{namespace}/{name}" |         "managedObject": f"{namespace}/{name}" | ||||||
|     } |     } | ||||||
|     secret = kubernetes.client.V1Secret() |     secret = kubernetes.client.V1Secret() | ||||||
|     secret.metadata = kubernetes.client.V1ObjectMeta(name=secret_name, annotations=annotations) |     secret.metadata = kubernetes.client.V1ObjectMeta( | ||||||
|  |         name=secret_name, annotations=annotations) | ||||||
|     secret = create_template_secret(secret, filename, template) |     secret = create_template_secret(secret, filename, template) | ||||||
|  |  | ||||||
|     try: |     try: | ||||||
| @@ -93,11 +116,13 @@ def update_managed_secret(spec, status, name, namespace, logger, body, **kwargs) | |||||||
|             name=secret_name, |             name=secret_name, | ||||||
|             body=secret, |             body=secret, | ||||||
|             namespace="{}".format(secret_namespace)) |             namespace="{}".format(secret_namespace)) | ||||||
|         logger.info(f"Secret {secret_namespace}/{secret_name} has been updated") |         logger.info( | ||||||
|     except: |             f"Secret {secret_namespace}/{secret_name} has been updated") | ||||||
|  |     except BaseException: | ||||||
|         logger.warn( |         logger.warn( | ||||||
|             f"Could not update secret {secret_namespace}/{secret_name}!") |             f"Could not update secret {secret_namespace}/{secret_name}!") | ||||||
|  |  | ||||||
|  |  | ||||||
| @kopf.on.delete('bitwarden-template.lerentis.uploadfilter24.eu') | @kopf.on.delete('bitwarden-template.lerentis.uploadfilter24.eu') | ||||||
| def delete_managed_secret(spec, name, namespace, logger, **kwargs): | def delete_managed_secret(spec, name, namespace, logger, **kwargs): | ||||||
|     secret_name = spec.get('name') |     secret_name = spec.get('name') | ||||||
| @@ -106,6 +131,8 @@ def delete_managed_secret(spec, name, namespace, logger, **kwargs): | |||||||
|  |  | ||||||
|     try: |     try: | ||||||
|         api.delete_namespaced_secret(secret_name, secret_namespace) |         api.delete_namespaced_secret(secret_name, secret_namespace) | ||||||
|         logger.info(f"Secret {secret_namespace}/{secret_name} has been deleted") |         logger.info( | ||||||
|     except: |             f"Secret {secret_namespace}/{secret_name} has been deleted") | ||||||
|         logger.warn(f"Could not delete secret {secret_namespace}/{secret_name}!") |     except BaseException: | ||||||
|  |         logger.warn( | ||||||
|  |             f"Could not delete secret {secret_namespace}/{secret_name}!") | ||||||
|   | |||||||
| @@ -2,37 +2,52 @@ import os | |||||||
| import json | import json | ||||||
| import subprocess | import subprocess | ||||||
|  |  | ||||||
|  |  | ||||||
| class BitwardenCommandException(Exception): | class BitwardenCommandException(Exception): | ||||||
|     pass |     pass | ||||||
|  |  | ||||||
| def get_secret_from_bitwarden(id): |  | ||||||
|     return command_wrapper(command=f"get item {id}") | def get_secret_from_bitwarden(logger, id): | ||||||
|  |     return command_wrapper(logger, command=f"get item {id}") | ||||||
|  |  | ||||||
|  |  | ||||||
| def unlock_bw(logger): | def unlock_bw(logger): | ||||||
|     status_output = command_wrapper("status") |     status_output = command_wrapper(logger, "status", False) | ||||||
|     status = json.loads(status_output)['status'] |     status = status_output['data']['template']['status'] | ||||||
|     if status == 'unlocked': |     if status == 'unlocked': | ||||||
|         logger.info("Already unlocked") |         logger.info("Already unlocked") | ||||||
|         return |         return | ||||||
|     token_output = command_wrapper("unlock --passwordenv BW_PASSWORD") |     token_output = command_wrapper(logger, "unlock --passwordenv BW_PASSWORD") | ||||||
|     tokens = token_output.split('"')[1::2] |     os.environ["BW_SESSION"] = token_output["data"]["raw"] | ||||||
|     os.environ["BW_SESSION"] = tokens[1] |  | ||||||
|     logger.info("Signin successful. Session exported") |     logger.info("Signin successful. Session exported") | ||||||
|  |  | ||||||
| def command_wrapper(command): |  | ||||||
|  | def command_wrapper(logger, command, use_success: bool = True): | ||||||
|     system_env = dict(os.environ) |     system_env = dict(os.environ) | ||||||
|     sp = subprocess.Popen([f"bw {command}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=True, env=system_env) |     sp = subprocess.Popen( | ||||||
|  |         [f"bw --response {command}"], | ||||||
|  |         stdout=subprocess.PIPE, | ||||||
|  |         stderr=subprocess.PIPE, | ||||||
|  |         close_fds=True, | ||||||
|  |         shell=True, | ||||||
|  |         env=system_env) | ||||||
|     out, err = sp.communicate() |     out, err = sp.communicate() | ||||||
|     if err: |     resp = json.loads(out.decode(encoding='UTF-8')) | ||||||
|         raise BitwardenCommandException(err) |     if "DEBUG" in system_env: | ||||||
|     return out.decode(encoding='UTF-8') |         logger.info(resp) | ||||||
|  |     if resp["success"] != None and (not use_success or (use_success and resp["success"] == True)): | ||||||
|  |         return resp | ||||||
|  |     logger.warn(resp) | ||||||
|  |     return None | ||||||
|  |  | ||||||
|  |  | ||||||
| def parse_login_scope(secret_json, key): | def parse_login_scope(secret_json, key): | ||||||
|     return secret_json["login"][key] |     return secret_json["data"]["login"][key] | ||||||
|  |  | ||||||
|  |  | ||||||
| def parse_fields_scope(secret_json, key): | def parse_fields_scope(secret_json, key): | ||||||
|     if "fields" not in secret_json: |     if "fields" not in secret_json["data"]: | ||||||
|         return None |         return None | ||||||
|     for entry in secret_json["fields"]: |     for entry in secret_json["data"]["fields"]: | ||||||
|         if entry['name'] == key: |         if entry['name'] == key: | ||||||
|             return entry['value'] |             return entry['value'] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user