diff --git a/.devcontainer/common/devcontainer.json b/.devcontainer/common/devcontainer.json new file mode 100644 index 00000000..5fbe84ec --- /dev/null +++ b/.devcontainer/common/devcontainer.json @@ -0,0 +1,43 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/kubernetes-helm +{ + "name": "Bitwarden - Helm Charts Dev", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/base:bookworm", + "runArgs": ["--network=host"], // needed for kind + "postCreateCommand": "sudo .devcontainer/common/postCreateCommand.sh", + "customizations": { + "vscode": { + "extensions": [ + "technosophos.vscode-helm", + "Tim-Koehler.helm-intellisense", + "ms-kubernetes-tools.vscode-kubernetes-tools", + "ms-azuretools.vscode-docker" + ], + "settings": {} + } + }, + "features": { + "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { + "runArgs": [ + "--privileged" + ] + }, + "ghcr.io/meaningful-ooo/devcontainer-features/fish:1": { + "fisher": true + }, + "ghcr.io/devcontainers-contrib/features/kind:1": {} + }, + // "initializeCommand": "cd .devcontainer && bash ensure-mount-sources", + "mounts": [ + "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" + ], + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "kubectl version", + // Configure tool-specific properties. + // "customizations": {}, + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + "remoteUser": "root" // needed for kind: https://github.com/kubernetes-sigs/kind/issues/3196#issuecomment-1537260166 +} diff --git a/.devcontainer/common/kind-config.yaml b/.devcontainer/common/kind-config.yaml new file mode 100644 index 00000000..d4c70073 --- /dev/null +++ b/.devcontainer/common/kind-config.yaml @@ -0,0 +1,17 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: +- role: control-plane + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + extraPortMappings: + - containerPort: 80 + hostPort: 80 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP diff --git a/.devcontainer/common/postCreateCommand.sh b/.devcontainer/common/postCreateCommand.sh new file mode 100755 index 00000000..596df87f --- /dev/null +++ b/.devcontainer/common/postCreateCommand.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +apt-get update +apt-get install -y kubernetes-client # kubectl +curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash +kind delete cluster --name helm-charts && kind create cluster --name helm-charts --config .devcontainer/common/kind-config.yaml + diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..314766e9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +* text=auto eol=lf +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c7d66fb7..e1c4c696 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,6 +19,7 @@ jobs: matrix: include: - chart_name: self-host + - chart_name: sm-operator steps: - name: Checkout repo uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 diff --git a/.github/workflows/config/sample-bw-secret.yaml b/.github/workflows/config/sample-bw-secret.yaml new file mode 100644 index 00000000..5ea121ec --- /dev/null +++ b/.github/workflows/config/sample-bw-secret.yaml @@ -0,0 +1,14 @@ +apiVersion: k8s.bitwarden.com/v1 +kind: BitwardenSecret +metadata: + labels: + app.kubernetes.io/name: bitwardensecret + app.kubernetes.io/instance: bitwardensecret-sample + app.kubernetes.io/part-of: sm-operator + name: bitwardensecret-sample +spec: + organizationId: "5a30c3dd-d7b9-4d32-8764-b06800c9e6ff" + secretName: bw-sample-secret + authToken: + secretName: bw-auth-token + secretKey: token diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b0fd7311..8a59767b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,6 +16,7 @@ jobs: test: name: Test Helm charts runs-on: ubuntu-22.04 + environment: Production steps: - name: Checkout repo uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 @@ -27,6 +28,18 @@ jobs: with: version: 'v3.13.1' + - name: Login to Azure - CI Subscription + uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 + with: + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + + - name: Retrieve secrets + id: retrieve-secrets + uses: bitwarden/gh-actions/get-keyvault-secrets@main + with: + keyvault: "bitwarden-ci" + secrets: "helm-sm-operator-ci-test-access-token" + - name: Set up lynx run: sudo apt install lynx @@ -45,6 +58,7 @@ jobs: CHANGED=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }}) if [[ -n "$CHANGED" ]]; then echo "changed=true" >> $GITHUB_OUTPUT + echo "changed-list=$CHANGED" >> $GITHUB_OUTPUT fi - name: Create kind cluster @@ -107,12 +121,17 @@ jobs: kubectl create secret tls tls-secret --cert=bitwarden.localhost.pem --key=bitwarden.localhost.key + #Setup image pull secret + kubectl create secret -n bitwarden docker-registry ghcr-login-secret --docker-server=ghcr.io --docker-username=bitwarden-devops-bot --docker-password=${{ secrets.GITHUB_TOKEN }} --docker-email=106330231+bitwarden-devops-bot@users.noreply.github.com + + + - name: Run chart-testing (install) if: steps.list-changed.outputs.changed == 'true' run: ct install --target-branch ${{ github.event.repository.default_branch }} --skip-clean-up --namespace bitwarden - - name: Test install - if: steps.list-changed.outputs.changed == 'true' + - name: Test install (self-host) + if: steps.list-changed.outputs.changed == 'true' && contains(steps.list-changed.outputs.changed-list,'self-host') run: | #For review purposes echo "*****DEPLOYMENTS*****" @@ -175,6 +194,57 @@ jobs: echo "Admin OK." + + - name: Test install (sm-operator) + if: steps.list-changed.outputs.changed == 'true' && contains(steps.list-changed.outputs.changed-list,'sm-operator') + run: | + #For review purposes + echo "*****DEPLOYMENTS*****" + kubectl get deployments + + echo "*****PODS*****" + pods=$(kubectl get pods -l app.kubernetes.io/name=sm-operator | grep 2/2) + echo $pods + + if [[ -z "$pods" ]]; then + echo "::error::No pods found." + exit 1 + fi + + echo "*****CREATING AUTH SECRET*****" + kubectl create secret generic bw-auth-token -n bitwarden --from-literal=token="$AUTH_TOKEN" + echo "*****CREATING BW SECRET*****" + kubectl apply -f .github/workflows/config/sample-bw-secret.yaml + + # Sleeping while BitwardenSecret is being created and synced + sleep 2s + + echo "*****LOGS*****" + logs=$(kubectl logs -l app.kubernetes.io/name=sm-operator -c manager) + echo "$logs" + + completed=$(echo "$logs"| grep "Completed sync for bitwarden/bitwardensecret-sample") + + if [[ -z "$completed" ]]; then + echo "::error::Secret did not sync." + exit 1 + fi + + echo "*****RESULTING SECRETS*****" + secrets=$(kubectl get secrets) + echo "$secrets" + + secretCreated=$(echo "$secrets" | grep "bw-sample-secret Opaque 3") + + if [[ -z "$secretCreated" ]]; then + echo "::error::Secret not created correctly." + exit 1 + fi + + echo "*****OPERATOR OK*****" + env: + AUTH_TOKEN: ${{ steps.retrieve-secrets.outputs.helm-sm-operator-ci-test-access-token }} + - name: Clean-up if: steps.list-changed.outputs.changed == 'true' run: | diff --git a/.github/workflows/update-versions.yml b/.github/workflows/update-versions-self-host.yml similarity index 99% rename from .github/workflows/update-versions.yml rename to .github/workflows/update-versions-self-host.yml index 79c16087..e89d3698 100644 --- a/.github/workflows/update-versions.yml +++ b/.github/workflows/update-versions-self-host.yml @@ -1,5 +1,5 @@ --- -name: Update Versions +name: Update Versions - Self Host on: workflow_dispatch: @@ -81,9 +81,9 @@ jobs: WEBVERSION: ${{ steps.get-web.outputs.version }} run: | echo "Checking..." - + coreimages=( "admin" "api" "attachments" "events" "icons" "identity" "notifications" "scim" "sso" "mssqlmigratorutility" ) - + test_image() { image=$1 version=$2 @@ -98,7 +98,7 @@ jobs: } echo "Core Images ($COREVERSION)..." - + for key in "${!coreimages[@]}" do image=${coreimages[$key]} @@ -106,7 +106,7 @@ jobs: done echo "Web Image ($WEBVERSION)..." - + test_image "web" $WEBVERSION update-versions: diff --git a/.github/workflows/update-versions-sm-operator.yml b/.github/workflows/update-versions-sm-operator.yml new file mode 100644 index 00000000..9933d97b --- /dev/null +++ b/.github/workflows/update-versions-sm-operator.yml @@ -0,0 +1,112 @@ +--- +name: Update Versions - SM Operator + +on: + workflow_dispatch: + +env: + _BRANCH: main + +jobs: + setup: + name: Setup + runs-on: ubuntu-22.04 + outputs: + operator_version: ${{ steps.operator-update.outputs.version }} + operator_version_update: ${{ steps.operator-update.outputs.update }} + steps: + - name: Checkout Branch + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ env._BRANCH }} + + - name: Get Latest Operator Version + id: get-operator-version + run: | + image="sm-operator" + json=$(curl -s "https://registry.hub.docker.com/v2/repositories/bitwarden/$image/tags/") + digest=$(echo $json |jq '."results"[] | select(.name=="latest")["digest"]') + latestTag=$(echo $json | jq --argjson DIGEST $digest '."results"[] | select(.digest==$DIGEST) | select(.name != "latest")["name"]' | head -n 1) + + echo "Operator Image ($latestTag)..." + + echo "version=$latestTag" >> $GITHUB_OUTPUT + + - name: Check if operator needs updating + id: operator-update + env: + LATEST_OPERATOR_VERSION: ${{ steps.get-operator-version.outputs.version }} + run: | + OPERATOR_VERSION=$(sed -nE 's/^appVersion:\s+([^\s]+)/\1/p' Chart.yaml) + + echo "Operator Version: $OPERATOR_VERSION" + echo "Latest Operator Version: $LATEST_OPERATOR_VERSION" + if [ "$OPERATOR_VERSION" != "$LATEST_OPERATOR_VERSION" ]; then + echo "Needs Operator update!" + echo "update=1" >> $GITHUB_OUTPUT + else + echo "update=0" >> $GITHUB_OUTPUT + fi + working-directory: charts/sm-operator + + update-versions: + name: "Update Versions" + if: needs.setup.outputs.operator_version_update == 1 + runs-on: ubuntu-22.04 + needs: setup + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout Branch + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ env._BRANCH }} + + - name: Create Update Versions Branch + run: | + PR_BRANCH=update-versions-$GITHUB_RUN_ID + echo "PR_BRANCH=$PR_BRANCH" >> $GITHUB_ENV + git switch -c $PR_BRANCH + git push -u origin $PR_BRANCH + + - name: Checkout Update Versions Branch + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ env.PR_BRANCH }} + + - name: Update Chart appVersion + env: + VERSION: ${{ needs.setup.outputs.operator_version }} + run: "sed -i -e 's/appVersion:.*/appVersion: '$VERSION'/' Chart.yaml" + working-directory: charts/sm-operator + + - name: Commit updated files + run: | + git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git commit -m "Updated operator version" -a + + - name: Push changes + run: git push -u origin $PR_BRANCH + + - name: Create Update Versions PR + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BASE_BRANCH: ${{ github.ref_name }} + TITLE: "Update operator version" + run: | + gh pr create --title "$TITLE" \ + --base "$BASE_BRANCH" \ + --head "$PR_BRANCH" \ + --label "automated pr" \ + --body " + ## Type of change + - [ ] Bug fix + - [ ] New feature development + - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) + - [ ] Build/deploy pipeline (DevOps) + - [X] Other + + ## Objective + Automated version update to appVersion in charts/sm-operator/Chart.yaml" diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump-self-host.yml similarity index 99% rename from .github/workflows/version-bump.yml rename to .github/workflows/version-bump-self-host.yml index 52b7dcd3..65304934 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump-self-host.yml @@ -1,5 +1,5 @@ --- -name: Version Bump +name: Version Bump - Self Host on: workflow_dispatch: diff --git a/.github/workflows/version-bump-sm-operator.yml b/.github/workflows/version-bump-sm-operator.yml new file mode 100644 index 00000000..f9958b84 --- /dev/null +++ b/.github/workflows/version-bump-sm-operator.yml @@ -0,0 +1,97 @@ +--- +name: Version Bump - SM Operator + +on: + workflow_dispatch: + inputs: + version_number: + description: "New Version" + required: true + +jobs: + bump_version: + name: "Bump Version to ${{ inputs.version_number }}" + runs-on: ubuntu-22.04 + environment: Production + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout Branch + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Login to Azure - CI Subscription + uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 + with: + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + + - name: Retrieve secrets + id: retrieve-secrets + uses: bitwarden/gh-actions/get-keyvault-secrets@main + with: + keyvault: "bitwarden-ci" + secrets: "github-gpg-private-key, github-gpg-private-key-passphrase" + + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0 + with: + gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }} + passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }} + git_user_signingkey: true + git_commit_gpgsign: true + + - name: "Create version_bump_${{ inputs.version_number }} branch" + run: git switch -c version_bump_${{ inputs.version_number }} + + - name: Bump Version + uses: bitwarden/gh-actions/version-bump@main + with: + version: ${{ inputs.version_number }} + file_path: "charts/sm-operator/Chart.yaml" + + - name: Setup git + run: | + git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com" + git config --local user.name "bitwarden-devops-bot" + + - name: Check if version changed + id: version-changed + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "changes_to_commit=TRUE" >> $GITHUB_OUTPUT + else + echo "changes_to_commit=FALSE" >> $GITHUB_OUTPUT + echo "No changes to commit!"; + fi + + - name: Commit files + if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }} + run: git commit -m "Bumped version to ${{ inputs.version_number }}" -a + + - name: Push changes + if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }} + run: git push -u origin version_bump_${{ inputs.version_number }} + + - name: Create Version PR + if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }} + env: + PR_BRANCH: "version_bump_${{ inputs.version_number }}" + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + BASE_BRANCH: main + TITLE: "Bump version to ${{ inputs.version_number }}" + run: | + gh pr create --title "$TITLE" \ + --base "$BASE" \ + --head "$PR_BRANCH" \ + --label "version update" \ + --label "automated pr" \ + --body " + ## Type of change + - [ ] Bug fix + - [ ] New feature development + - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) + - [ ] Build/deploy pipeline (DevOps) + - [X] Other + + ## Objective + Automated version bump to ${{ inputs.version_number }}" diff --git a/.gitignore b/.gitignore index 47b1df1a..a33dfd3a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .DS_Store .cr-release-packages -.cr-index \ No newline at end of file +.cr-index +# Create a folder for placing user test files (Useful for the Dev Container) +values/* diff --git a/README.md b/README.md index d1892bd0..4ac2491f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ # Bitwarden Helm Charts Repository - [Self-Host](charts/self-host/README.md) +- [Secrets Manager Operator](charts/sm-operator/README.md) diff --git a/charts/sm-operator/.helmignore b/charts/sm-operator/.helmignore new file mode 100644 index 00000000..6eee0fae --- /dev/null +++ b/charts/sm-operator/.helmignore @@ -0,0 +1,30 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store + +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ + +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ + +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ + +# Project related +/ci +README.md diff --git a/charts/sm-operator/Chart.yaml b/charts/sm-operator/Chart.yaml new file mode 100644 index 00000000..0ca8391f --- /dev/null +++ b/charts/sm-operator/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +name: sm-operator +description: A Helm chart to install the Bitwarden Secrets Manager operator. +type: application +version: 0.1.0-Beta +appVersion: "0.1.0" +home: https://github.com/bitwarden/helm-charts/tree/main/charts/sm-operator +icon: https://raw.githubusercontent.com/bitwarden/brand/master/icons/icon-square.svg +maintainers: +- name: dept-devops diff --git a/charts/sm-operator/README.md b/charts/sm-operator/README.md index 806eb00a..0876a086 100644 --- a/charts/sm-operator/README.md +++ b/charts/sm-operator/README.md @@ -1,4 +1,114 @@ # Bitwarden Secrets Manager Operator -This chart is for the deployment of the Kubernetes Operator for Secrets Manager. +This chart is for the deployment of the Kubernetes Operator for Secrets Manager to different Kubernetes environments. The operator installs into your cluster and allows you to create a custom resource called a BitwardenSecret to synchronize secrets stored in Secrets Manager into your cluster as Kubernetes secrets. +## Requirements + +- [kubectl](https://kubernetes.io/docs/tasks/tools/) +- [Helm 3](https://v3.helm.sh/docs/intro/install/) +- A [Bitwarden Organization with Secrets Manager](https://bitwarden.com/help/sign-up-for-secrets-manager/). You will need the organization ID GUID for your organization. +- One or more [access tokens](https://bitwarden.com/help/access-tokens/) for a Secrets Manager machine account tied to the projects you want to pull. + +--- + +## Add the repo to Helm + +```shell +helm repo add bitwarden https://charts.bitwarden.com/ +helm repo update +``` + +## Installation Steps + +### Create config file + +Run the following command to create a custom values file used for deployment: + +```shell +helm show values bitwarden/sm-operator > my-values.yaml +``` + +### Update the config file + +Edit the `my-values.yaml` file and fill out the values. Required values that must be set. The values included can be used as is, but you should verify the following values make sense for your installation. More information on each setting is found in `values.yaml` + +- settings.bwSecretsManagerRefreshInterval +- settings.cloudRegion +- settings.bwApiUrlOverride (if you are self-hosting Bitwarden) +- settings.bwIdentityUrlOverride (if you are self-hosting Bitwarden) +- containers.enableSeccompProfileRuntimeDefault + +__NOTE: If you are testing this chart via the DevContainer and trying to point to a locally running copy of Bitwarden server, you will need to use the `host.docker.internal` hostname.__ + +#### Using non-default chart images + +Update `containers.manager.image.tag` to use a different operator image version than the one shipped with the chart. + +### Install Helm Chart + +1. Run `helm upgrade sm-operator bitwarden/sm-operator -i --debug -n sm-operator-system --create-namespace --values my-values.yaml`. + - This installs/upgrades a release named `sm-operator`, in the namespace `sm-operator-system`, using values from `my-values.yaml`. + - You can see help information for the `helm install` command by running `helm install --help`. + - You can see help information for the `helm upgrade` command by running `helm upgrade --help`. + +## Creating BitwardenSecrets + +Below is an example of creating a BitwardenSecret object to synchronsize secrets stored in Bitwarden Secrets Manager into Kubernetes secrets. + +### Create an authorization token secret + +Each namespace where a BitwardenSecret is created will require a Kubernetes secret be created to authenticate against Secrets Manager. + +```shell +kubectl create secret generic bw-auth-token -n --from-literal=token="" +``` + +__*NOTE: This command is recorded in your shell history. To avoid this, consider deploying via an ephemeral pipeline agent.*__ + +### Deploy a BitwardenSecret + +Think of the BitwardenSecret object as the synchronization settings that will be used by the operator to create and synchronize a Kubernetes secret. This Kubernetes secret will live inside of a namespace and will be injected with the data available to a Secrets Manager machine account. The resulting Kubernetes secret will include all secrets that a specific machine account has access to. The key settings that you will want to update are listed below: + +- __metadata.name__: The name of the BitwardenSecret object you are deploying + +- __spec.organizationId__: The Bitwarden organization ID you are pulling Secrets Manager data from + +- __spec.secretName__: The name of the Kubernetes secret that will be created and injected with Secrets Manager data. + +- __spec.authToken__: The name of a secret inside of the Kubernetes namespace that the BitwardenSecrets object is being deployed into that contains the Secrets Manager machine account authorization token being used to access secrets. + +Secrets Manager does not guarantee unique secret names across projects, so by default secrets will be created with the Secrets Manager secret UUID used as the key. To make your generated secret easier to use, you can create a map of Bitwarden Secret IDs to Kubernetes secret keys. The generated secret will replace the Bitwarden Secret IDs with the mapped friendly name you provide. Below are the map settings available: + +- __bwSecretId__: This is the UUID of the secret in Secrets Manager. This can found under the secret name in the Secrets Manager web portal or by using the [Bitwarden Secrets Manager CLI](https://github.com/bitwarden/sdk/releases). + +- __secretKeyName__: The resulting key inside the Kubernetes secret that replaces the UUID + +Note that the custom mapping is made available on the generated secret for informational purposes in the `k8s.bitwarden.com/custom-map` annotation. + +Below is an example deployment of a BitwardenSecret with a custom mapping. Note that the map element is optional. + +```shell +cat < -f - +apiVersion: k8s.bitwarden.com/v1 +kind: BitwardenSecret +metadata: + labels: + app.kubernetes.io/name: bitwardensecret + app.kubernetes.io/instance: bitwardensecret-sample + app.kubernetes.io/part-of: sm-operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: sm-operator + name: bitwardensecret-sample +spec: + organizationId: "a08a8157-129e-4002-bab4-b118014ca9c7" + secretName: bw-sample-secret + map: + - bwSecretId: 6c230265-d472-45f7-b763-b11b01023ca6 + secretKeyName: test__secret__1 + - bwSecretId: d132a5ed-12bd-49af-9b74-b11b01025d58 + secretKeyName: test__secret__2 + authToken: + secretName: bw-auth-token + secretKey: token +EOF +``` diff --git a/charts/sm-operator/ci/test-values.yaml b/charts/sm-operator/ci/test-values.yaml new file mode 100644 index 00000000..25cd1f38 --- /dev/null +++ b/charts/sm-operator/ci/test-values.yaml @@ -0,0 +1,63 @@ +settings: + # How often the secrets synchronize in seconds. Minimum value is 180. + bwSecretsManagerRefreshInterval: 300 + # Cloud region for sync. Please see: https://bitwarden.com/help/families-for-enterprise-self-hosted/#step-1-enable-cloud-communication + cloudRegion: US + # Set only if self-hosted. These are the URLs for the Bitwarden API and Identity services + bwApiUrlOverride: + bwIdentityUrlOverride: + # This is the internal Kubernetes DNS zone. You will likely not need to change this + # unless you have specifically changed the internal DNS name. + kubernetesClusterDomain: cluster.local + # The number of operator pod replicas to run. When in doubt, leave at 1 + replicas: 1 + +# Settings specific to the pod containers +containers: + manager: + image: + # NOTE: This should be updated before the final PR + repository: ghcr.io/bitwarden/sm-operator + # Will default to the Chart's AppVersion, but you can override here. + tag: + # The pod resource requirements. You can adjust these up and down for your environment + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + kubeRbacProxy: + image: + repository: gcr.io/kubebuilder/kube-rbac-proxy + tag: v0.14.1 + # The pod resource requirements. You can adjust these up and down for your environment + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + # Provide annotations for the service account + serviceAccount: + annotations: {} + # Set to false to work on older Kubernetes versions (< 1.19) or on vendors versions + # which do NOT support this field by default (i.e. Openshift < 4.11 ). + # This setting is recommended for most common cases that do not require escalating privileges + # to make containers restrictive. + # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted + enableSeccompProfileRuntimeDefault: true + # Optional value if you have privatized the image and need to pull with credentials + imagePullSecrets: ghcr-login-secret + + +# Service endpoint configuration for the metrics service. +metricsService: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: https + type: ClusterIP diff --git a/charts/sm-operator/templates/_helpers.tpl b/charts/sm-operator/templates/_helpers.tpl new file mode 100644 index 00000000..7e6e651a --- /dev/null +++ b/charts/sm-operator/templates/_helpers.tpl @@ -0,0 +1,79 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "sm-operator.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "sm-operator.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "sm-operator.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "sm-operator.labels" -}} +helm.sh/chart: {{ include "sm-operator.chart" . }} +{{ include "sm-operator.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "sm-operator.fullLabels" -}} +{{ include "sm-operator.labels" . }} +app.kubernetes.io/created-by: {{ include "sm-operator.name" . }} +app.kubernetes.io/part-of: {{ include "sm-operator.name" . }} +{{- end }} + + +{{/* +Selector labels +*/}} +{{- define "sm-operator.selectorLabels" -}} +app.kubernetes.io/name: {{ include "sm-operator.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "sm-operator.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "sm-operator.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Name of the keys secret +*/}} +{{- define "sm-operator.configmap" -}} +{{ template "sm-operator.fullname" . }}-config-map +{{- end -}} diff --git a/charts/sm-operator/templates/bitwardensecret-crd.yaml b/charts/sm-operator/templates/bitwardensecret-crd.yaml new file mode 100644 index 00000000..5cdd6d73 --- /dev/null +++ b/charts/sm-operator/templates/bitwardensecret-crd.yaml @@ -0,0 +1,171 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: bitwardensecrets.k8s.bitwarden.com + annotations: + controller-gen.kubebuilder.io/version: v0.12.0 + labels: + {{- include "sm-operator.labels" . | nindent 4 }} +spec: + group: k8s.bitwarden.com + names: + kind: BitwardenSecret + listKind: BitwardenSecretList + plural: bitwardensecrets + singular: bitwardensecret + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: BitwardenSecret is the Schema for the bitwardensecrets API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: BitwardenSecretSpec defines the desired state of BitwardenSecret + properties: + authToken: + description: The secret key reference for the authorization token + used to connect to Secrets Manager + properties: + secretKey: + description: The key of the Kubernetes secret where the authorization + token is stored + type: string + secretName: + description: The name of the Kubernetes secret where the authorization + token is stored + type: string + required: + - secretKey + - secretName + type: object + map: + description: The mapping of organization secret IDs to K8s secret keys. This + helps improve readability and mapping to environment variables. + items: + properties: + bwSecretId: + description: The ID of the secret in Secrets Manager + type: string + secretKeyName: + description: The name of the mapped key in the created Kubernetes + secret + type: string + required: + - bwSecretId + - secretKeyName + type: object + type: array + organizationId: + description: The organization ID for your organization + type: string + secretName: + description: The name of the secret for the + type: string + required: + - authToken + - organizationId + - secretName + type: object + status: + description: BitwardenSecretStatus defines the observed state of BitwardenSecret + properties: + conditions: + description: Conditions store the status conditions of the BitwardenSecret + instances + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a foo's + current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details + about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers of + specific condition types may define expected values and meanings + for this field, and whether the values are considered a guaranteed + API. The value should be a CamelCase string. This field may + not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastSuccessfulSyncTime: + description: Conditions store the status conditions of the BitwardenSecret + instances + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/sm-operator/templates/configMap.yaml b/charts/sm-operator/templates/configMap.yaml new file mode 100644 index 00000000..8aec5946 --- /dev/null +++ b/charts/sm-operator/templates/configMap.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "sm-operator.configmap" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "sm-operator.fullLabels" . | nindent 4 }} +data: + BW_API_URL: {{ default (eq .Values.settings.cloudRegion "EU" | ternary "https://api.bitwarden.eu" "https://api.bitwarden.com") .Values.settings.bwApiUrlOverride | quote }} + BW_IDENTITY_API_URL: {{ default (eq .Values.settings.cloudRegion "EU" | ternary "https://identity.bitwarden.eu" "https://identity.bitwarden.com") .Values.settings.bwIdentityUrlOverride | quote }} + BW_SECRETS_MANAGER_REFRESH_INTERVAL: {{ .Values.settings.bwSecretsManagerRefreshInterval | quote }} + KUBERNETES_CLUSTER_DOMAIN: {{ .Values.settings.kubernetesClusterDomain }} diff --git a/charts/sm-operator/templates/deployment.yaml b/charts/sm-operator/templates/deployment.yaml new file mode 100644 index 00000000..71166aae --- /dev/null +++ b/charts/sm-operator/templates/deployment.yaml @@ -0,0 +1,101 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "sm-operator.fullname" . }}-controller-manager + labels: + app.kubernetes.io/component: manager + control-plane: controller-manager + {{- include "sm-operator.fullLabels" . | nindent 4 }} + namespace: {{ .Release.Namespace }} +spec: + replicas: {{ .Values.settings.replicas }} + selector: + matchLabels: + control-plane: controller-manager + {{- include "sm-operator.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + control-plane: controller-manager + {{- include "sm-operator.selectorLabels" . | nindent 8 }} + annotations: + kubectl.kubernetes.io/default-container: manager + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: + - amd64 + - arm64 + - key: kubernetes.io/os + operator: In + values: + - linux + {{- if .Values.containers.imagePullSecrets }} + imagePullSecrets: + - name: {{ .Values.containers.imagePullSecrets }} + {{- end }} + containers: + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=0 + envFrom: + - configMapRef: + name: {{ include "sm-operator.configmap" . }} + image: {{ .Values.containers.kubeRbacProxy.image.repository }}:{{ .Values.containers.kubeRbacProxy.image.tag }} + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: {{- toYaml .Values.containers.kubeRbacProxy.resources | nindent + 10 }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + envFrom: + - configMapRef: + name: {{ include "sm-operator.configmap" . }} + image: {{ .Values.containers.manager.image.repository }}:{{ .Values.containers.manager.image.tag | default .Chart.AppVersion }} + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: {{- toYaml .Values.containers.manager.resources | nindent 10 + }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + securityContext: + runAsNonRoot: true + {{- if .Values.containers.enableSeccompProfileRuntimeDefault }} + seccompProfile: + type: RuntimeDefault + {{- end }} + serviceAccountName: {{ include "sm-operator.fullname" . }}-controller-manager + terminationGracePeriodSeconds: 10 diff --git a/charts/sm-operator/templates/leader-election-rbac.yaml b/charts/sm-operator/templates/leader-election-rbac.yaml new file mode 100644 index 00000000..2a446a49 --- /dev/null +++ b/charts/sm-operator/templates/leader-election-rbac.yaml @@ -0,0 +1,57 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "sm-operator.fullname" . }}-leader-election-role + labels: + app.kubernetes.io/component: rbac + {{- include "sm-operator.fullLabels" . | nindent 4 }} + namespace: {{ .Release.Namespace }} +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "sm-operator.fullname" . }}-leader-election-rolebinding + labels: + app.kubernetes.io/component: rbac + {{- include "sm-operator.fullLabels" . | nindent 4 }} + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: '{{ include "sm-operator.fullname" . }}-leader-election-role' +subjects: +- kind: ServiceAccount + name: '{{ include "sm-operator.fullname" . }}-controller-manager' + namespace: {{ .Release.Namespace }} diff --git a/charts/sm-operator/templates/manager-rbac.yaml b/charts/sm-operator/templates/manager-rbac.yaml new file mode 100644 index 00000000..8eaa9aee --- /dev/null +++ b/charts/sm-operator/templates/manager-rbac.yaml @@ -0,0 +1,67 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "sm-operator.fullname" . }}-manager-role + labels: + {{- include "sm-operator.fullLabels" . | nindent 4 }} +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - secrets/status + verbs: + - get +- apiGroups: + - k8s.bitwarden.com + resources: + - bitwardensecrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - k8s.bitwarden.com + resources: + - bitwardensecrets/finalizers + verbs: + - update +- apiGroups: + - k8s.bitwarden.com + resources: + - bitwardensecrets/status + verbs: + - get + - patch + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "sm-operator.fullname" . }}-manager-rolebinding + labels: + app.kubernetes.io/component: rbac + {{- include "sm-operator.fullLabels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: '{{ include "sm-operator.fullname" . }}-manager-role' +subjects: +- kind: ServiceAccount + name: '{{ include "sm-operator.fullname" . }}-controller-manager' + namespace: {{ .Release.Namespace }} diff --git a/charts/sm-operator/templates/metrics-reader-rbac.yaml b/charts/sm-operator/templates/metrics-reader-rbac.yaml new file mode 100644 index 00000000..3edecc89 --- /dev/null +++ b/charts/sm-operator/templates/metrics-reader-rbac.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "sm-operator.fullname" . }}-metrics-reader + labels: + app.kubernetes.io/component: kube-rbac-proxy + {{- include "sm-operator.fullLabels" . | nindent 4 }} +rules: +- nonResourceURLs: + - /metrics + verbs: + - get diff --git a/charts/sm-operator/templates/metrics-service.yaml b/charts/sm-operator/templates/metrics-service.yaml new file mode 100644 index 00000000..b2cc115a --- /dev/null +++ b/charts/sm-operator/templates/metrics-service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "sm-operator.fullname" . }}-controller-manager-metrics-service + labels: + app.kubernetes.io/component: kube-rbac-proxy + control-plane: controller-manager + {{- include "sm-operator.fullLabels" . | nindent 4 }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.metricsService.type }} + selector: + control-plane: controller-manager + {{- include "sm-operator.selectorLabels" . | nindent 4 }} + ports: + {{- .Values.metricsService.ports | toYaml | nindent 2 }} diff --git a/charts/sm-operator/templates/proxy-rbac.yaml b/charts/sm-operator/templates/proxy-rbac.yaml new file mode 100644 index 00000000..07f31351 --- /dev/null +++ b/charts/sm-operator/templates/proxy-rbac.yaml @@ -0,0 +1,36 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "sm-operator.fullname" . }}-proxy-role + labels: + app.kubernetes.io/component: kube-rbac-proxy + {{- include "sm-operator.fullLabels" . | nindent 4 }} +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "sm-operator.fullname" . }}-proxy-rolebinding + labels: + app.kubernetes.io/component: kube-rbac-proxy + {{- include "sm-operator.fullLabels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: '{{ include "sm-operator.fullname" . }}-proxy-role' +subjects: +- kind: ServiceAccount + name: '{{ include "sm-operator.fullname" . }}-controller-manager' + namespace: {{ .Release.Namespace }} diff --git a/charts/sm-operator/templates/serviceaccount.yaml b/charts/sm-operator/templates/serviceaccount.yaml new file mode 100644 index 00000000..52181ea9 --- /dev/null +++ b/charts/sm-operator/templates/serviceaccount.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "sm-operator.fullname" . }}-controller-manager + labels: + app.kubernetes.io/component: rbac + {{- include "sm-operator.fullLabels" . | nindent 4 }} + namespace: {{ .Release.Namespace }} + annotations: + {{- toYaml .Values.containers.serviceAccount.annotations | nindent 4 }} diff --git a/charts/sm-operator/values.schema.json b/charts/sm-operator/values.schema.json new file mode 100644 index 00000000..91e36db4 --- /dev/null +++ b/charts/sm-operator/values.schema.json @@ -0,0 +1,164 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "properties": { + "containers": { + "properties": { + "kubeRbacProxy": { + "properties": { + "image": { + "properties": { + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + }, + "type": "object" + }, + "resources": { + "properties": { + "limits": { + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + } + }, + "type": "object" + }, + "requests": { + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "manager": { + "properties": { + "image": { + "properties": { + "repository": { + "type": "string" + }, + "tag": { + "type": ["null", "string"] + } + }, + "type": "object" + }, + "resources": { + "properties": { + "limits": { + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + } + }, + "type": "object" + }, + "requests": { + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "serviceAccount": { + "properties": { + "annotations": { + "properties": {}, + "type": "object" + } + }, + "type": "object" + }, + "enableSeccompProfileRuntimeDefault": { + "type": "boolean" + }, + "imagePullSecrets": { + "type": [ "null", "string"] + } + }, + "type": "object" + }, + "metricsService": { + "properties": { + "ports": { + "items": { + "properties": { + "name": { + "type": "string" + }, + "port": { + "type": "integer" + }, + "protocol": { + "type": "string" + }, + "targetPort": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": { + "type": "string" + } + }, + "type": "object" + }, + "settings": { + "properties": { + "bwApiUrlOverride": { + "type": ["null", "string"] + }, + "bwIdentityUrlOverride": { + "type": ["null", "string"] + }, + "bwSecretsManagerRefreshInterval": { + "type": "integer", + "minimum": 180 + }, + "cloudRegion": { + "type": "string" + }, + "kubernetesClusterDomain": { + "type": "string" + }, + "replicas": { + "type": "integer" + } + }, + "type": "object" + } + }, + "type": "object" +} diff --git a/charts/sm-operator/values.yaml b/charts/sm-operator/values.yaml new file mode 100644 index 00000000..abb3a448 --- /dev/null +++ b/charts/sm-operator/values.yaml @@ -0,0 +1,62 @@ +settings: + # How often the secrets synchronize in seconds. Minimum value is 180. + bwSecretsManagerRefreshInterval: 300 + # Cloud region for sync. Please see: https://bitwarden.com/help/families-for-enterprise-self-hosted/#step-1-enable-cloud-communication + cloudRegion: US + # Set only if self-hosted. These are the URLs for the Bitwarden API and Identity services + bwApiUrlOverride: + bwIdentityUrlOverride: + # This is the internal Kubernetes DNS zone. You will likely not need to change this + # unless you have specifically changed the internal DNS name. + kubernetesClusterDomain: cluster.local + # The number of operator pod replicas to run. When in doubt, leave at 1 + replicas: 1 + +# Settings specific to the pod containers +containers: + manager: + image: + # NOTE: This should be updated before the final PR + repository: ghcr.io/bitwarden/sm-operator + # Will default to the Chart's AppVersion, but you can override here. + tag: + # The pod resource requirements. You can adjust these up and down for your environment + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + kubeRbacProxy: + image: + repository: gcr.io/kubebuilder/kube-rbac-proxy + tag: v0.14.1 + # The pod resource requirements. You can adjust these up and down for your environment + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + # Provide annotations for the service account + serviceAccount: + annotations: {} + # Set to false to work on older Kubernetes versions (< 1.19) or on vendors versions + # which do NOT support this field by default (i.e. OpenShift < 4.11 ). + # This setting is recommended for most common cases that do not require escalating privileges + # to make containers restrictive. + # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted + enableSeccompProfileRuntimeDefault: true + # Optional value if you have privatized the image and need to pull with credentials + imagePullSecrets: + +# Service endpoint configuration for the metrics service. +metricsService: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: https + type: ClusterIP