diff --git a/.github/config.js b/.github/config.js new file mode 100644 index 0000000..503b8c8 --- /dev/null +++ b/.github/config.js @@ -0,0 +1,41 @@ +module.exports = { + branchPrefix: 'test-renovate/', + username: 'renovate-release', + gitAuthor: 'Renovate Bot ', + platform: 'github', + includeForks: true, + dryRun: 'full', + repositories: ['jessebot/bitwarden-eso-provider'], + extends: ['config:base'], + allowPostUpgradeCommandTemplating: true, + allowedPostUpgradeCommands: ['^.*'], + regexManagers: [ + { + fileMatch: ['(^|/)Chart\\.yaml$'], + matchStrings: [ + '#\\s?renovate: image=(?.*?)\\s?appVersion:\\s?\\"?(?[\\w+\\.\\-]*)', + ], + datasourceTemplate: 'docker', + }, + ], + packageRules: [ + { + matchManagers: ['helm-requirements', 'helm-values', 'regex'], + postUpgradeTasks: { + commands: [ + `version=$(grep '^version:' {{{parentDir}}}/Chart.yaml | awk '{print $2}') + major=$(echo $version | cut -d. -f1) + minor=$(echo $version | cut -d. -f2) + patch=$(echo $version | cut -d. -f3) + minor=$(expr $minor + 1) + echo "Replacing $version with $major.$minor.$patch" + sed -i "s/^version:.*/version: $\{major\}.$\{minor\}.$\{patch\}/g" {{{parentDir}}}/Chart.yaml + cat {{{parentDir}}}/Chart.yaml + `, + ], + }, + fileFilters: ['**/Chart.yaml'], + executionMode: 'branch', + }, + ], +}; diff --git a/.github/renovate.json b/.github/renovate.json deleted file mode 100644 index 42ba690..0000000 --- a/.github/renovate.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "repositories": [ - "jessebot/bitwarden-eso-provider" - ] -} diff --git a/.github/workflows/ci-helm-lint-test.yml b/.github/workflows/ci-helm-lint-test.yml index a05ffc4..9637302 100644 --- a/.github/workflows/ci-helm-lint-test.yml +++ b/.github/workflows/ci-helm-lint-test.yml @@ -20,6 +20,8 @@ jobs: - name: Install Helm uses: azure/setup-helm@v3.5 + with: + token: ${{ secrets.GITHUB_TOKEN }} - name: Set up chart-testing uses: helm/chart-testing-action@v2.4.0 @@ -44,4 +46,4 @@ jobs: - name: Run chart-testing (install) id: install if: steps.list-changed.outputs.changed == 'true' - run: ct install --target-branch ${{ github.event.repository.default_branch }} + run: ct install --target-branch ${{ github.event.repository.default_branch }} --helm-extra-set-args="--set=bitwarden_eso_provider.create_cluster_secret_store=false --set=bitwarden_eso_provider.auth.password=${BOT_PASSWORD} --set=bitwarden_eso_provider.auth.clientID=${BOT_CLIENT_ID} --set=bitwarden_eso_provider.auth.clientSecret=${BOT_CLIENT_SECRET}" diff --git a/.github/workflows/docker-push-daily.yml b/.github/workflows/docker-push-daily.yml index 8286c5b..e2b2fe2 100644 --- a/.github/workflows/docker-push-daily.yml +++ b/.github/workflows/docker-push-daily.yml @@ -46,5 +46,6 @@ jobs: platforms: | linux/amd64 linux/arm64 - tags: | - jessebot/argocd-appset-secret-plugin:latest + tags: | + jessebot/bweso:latest + ${{ startsWith(github.ref, 'refs/tags') || format('{0}:{1}', jessebot/bweso, github.ref_name) || '' }} diff --git a/.github/workflows/renovatebot.yml b/.github/workflows/renovatebot.yml index 3f4e153..77ce572 100644 --- a/.github/workflows/renovatebot.yml +++ b/.github/workflows/renovatebot.yml @@ -14,4 +14,4 @@ jobs: uses: renovatebot/github-action@v39.0.1 with: token: ${{ secrets.RENOVATE_TOKEN }} - configurationFile: .github/renovate.json + configurationFile: .github/config.js diff --git a/README.md b/README.md index 000469e..79f27a9 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,50 @@ # Bitwarden External Secrets Operator Provider -We followed the [example](https://external-secrets.io/v0.9.2/examples/bitwarden/) over at the ESO docs to create a simple helm chart to deploy the Bitwarden ESO provider without having to spend a bunch of time on it. This project is neither affiliated with the External Secrets Operator, nor the official Bitwarden project. Report bugs here :) +We, mostly @cloudymax, followed the [example](https://external-secrets.io/v0.9.2/examples/bitwarden/) over at the ESO docs to create a simple helm chart to deploy the Bitwarden ESO provider without having to spend a bunch of time on it. This allows you to use the [`ExternalSecrets` Custom Resource](https://external-secrets.io/latest/introduction/overview/#externalsecret) with Bitwarden. +This project is neither affiliated with the External Secrets Operator, nor the official Bitwarden project. Report bugs here :) -### TLDR +## Usage For helm, see the [README](./charts/bitwarden-eso-provider/README.md) for full details of the allowed values in `values.yaml`, but this is the gist: ```bash helm repo add bitwarden-eso-provider https://jessebot.github.io/bitwarden-eso-provider helm install my-release bitwarden-eso-provider ``` + +# Example Secret +By default we will create two [`ClusterSecretStore`s](https://external-secrets.io/latest/introduction/overview/#clustersecretstore) for you that can then be accessed when you create a secret like [this](./examples/example-secret.yaml), but also printed below here: + +```yaml +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + # this is the name of the ExternalSecret object + name: cool-secret-4-dogs + namespace: coolapp4dogs +spec: + target: + # This is the name of the secret in bitwarden + name: cool-secret + deletionPolicy: Delete + template: + type: Opaque + data: + # The kubernetes secret name + password: |- + {{ .password }} + data: + # the value to pass to the kubernetes secret. + - secretKey: password + sourceRef: + storeRef: + # Use the `bitwarden-login` store to get `username` and + # `password` values from a bitwarden secret that does not + # contain custom fields, Otherwise use `bitwarden-fields' + name: bitwarden-login + kind: ClusterSecretStore + remoteRef: + # This is the `name` of your bitwarden secret. + key: + # This is the property of the bitwarden secret that we want + property: +``` diff --git a/charts/bitwarden-eso-provider/Chart.yaml b/charts/bitwarden-eso-provider/Chart.yaml index b9f54e9..2347275 100644 --- a/charts/bitwarden-eso-provider/Chart.yaml +++ b/charts/bitwarden-eso-provider/Chart.yaml @@ -15,13 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 +version: 0.2.0 -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "0.0.1" +# renovate: image=deserializeme/bweso +appVersion: "v0.0.1" maintainers: - name: "cloudymax" diff --git a/charts/bitwarden-eso-provider/README.md b/charts/bitwarden-eso-provider/README.md index aa4b3d6..14034e3 100644 --- a/charts/bitwarden-eso-provider/README.md +++ b/charts/bitwarden-eso-provider/README.md @@ -1,6 +1,6 @@ # bitwarden-eso-provider -![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) +![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v0.0.1](https://img.shields.io/badge/AppVersion-v0.0.1-informational?style=flat-square) A Helm chart for Kubernetes @@ -20,6 +20,15 @@ A Helm chart for Kubernetes | autoscaling.maxReplicas | int | `100` | | | autoscaling.minReplicas | int | `1` | | | autoscaling.targetCPUUtilizationPercentage | int | `80` | | +| bitwarden_eso_provider.auth.clientID | string | `""` | bitwarden client ID to use to grabs secrets in the pod, ignored if existingSecret is set | +| bitwarden_eso_provider.auth.clientSecret | string | `""` | bitwarden client Secret to use to grabs secrets in the pod, ignored if existingSecret is set | +| bitwarden_eso_provider.auth.existingSecret | string | `""` | use an existing secret for bitwarden credentials, ignores above credentials if this is set | +| bitwarden_eso_provider.auth.host | string | `"https://bitwarden.com"` | bitwarden hostname to use to grab secrets in the pod, ignored if existingSecret is set | +| bitwarden_eso_provider.auth.password | string | `""` | password for bitwarden | +| bitwarden_eso_provider.auth.secretKeys.clientID | string | `"BW_CLIENTID"` | secret key for bitwarden client ID to use to grabs secrets in the pod | +| bitwarden_eso_provider.auth.secretKeys.clientSecret | string | `"BW_CLIENTSECRET"` | secret key for bitwarden client Secret to use to grabs secrets in the pod | +| bitwarden_eso_provider.auth.secretKeys.host | string | `"BW_HOST"` | secret key for bitwarden hostname to use to grab secrets in the pod | +| bitwarden_eso_provider.auth.secretKeys.password | string | `"BW_PASSWORD"` | secret key for bitwarden password key | | bitwarden_eso_provider.create_cluster_secret_store | bool | `true` | if set to True, we'll create a cluster-wide accessible Cluster Secret Store see: https://external-secrets.io/latest/introduction/overview/#clustersecretstore | | fullnameOverride | string | `""` | | | image.pullPolicy | string | `"IfNotPresent"` | Overrides the image pullPolicy. Hint: set to Always if using latest tag | diff --git a/charts/bitwarden-eso-provider/templates/cluster-secret-stores.yaml b/charts/bitwarden-eso-provider/templates/cluster-secret-stores.yaml index 4217ebd..8af5988 100644 --- a/charts/bitwarden-eso-provider/templates/cluster-secret-stores.yaml +++ b/charts/bitwarden-eso-provider/templates/cluster-secret-stores.yaml @@ -7,11 +7,11 @@ metadata: spec: provider: webhook: - url: "http://{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.port }}/object/item/{{ .remoteRef.key }}" + url: "http://{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.port }}/object/items" headers: Content-Type: application/json result: - jsonPath: "$.data.login.{{ .remoteRef.property }}" + jsonPath: "$.data.data[?(@.name=={{`{{ .remoteRef.key }}`}})].login.{{`{{ .remoteRef.property }}`}}" --- apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore @@ -20,7 +20,7 @@ metadata: spec: provider: webhook: - url: "http://{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.port }}/object/item/{{ .remoteRef.key }}" + url: "http://{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.port }}/object/item/" result: - jsonPath: "$.data.fields[?@.name==\"{{ .remoteRef.property }}\"].value" + jsonPath: "$.data.data[?(@.name==\"{{`{{ .remoteRef.key }}`}}\"].fields[?@.name==\"{{`{{ .remoteRef.property }}`}}\"].value" {{- end }} diff --git a/charts/bitwarden-eso-provider/templates/credentials.yaml b/charts/bitwarden-eso-provider/templates/credentials.yaml new file mode 100644 index 0000000..b0d821b --- /dev/null +++ b/charts/bitwarden-eso-provider/templates/credentials.yaml @@ -0,0 +1,12 @@ +{{- if not .Values.bitwarden_eso_provider.auth.existingSecret }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }} +type: Opaque +data: + BW_HOST: {{ .Values.bitwarden_eso_provider.auth.host | b64enc | quote }} + BW_PASSWORD: {{ .Values.bitwarden_eso_provider.auth.password | b64enc | quote}} + BW_CLIENTID: {{ .Values.bitwarden_eso_provider.auth.clientID | b64enc | quote}} + BW_CLIENTSECRET: {{ .Values.bitwarden_eso_provider.auth.clientSecret | b64enc | quote}} +{{- end }} diff --git a/charts/bitwarden-eso-provider/templates/deployment.yaml b/charts/bitwarden-eso-provider/templates/deployment.yaml index fc0e331..89ca124 100644 --- a/charts/bitwarden-eso-provider/templates/deployment.yaml +++ b/charts/bitwarden-eso-provider/templates/deployment.yaml @@ -33,18 +33,52 @@ spec: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: BW_HOST + valueFrom: + secretKeyRef: + name: "{{ .Values.bitwarden_eso_provider.auth.existingSecret | default .Release.Name }}" + key: {{ .Values.bitwarden_eso_provider.auth.secretKeys.host }} + - name: BW_CLIENTID + valueFrom: + secretKeyRef: + name: "{{ .Values.bitwarden_eso_provider.auth.existingSecret | default .Release.Name }}" + key: {{ .Values.bitwarden_eso_provider.auth.secretKeys.clientID }} + - name: BW_CLIENTSECRET + valueFrom: + secretKeyRef: + name: "{{ .Values.bitwarden_eso_provider.auth.existingSecret | default .Release.Name }}" + key: {{ .Values.bitwarden_eso_provider.auth.secretKeys.clientSecret }} + - name: BW_PASSWORD + valueFrom: + secretKeyRef: + name: "{{ .Values.bitwarden_eso_provider.auth.existingSecret | default .Release.Name }}" + key: {{ .Values.bitwarden_eso_provider.auth.secretKeys.password }} ports: - name: http containerPort: {{ .Values.service.targetPort }} protocol: TCP livenessProbe: - httpGet: - path: / - port: http + exec: + command: + - wget + - -q + - http://127.0.0.1:{{ .Values.service.targetPort }}/sync + - --post-data='' readinessProbe: - httpGet: - path: / - port: http + tcpSocket: + port: {{ .Values.service.targetPort }} + initialDelaySeconds: 20 + failureThreshold: 3 + timeoutSeconds: 1 + periodSeconds: 10 + startupProbe: + tcpSocket: + port: {{ .Values.service.targetPort }} + initialDelaySeconds: 10 + failureThreshold: 30 + timeoutSeconds: 1 + periodSeconds: 5 resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} diff --git a/charts/bitwarden-eso-provider/templates/tests/test-configmap.yaml b/charts/bitwarden-eso-provider/templates/tests/test-configmap.yaml new file mode 100644 index 0000000..ff715c8 --- /dev/null +++ b/charts/bitwarden-eso-provider/templates/tests/test-configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-connection +data: + curl_script.sh: | + curl http://{{ include "bitwarden-eso-provider.fullname" . }}:{{ .Values.service.port }}/status diff --git a/charts/bitwarden-eso-provider/templates/tests/test-connection.yaml b/charts/bitwarden-eso-provider/templates/tests/test-connection.yaml index 1c46ab1..be80d6d 100644 --- a/charts/bitwarden-eso-provider/templates/tests/test-connection.yaml +++ b/charts/bitwarden-eso-provider/templates/tests/test-connection.yaml @@ -8,8 +8,19 @@ metadata: "helm.sh/hook": test spec: containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "bitwarden-eso-provider.fullname" . }}:{{ .Values.service.port }}'] + - name: curl + image: curlimages/curl + command: ['/bin/sh'] + args: + - "-c" + - "/testing/curl_script.sh" + volumeMounts: + - name: curl-script + mountPath: "/testing" + readOnly: false + volumes: + - name: curl-script + configMap: + name: test-connection + defaultMode: 0777 restartPolicy: Never diff --git a/charts/bitwarden-eso-provider/values.yaml b/charts/bitwarden-eso-provider/values.yaml index 191d258..71b95ea 100644 --- a/charts/bitwarden-eso-provider/values.yaml +++ b/charts/bitwarden-eso-provider/values.yaml @@ -20,7 +20,27 @@ fullnameOverride: "" bitwarden_eso_provider: # -- if set to True, we'll create a cluster-wide accessible Cluster Secret Store # see: https://external-secrets.io/latest/introduction/overview/#clustersecretstore - create_cluster_secret_store: True + create_cluster_secret_store: true + auth: + # -- password for bitwarden + password: "" + # -- bitwarden client Secret to use to grabs secrets in the pod, ignored if existingSecret is set + clientSecret: "" + # -- bitwarden client ID to use to grabs secrets in the pod, ignored if existingSecret is set + clientID: "" + # -- bitwarden hostname to use to grab secrets in the pod, ignored if existingSecret is set + host: "https://bitwarden.com" + # -- use an existing secret for bitwarden credentials, ignores above credentials if this is set + existingSecret: "" + secretKeys: + # -- secret key for bitwarden password key + password: "BW_PASSWORD" + # -- secret key for bitwarden client Secret to use to grabs secrets in the pod + clientSecret: "BW_CLIENTSECRET" + # -- secret key for bitwarden client ID to use to grabs secrets in the pod + clientID: "BW_CLIENTID" + # -- secret key for bitwarden hostname to use to grab secrets in the pod + host: "BW_HOST" serviceAccount: # -- Specifies whether a service account should be created diff --git a/docker/Dockerfile b/docker/Dockerfile index c5815b8..2fcdeb3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,8 +1,9 @@ -FROM debian:sid +FROM debian:bookworm-slim -ENV BW_CLI_VERSION=2023.1.0 +ENV BW_CLI_VERSION=2023.7.0 RUN apt update && \ + apt list --upgradeable | grep security | cut -f1 -d '/' | xargs apt-get install --no-install-recommends -y && \ apt install -y wget unzip && \ wget https://github.com/bitwarden/clients/releases/download/cli-v${BW_CLI_VERSION}/bw-linux-${BW_CLI_VERSION}.zip && \ unzip bw-linux-${BW_CLI_VERSION}.zip && \ @@ -25,4 +26,8 @@ RUN useradd -ms /bin/bash $USER && \ USER $USER WORKDIR /home/$USER/ +RUN apt-get clean && \ + rm -rf /var/lib/apt/lists/* && \ + rm -rf /tmp/* + CMD ["/bin/bash", "/usr/bin/entrypoint.sh"] diff --git a/examples/example-secret.yaml b/examples/example-secret.yaml new file mode 100644 index 0000000..103e190 --- /dev/null +++ b/examples/example-secret.yaml @@ -0,0 +1,34 @@ +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + # this is the name of the ExternalSecret object + name: cool-secret-4-dogs + namespace: coolapp4dogs +spec: + target: + # This is the name of the secret in bitwarden + name: cool-secret + deletionPolicy: Delete + template: + type: Opaque + data: + # The kubernetes secret name + password: |- + {{ .password }} + + data: + # the value to pass to the kubernetes secret. + - secretKey: password + sourceRef: + storeRef: + # Use the `bitwarden-login` store to get `username` and + # `password` values from a bitwarden secret that does not + # contain custom fields, Otherwise use `bitwarden-fields' + name: bitwarden-login + kind: ClusterSecretStore + remoteRef: + # This is the `name` of your bitwarden secret. + key: + # This is the property of the bitwarden secret that we want + property: