├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── semantic.yml └── workflows │ ├── chart.yaml │ ├── codeql.yaml │ ├── cover.yaml │ ├── create-release.yaml │ ├── dependency-review.yml │ ├── scan-vulns.yaml │ ├── scorecards.yml │ ├── stale.yaml │ └── website.yaml ├── .gitignore ├── .gitmodules ├── .golangci.yml ├── .goreleaser.yml ├── .pipelines ├── e2e-job-azure.yaml ├── nightly.yaml └── templates │ ├── aks-setup.yaml │ ├── aks-upgrade.yaml │ ├── arc │ ├── cluster-connect.yaml │ ├── e2e-extension-test.yaml │ ├── e2e-test-kind.yaml │ ├── extension-create.yaml │ └── setup.yaml │ ├── assign-user-identity.yaml │ ├── az-login.yaml │ ├── build-images.yaml │ ├── cleanup-images.yaml │ ├── cleanup-role-assignments.yaml │ ├── create-fic.yaml │ ├── e2e-test-azure.yaml │ ├── e2e-test-kind.yaml │ ├── e2e-test.yaml │ ├── get-logs.yaml │ ├── load-test.yaml │ ├── publish-load-test-result.yaml │ ├── role-assignment.yaml │ ├── soak-test.yaml │ ├── teardown.yaml │ └── unit-test.yaml ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── arc ├── conformance │ └── plugin │ │ ├── Dockerfile │ │ ├── arc_conformance.sh │ │ ├── conformance.yaml │ │ └── setup_failure_handler.py └── portal_gallery_package │ ├── DeploymentTemplates │ ├── CreateUiDefinition.json │ └── DefaultTemplate.json │ ├── Icons │ ├── Large.png │ ├── Medium.png │ ├── Small.png │ └── Wide.png │ ├── Manifest.json │ └── UIDefinition.json ├── azure-pipelines.yml ├── charts └── csi-secrets-store-provider-azure │ ├── Chart.lock │ ├── Chart.yaml │ ├── README.md │ ├── arc-values.yaml │ ├── config │ ├── fluentd-kubernetes.conf │ ├── fluentd.conf │ ├── gcstenant-conf.json │ └── telegraf-conf.tmpl │ ├── templates │ ├── _helpers.tpl │ ├── arc-extension-identity.yaml │ ├── arc-monitoring-config.yaml │ ├── arc-monitoring-service.yaml │ ├── arc-monitoring.yaml │ ├── arc-proxy-secret.yaml │ ├── podsecuritypolicy.yaml │ ├── provider-azure-installer-windows.yaml │ ├── provider-azure-installer.yaml │ ├── role.yaml │ ├── rolebinding.yaml │ └── serviceaccount.yaml │ └── values.yaml ├── cmd └── main.go ├── codecov.yml ├── deployment ├── provider-azure-installer-windows.yaml └── provider-azure-installer.yaml ├── docs ├── Release_Management.md ├── images │ ├── bottom-left.png │ ├── container_open.png │ ├── debug_console.png │ └── reopen-container.png ├── keyvault-test-setup.md ├── remote-devcontainer.md ├── sample │ └── ingress-controller-tls │ │ ├── deployment-app-one.yaml │ │ ├── deployment-app-two.yaml │ │ ├── ingress.yaml │ │ └── readme.md └── testing.md ├── examples ├── keyvault-certs │ └── v1alpha1_secretproviderclass_certs.yaml ├── keyvault-keys │ └── v1alpha1_secretproviderclass_keys.yaml ├── keyvault-secrets │ └── v1alpha1_secretproviderclass_secrets.yaml ├── kind │ ├── README.md │ └── kind-demo.sh ├── pod-identity │ ├── pod-inline-volume-pod-identity.yaml │ └── v1alpha1_secretproviderclass_pod_identity.yaml ├── service-principal │ ├── pod-inline-volume-service-principal.yaml │ └── v1alpha1_secretproviderclass_service_principal.yaml ├── sync-as-kubernetes-secret │ ├── deployment-synck8s.yaml │ ├── dockerconfigjson_synck8s_v1alpha1_secretproviderclass.yaml │ ├── synck8s_v1alpha1_secretproviderclass.yaml │ └── tls_synck8s_v1alpha1_secretproviderclass.yaml ├── system-assigned-managed-identity │ ├── pod-inline-volume-system-assigned-identity.yaml │ └── v1alpha1_secretproviderclass_system_assigned_identity.yaml └── user-assigned-managed-identity │ ├── pod-inline-volume-user-assigned-identity.yaml │ └── v1alpha1_secretproviderclass_user_assigned_identity.yaml ├── go.mod ├── go.sum ├── hack └── cherry_pick_pull.sh ├── images └── demo.gif ├── manifest_staging ├── charts │ └── csi-secrets-store-provider-azure │ │ ├── Chart.lock │ │ ├── Chart.yaml │ │ ├── README.md │ │ ├── arc-values.yaml │ │ ├── config │ │ ├── fluentd-kubernetes.conf │ │ ├── fluentd.conf │ │ ├── gcstenant-conf.json │ │ └── telegraf-conf.tmpl │ │ ├── templates │ │ ├── _helpers.tpl │ │ ├── arc-extension-identity.yaml │ │ ├── arc-monitoring-config.yaml │ │ ├── arc-monitoring-service.yaml │ │ ├── arc-monitoring.yaml │ │ ├── arc-proxy-secret.yaml │ │ ├── podsecuritypolicy.yaml │ │ ├── provider-azure-installer-windows.yaml │ │ ├── provider-azure-installer.yaml │ │ ├── role.yaml │ │ ├── rolebinding.yaml │ │ └── serviceaccount.yaml │ │ └── values.yaml └── deployment │ ├── provider-azure-installer-windows.yaml │ └── provider-azure-installer.yaml ├── netlify.toml ├── pkg ├── auth │ ├── auth.go │ └── auth_test.go ├── metrics │ ├── exporter.go │ ├── prometheus_exporter.go │ └── stats_reporter.go ├── provider │ ├── keyvault.go │ ├── mock_keyvault │ │ ├── doc.go │ │ └── keyvault_mock.go │ ├── mock_provider │ │ ├── doc.go │ │ └── provider_mock.go │ ├── provider.go │ ├── provider_test.go │ ├── types │ │ ├── parameters.go │ │ ├── parameters_test.go │ │ └── types.go │ ├── validate.go │ └── validate_test.go ├── server │ ├── healthz.go │ ├── healthz_test.go │ ├── server.go │ └── server_test.go ├── utils │ ├── grpc.go │ ├── grpc_test.go │ ├── helpers.go │ └── helpers_test.go └── version │ ├── version.go │ └── version_test.go ├── scripts └── create-kind-cluster.sh ├── test ├── custom_environment.json ├── e2e │ ├── Makefile │ ├── arc_test.go │ ├── auto_rotation_test.go │ ├── certificates_test.go │ ├── cleanup.go │ ├── custom_cloudenv_test.go │ ├── e2e_suite_test.go │ ├── framework │ │ ├── certificates │ │ │ └── certificates.go │ │ ├── cluster_proxy.go │ │ ├── config.go │ │ ├── daemonset │ │ │ └── daemonset.go │ │ ├── deploy │ │ │ └── deploy.go │ │ ├── exec │ │ │ └── kubectl.go │ │ ├── helm │ │ │ └── helm.go │ │ ├── interfaces.go │ │ ├── keyvault │ │ │ └── keyvault.go │ │ ├── namespace │ │ │ └── namespace.go │ │ ├── openssl │ │ │ └── openssl.go │ │ ├── pod │ │ │ └── pod.go │ │ ├── scheme.go │ │ ├── secret │ │ │ └── secret.go │ │ ├── secretproviderclass │ │ │ └── secretproviderclass.go │ │ ├── serviceaccount │ │ │ └── serviceaccount.go │ │ └── timeout.go │ ├── go.mod │ ├── go.sum │ ├── key_test.go │ ├── multiple_secret_versions_autorotation_test.go │ ├── multiple_secret_versions_test.go │ ├── pod_identity_test.go │ ├── secret_file_permission_test.go │ ├── secret_test.go │ ├── user_assigned_identity_test.go │ └── workload_identity_test.go └── load │ ├── config-deployment-template.yaml │ ├── deployment-template.yaml │ └── secret-provider-class-template.yaml ├── tools ├── go.mod ├── go.sum └── tools.go ├── website ├── Makefile ├── README.md ├── assets │ └── scss │ │ └── _variables_project.scss ├── config.toml ├── content │ └── en │ │ ├── _index.md │ │ ├── configurations │ │ ├── _index.md │ │ ├── custom-environments.md │ │ ├── deploy-in-openshift.md │ │ ├── enable-auto-rotation-secrets.md │ │ ├── feature-flags.md │ │ ├── getting-certs-and-keys.md │ │ ├── identity-access-modes │ │ │ ├── _index.md │ │ │ ├── pod-identity-mode.md │ │ │ ├── service-principal-mode.md │ │ │ ├── system-assigned-msi-mode.md │ │ │ ├── user-assigned-msi-mode.md │ │ │ └── workload-identity-mode.md │ │ ├── ingress-tls.md │ │ ├── metrics.md │ │ ├── set-env-var.md │ │ ├── sync-multiple-versions.md │ │ └── sync-with-k8s-secrets.md │ │ ├── contribution-guidelines │ │ └── _index.md │ │ ├── demos │ │ ├── _index.md │ │ ├── community-demos-and-presentations │ │ │ ├── _index.md │ │ │ ├── houssem-dellai-workload-identity.md │ │ │ ├── houssem-dellai.md │ │ │ └── nilesh-gule.md │ │ └── standard-walkthrough │ │ │ └── _index.md │ │ ├── getting-started │ │ ├── _index.md │ │ ├── installation │ │ │ └── _index.md │ │ └── usage │ │ │ └── _index.md │ │ ├── support │ │ └── _index.md │ │ ├── troubleshooting │ │ └── _index.md │ │ └── upgrading │ │ └── _index.md ├── layouts │ └── 404.html ├── package-lock.json └── package.json └── windows.Dockerfile /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Azure Key Vault provider for Secrets Store CSI driver", 3 | "dockerFile": "Dockerfile", 4 | "mounts": [ 5 | "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind", 6 | "source=${env:HOME}${env:USERPROFILE}/.azure,target=/root/.azure,type=bind" 7 | ], 8 | "workspaceMount": "src=${localWorkspaceFolder},dst=/go/src/secrets-store-csi-driver-provider-azure,type=bind,consistency=cached", 9 | "workspaceFolder": "/go/src/secrets-store-csi-driver-provider-azure", 10 | "settings": { 11 | "terminal.integrated.shell.linux": "/bin/bash" 12 | }, 13 | "runArgs": [ 14 | "--net=host", 15 | "--cap-add=SYS_PTRACE", 16 | "--security-opt", 17 | "seccomp=unconfined" 18 | ], 19 | "extensions": [ 20 | "ms-vscode.azurecli", 21 | "golang.go" 22 | ], 23 | "remoteEnv": { 24 | "DEVCONTAINER": "true" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | **Have you** 10 | 11 | - [ ] Read [Troubleshooting Guide](https://azure.github.io/secrets-store-csi-driver-provider-azure/docs/troubleshooting/) 12 | - [ ] Searched on [GitHub issues](https://github.com/Azure/secrets-store-csi-driver-provider-azure/issues) and [Discussions](https://github.com/Azure/secrets-store-csi-driver-provider-azure/discussions) 13 | 14 | **What steps did you take and what happened:** 15 | [A clear and concise description of what the bug is.] 16 | 17 | 18 | **What did you expect to happen:** 19 | 20 | 21 | **Anything else you would like to add:** 22 | [Miscellaneous information that will assist in solving the issue.] 23 | 24 | 25 | **Which access mode did you use to access the Azure Key Vault instance:** 26 | [e.g. Service Principal, Pod Identity, User Assigned Managed Identity, System Assigned Managed Identity] 27 | 28 | 29 | **Environment:** 30 | 31 | - Secrets Store CSI Driver version: (use the image tag): 32 | - Azure Key Vault provider version: (use the image tag): 33 | - Kubernetes version: (use `kubectl version` and `kubectl get nodes -o wide`): 34 | - Cluster type: (e.g. AKS, aks-engine, etc): 35 | - Installation method: ([Helm](https://azure.github.io/secrets-store-csi-driver-provider-azure/docs/getting-started/installation/#deployment-using-helm) , [Deployment yamls](https://azure.github.io/secrets-store-csi-driver-provider-azure/docs/getting-started/installation/#using-deployment-yamls), [AKS managed add-on](https://docs.microsoft.com/en-us/azure/aks/csi-secrets-store-driver)): 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the solution you'd like** 11 | [A clear and concise description of what you want to happen.] 12 | 13 | 14 | **Anything else you would like to add:** 15 | [Miscellaneous information that will assist in solving the issue.] 16 | 17 | 18 | **Environment:** 19 | 20 | - Secrets Store CSI Driver version: (use the image tag): 21 | - Azure Key Vault provider version: (use the image tag): 22 | - Kubernetes version: (use `kubectl version`): 23 | - Cluster type: (e.g. AKS, aks-engine, etc): -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Reason for Change**: 4 | 5 | 6 | 10 | 11 | **Requirements** 12 | 13 | - [ ] squashed commits 14 | - [ ] included documentation 15 | - [ ] added unit tests and e2e tests (if applicable). 16 | 17 | 18 | **Issue Fixed**: 19 | 20 | 21 | **Does this change contain code from or inspired by another project?** 22 | 23 | - [ ] Yes 24 | - [ ] No 25 | 26 | **If "Yes," did you notify that project's maintainers and provide attribution?** 27 | 28 | **Special Notes for Reviewers**: 29 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/website" 5 | schedule: 6 | interval: "weekly" 7 | commit-message: 8 | prefix: "chore" 9 | 10 | - package-ecosystem: "github-actions" 11 | directory: "/" 12 | schedule: 13 | interval: "weekly" 14 | commit-message: 15 | prefix: "chore" 16 | 17 | - package-ecosystem: "docker" 18 | directory: "/manifest_staging/charts/csi-secrets-store-provider-azure" 19 | schedule: 20 | interval: "weekly" 21 | commit-message: 22 | prefix: "chore" 23 | 24 | - package-ecosystem: docker 25 | directory: /.devcontainer 26 | schedule: 27 | interval: daily 28 | commit-message: 29 | prefix: "chore" 30 | 31 | - package-ecosystem: docker 32 | directory: / 33 | schedule: 34 | interval: daily 35 | commit-message: 36 | prefix: "chore" 37 | 38 | - package-ecosystem: docker 39 | directory: /arc/conformance/plugin 40 | schedule: 41 | interval: daily 42 | commit-message: 43 | prefix: "chore" 44 | 45 | - package-ecosystem: gomod 46 | directory: / 47 | schedule: 48 | interval: daily 49 | commit-message: 50 | prefix: "chore" 51 | 52 | - package-ecosystem: gomod 53 | directory: /test/e2e 54 | schedule: 55 | interval: daily 56 | commit-message: 57 | prefix: "chore" 58 | 59 | - package-ecosystem: gomod 60 | directory: /tools 61 | schedule: 62 | interval: daily 63 | commit-message: 64 | prefix: "chore" 65 | -------------------------------------------------------------------------------- /.github/semantic.yml: -------------------------------------------------------------------------------- 1 | titleOnly: true 2 | types: 3 | - build 4 | - chore 5 | - ci 6 | - docs 7 | - feat 8 | - fix 9 | - perf 10 | - refactor 11 | - release 12 | - revert 13 | - security 14 | - style 15 | - test 16 | -------------------------------------------------------------------------------- /.github/workflows/chart.yaml: -------------------------------------------------------------------------------- 1 | name: publish_helm_chart 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - ".github/workflows/chart.yaml" 9 | - "charts/**" 10 | 11 | permissions: 12 | contents: write 13 | 14 | jobs: 15 | deploy: 16 | runs-on: ubuntu-latest 17 | steps: 18 | # pinning to the sha 5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f from https://github.com/actions/checkout/releases/tag/v2.3.4 19 | - name: Harden Runner 20 | uses: step-security/harden-runner@128a63446a954579617e875aaab7d2978154e969 # v2.4.0 21 | with: 22 | egress-policy: audit 23 | 24 | - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 25 | with: 26 | submodules: true 27 | fetch-depth: 0 28 | - name: Publish Helm chart 29 | # pinning to the sha f1701eb82e4d4b82016e7965501c8b6d79feaec9 from https://github.com/stefanprodan/helm-gh-pages/releases/tag/v1.4.1 30 | uses: stefanprodan/helm-gh-pages@0ad2bb377311d61ac04ad9eb6f252fb68e207260 31 | with: 32 | token: ${{ secrets.GITHUB_TOKEN }} 33 | charts_dir: charts 34 | target_dir: charts 35 | linting: off 36 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yaml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | schedule: 11 | - cron: "0 15 * * 1" # Mondays at 7:00 AM PST 12 | 13 | permissions: read-all 14 | 15 | jobs: 16 | analyze: 17 | name: Analyze 18 | runs-on: ubuntu-latest 19 | permissions: 20 | security-events: write 21 | 22 | steps: 23 | - name: Harden Runner 24 | uses: step-security/harden-runner@128a63446a954579617e875aaab7d2978154e969 # v2.4.0 25 | with: 26 | egress-policy: audit 27 | 28 | - name: Checkout repository 29 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 30 | 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@df409f7d9260372bd5f19e5b04e83cb3c43714ae 33 | with: 34 | languages: go 35 | 36 | - name: Autobuild 37 | uses: github/codeql-action/autobuild@df409f7d9260372bd5f19e5b04e83cb3c43714ae 38 | 39 | - name: Perform CodeQL Analysis 40 | uses: github/codeql-action/analyze@df409f7d9260372bd5f19e5b04e83cb3c43714ae 41 | -------------------------------------------------------------------------------- /.github/workflows/cover.yaml: -------------------------------------------------------------------------------- 1 | name: Codecov 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | codecov: 15 | runs-on: ubuntu-latest 16 | steps: 17 | # pinning to the sha 5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f from https://github.com/actions/checkout/releases/tag/v2.3.4 18 | - name: Harden Runner 19 | uses: step-security/harden-runner@128a63446a954579617e875aaab7d2978154e969 # v2.4.0 20 | with: 21 | egress-policy: audit 22 | 23 | - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 24 | # pinning to the sha 331ce1d993939866bb63c32c6cbbfd48fa76fc57 from https://github.com/actions/setup-go/releases/tag/v2.1.4 25 | - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 26 | with: 27 | go-version: "^1.20" 28 | - name: Unit test 29 | run: make unit-test 30 | # pinning to the sha f32b3a3741e1053eb607407145bc9619351dc93b from https://github.com/codecov/codecov-action/releases/tag/v2.1.0 31 | - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d 32 | with: 33 | files: ./coverage.txt 34 | -------------------------------------------------------------------------------- /.github/workflows/create-release.yaml: -------------------------------------------------------------------------------- 1 | name: create_release 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | 7 | permissions: 8 | contents: write 9 | 10 | jobs: 11 | create-release: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Harden Runner 15 | uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 16 | with: 17 | egress-policy: audit 18 | 19 | - name: Checkout 20 | uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 21 | with: 22 | fetch-depth: 0 23 | - name: Goreleaser 24 | uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6.0.0 25 | with: 26 | version: '~> v2' 27 | args: release --clean --fail-fast --timeout 60m --verbose 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, 4 | # surfacing known-vulnerable versions of the packages declared or updated in the PR. 5 | # Once installed, if the workflow run is marked as required, 6 | # PRs introducing known-vulnerable packages will be blocked from merging. 7 | # 8 | # Source repository: https://github.com/actions/dependency-review-action 9 | name: 'Dependency Review' 10 | on: [pull_request] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | dependency-review: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Harden Runner 20 | uses: step-security/harden-runner@128a63446a954579617e875aaab7d2978154e969 # v2.4.0 21 | with: 22 | egress-policy: audit 23 | 24 | - name: 'Checkout Repository' 25 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 26 | - name: 'Dependency Review' 27 | uses: actions/dependency-review-action@f46c48ed6d4f1227fb2d9ea62bf6bcbed315589e # v3.0.4 28 | -------------------------------------------------------------------------------- /.github/workflows/scan-vulns.yaml: -------------------------------------------------------------------------------- 1 | name: scan_vulns 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - "docs/**" 7 | - "website/**" 8 | - "**.md" 9 | pull_request: 10 | paths-ignore: 11 | - "docs/**" 12 | - "website/**" 13 | - "**.md" 14 | 15 | permissions: read-all 16 | 17 | jobs: 18 | govulncheck: 19 | name: "Run govulncheck" 20 | runs-on: ubuntu-latest 21 | timeout-minutes: 15 22 | steps: 23 | - name: Harden Runner 24 | uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 25 | with: 26 | egress-policy: audit 27 | 28 | - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 29 | with: 30 | go-version: "1.24" 31 | check-latest: true 32 | - uses: golang/govulncheck-action@3a32958c2706f7048305d5a2e53633d7e37e97d0 # v1.0.2 33 | 34 | scan_vulnerabilities: 35 | name: "[Trivy] Scan for vulnerabilities" 36 | runs-on: ubuntu-latest 37 | timeout-minutes: 15 38 | steps: 39 | - name: Harden Runner 40 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0 41 | with: 42 | egress-policy: audit 43 | 44 | - name: Check out code into the Go module directory 45 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 46 | 47 | - name: Download trivy 48 | run: | 49 | pushd $(mktemp -d) 50 | wget https://github.com/aquasecurity/trivy/releases/download/v${{ env.TRIVY_VERSION }}/trivy_${{ env.TRIVY_VERSION }}_Linux-64bit.tar.gz 51 | tar zxvf trivy_${{ env.TRIVY_VERSION }}_Linux-64bit.tar.gz 52 | echo "$(pwd)" >> $GITHUB_PATH 53 | env: 54 | TRIVY_VERSION: "0.57.0" 55 | 56 | - name: Download trivy db 57 | run: | 58 | trivy image \ 59 | --download-db-only \ 60 | --db-repository=ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db,docker.io/aquasec/trivy-db 61 | 62 | - name: Run trivy on git repository 63 | run: | 64 | trivy fs --format table --ignore-unfixed --skip-dirs website --scanners vuln . 65 | 66 | - name: Build docker images 67 | run: | 68 | make container 69 | env: 70 | REGISTRY: e2e 71 | IMAGE_VERSION: test 72 | OUTPUT_TYPE: docker 73 | 74 | - name: Run trivy on image 75 | run: | 76 | trivy image --exit-code 1 --ignore-unfixed --severity MEDIUM,HIGH,CRITICAL --ignore-unfixed --pkg-types="os,library" "e2e/provider-azure:test" 77 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PRs' 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' # every day at midnight 5 | workflow_dispatch: 6 | 7 | permissions: 8 | issues: write 9 | pull-requests: write 10 | 11 | jobs: 12 | stale: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Harden Runner 16 | uses: step-security/harden-runner@128a63446a954579617e875aaab7d2978154e969 # v2.4.0 17 | with: 18 | egress-policy: audit 19 | 20 | - uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8.0.0 21 | with: 22 | days-before-stale: 60 23 | days-before-close: 7 24 | operations-per-run: 100 25 | exempt-issue-labels: 'known-issue,enhancement' 26 | exempt-pr-labels: 'wip,under-review' 27 | stale-issue-message: 'This issue is stale because it has been open 14 days with no activity. Please comment or this will be closed in 7 days.' 28 | close-issue-message: 'This issue was closed because it has been stalled for 21 days with no activity. Feel free to re-open if you are experiencing the issue again.' 29 | stale-pr-message: 'This PR is stale because it has been open 14 days with no activity. Please comment or this will be closed in 7 days.' 30 | close-pr-message: 'This PR was closed because it has been stalled for 21 days with no activity. Feel free to re-open if it is ready for review.' 31 | -------------------------------------------------------------------------------- /.github/workflows/website.yaml: -------------------------------------------------------------------------------- 1 | name: generate_website 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - ".github/workflows/website.yaml" 9 | - "website/**" 10 | 11 | permissions: 12 | contents: write 13 | 14 | jobs: 15 | deploy: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Harden Runner 19 | uses: step-security/harden-runner@128a63446a954579617e875aaab7d2978154e969 # v2.4.0 20 | with: 21 | egress-policy: audit 22 | 23 | - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 24 | with: 25 | submodules: true # Fetch Hugo themes (true OR recursive) 26 | fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod 27 | 28 | - name: Setup Hugo 29 | uses: peaceiris/actions-hugo@16361eb4acea8698b220b76c0d4e84e1fd22c61d # v2.6.0 30 | with: 31 | hugo-version: "0.75.1" 32 | extended: true 33 | 34 | - name: Build 35 | run: | 36 | make -C website production-build 37 | - name: Deploy 38 | uses: peaceiris/actions-gh-pages@373f7f263a76c20808c831209c920827a82a2847 # v3.9.3 39 | with: 40 | github_token: ${{ secrets.GITHUB_TOKEN }} 41 | publish_dir: ./website/public 42 | destination_dir: ./docs 43 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "website/themes/docsy"] 2 | path = website/themes/docsy 3 | url = https://github.com/google/docsy.git 4 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | timeout: 5m 3 | go: '1.24' 4 | 5 | linters: 6 | disable-all: true 7 | enable: 8 | - errorlint 9 | - goconst 10 | - gocyclo 11 | - gofmt 12 | - goimports 13 | - gosec 14 | - gosimple 15 | - govet 16 | - ineffassign 17 | - misspell 18 | - nakedret 19 | - prealloc 20 | - revive 21 | - staticcheck 22 | - unconvert 23 | - unused 24 | - whitespace 25 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # refer to https://goreleaser.com for more options 2 | version: 2 3 | builds: 4 | - skip: true 5 | release: 6 | prerelease: auto 7 | header: | 8 | ## {{.Tag}} - {{ time "2006-01-02" }} 9 | extra_files: 10 | - glob: deployment/*.yaml 11 | changelog: 12 | disable: false 13 | groups: 14 | - title: Bug Fixes 🐞 15 | regexp: ^.*fix[(\\w)]*:+.*$ 16 | - title: Build 🏭 17 | regexp: ^.*build[(\\w)]*:+.*$ 18 | - title: Code Refactoring 💎 19 | regexp: ^.*refactor[(\\w)]*:+.*$ 20 | - title: Code Style 🎶 21 | regexp: ^.*style[(\\w)]*:+.*$ 22 | - title: Continuous Integration 💜 23 | regexp: ^.*ci[(\\w)]*:+.*$ 24 | - title: Documentation 📘 25 | regexp: ^.*docs[(\\w)]*:+.*$ 26 | - title: Features 🌈 27 | regexp: ^.*feat[(\\w)]*:+.*$ 28 | - title: Maintenance 🔧 29 | regexp: ^.*chore[(\\w)]*:+.*$ 30 | - title: Performance Improvements 🚀 31 | regexp: ^.*perf[(\\w)]*:+.*$ 32 | - title: Revert Change ◀️ 33 | regexp: ^.*revert[(\\w)]*:+.*$ 34 | - title: Security Fix 🛡️ 35 | regexp: ^.*security[(\\w)]*:+.*$ 36 | - title: Testing 💚 37 | regexp: ^.*test[(\\w)]*:+.*$ 38 | -------------------------------------------------------------------------------- /.pipelines/e2e-job-azure.yaml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | 3 | pr: 4 | branches: 5 | include: 6 | - master 7 | - release-* 8 | paths: 9 | exclude: 10 | - docs/* 11 | - website/* 12 | - README.md 13 | 14 | pool: staging-pool-amd64-mariner-2 15 | 16 | jobs: 17 | - template: templates/e2e-test-azure.yaml 18 | parameters: 19 | osTypes: 20 | - "linux" 21 | - "windows" 22 | # TODO: re-enable this job after implementing automated ext release process 23 | # using https://github.com/Azure/secrets-store-csi-driver-provider-azure/issues/1382 for tracking 24 | # this will ensure any changes to provider works on arc extension too. 25 | # - template: templates/arc/e2e-extension-test.yaml 26 | -------------------------------------------------------------------------------- /.pipelines/nightly.yaml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | 3 | schedules: 4 | - cron: "0 0 * * *" 5 | always: true 6 | displayName: "Nightly Build & Test" 7 | branches: 8 | include: 9 | - master 10 | 11 | pool: staging-pool-amd64-mariner-2 12 | 13 | jobs: 14 | # to get image scan results on nightly runs 15 | - template: templates/unit-test.yaml 16 | - template: templates/e2e-test-kind.yaml 17 | - template: templates/load-test.yaml 18 | - template: templates/e2e-test-azure.yaml 19 | parameters: 20 | osTypes: 21 | - "linux" 22 | - "windows" 23 | testClusterUpgrade: true 24 | - template: templates/e2e-test-azure.yaml 25 | parameters: 26 | osTypes: 27 | - "linux" 28 | testWithGPU: true 29 | - template: templates/soak-test.yaml 30 | parameters: 31 | clusterConfigs: 32 | - "csi-secrets-store-soak-linux-aks" 33 | - "csi-secrets-store-soak-win-aks" 34 | - template: templates/arc/e2e-test-kind.yaml 35 | -------------------------------------------------------------------------------- /.pipelines/templates/aks-upgrade.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | #Get minor version of currently installed aks version. major.minor.patch -> major.minor 4 | minorVersion=$(az aks get-upgrades -n ${AZURE_CLUSTER_NAME} -g ${AZURE_CLUSTER_NAME} --query "controlPlaneProfile.kubernetesVersion" | jq 'split(".") | .[:2] | join(".")') 5 | echo "Minor version is - $minorVersion" 6 | 7 | #Get maximum non preview upgrade version from upgrade profiles that does not start with minorVersion(major.minor) 8 | upgradeVersion=$(az aks get-upgrades -n ${AZURE_CLUSTER_NAME} -g ${AZURE_CLUSTER_NAME} --query "max(controlPlaneProfile.upgrades[?isPreview==null && !(starts_with(kubernetesVersion, '$minorVersion'))].kubernetesVersion)" -otsv) 9 | 10 | echo "Upgrading to Kubernetes $upgradeVersion" 11 | az aks upgrade -g ${AZURE_CLUSTER_NAME} -n ${AZURE_CLUSTER_NAME} -k $upgradeVersion --yes 12 | 13 | # Sleep for 120 seconds to wait for nodes and pods to become ready 14 | sleep 2m 15 | kubectl wait --for=condition=ready node --all 16 | kubectl wait pod -n kube-system --for=condition=Ready --all 17 | kubectl get nodes -owide 18 | kubectl cluster-info 19 | displayName: "Upgrade kubernetes version" 20 | condition: succeeded() 21 | -------------------------------------------------------------------------------- /.pipelines/templates/arc/cluster-connect.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | az group create -n ${AZURE_CLUSTER_NAME} -l $(AZURE_CANARY_LOCATION) 4 | az connectedk8s connect -n ${AZURE_CLUSTER_NAME} -g ${AZURE_CLUSTER_NAME} --no-wait 5 | # It takes time for Arc pods to come up. Sometimes, in such cases helm might report unable to install helm release, but in fact Arc operators get installed and can connect to the cluster. Also, az connectedk8s connect will go through different phases (Connecting, Connected etc.) of installation. So to address both, we are checking the status later without waiting. 6 | echo "verifying cluster connectivity..." 7 | for i in $(seq 1 25); do 8 | provisioningState=$(az connectedk8s list --resource-group ${AZURE_CLUSTER_NAME} --query "[*].provisioningState" -otsv) 9 | connectivityStatus=$(az connectedk8s list --resource-group ${AZURE_CLUSTER_NAME} --query "[*].connectivityStatus" -otsv) 10 | if [ "$provisioningState" == "Succeeded" ] && [ "$connectivityStatus" == "Connected" ]; then 11 | echo "KinD cluster is 'Connected'" 12 | break 13 | else 14 | echo "Provisioning state - $provisioningState, Connectivity status - $connectivityStatus" 15 | sleep 1 16 | fi 17 | done 18 | if [ "$connectivityStatus" != "Connected" ]; then 19 | echo "failed to connect to the cluster." 20 | exit 1 21 | fi 22 | displayName: "connect KinD cluster" 23 | condition: succeeded() 24 | -------------------------------------------------------------------------------- /.pipelines/templates/arc/e2e-test-kind.yaml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: e2e_arc_kind 3 | pool: 4 | name: staging-pool-amd64-mariner-2 5 | demands: 6 | - ImageOverride -equals azcu-agent-amd64-mariner-2-cgv2-img 7 | variables: 8 | - name: AZURE_ENVIRONMENT_FILEPATH 9 | value: /etc/kubernetes/custom_environment.json 10 | - name: VOLUME_NAME 11 | value: cloudenvfile-vol 12 | - group: csi-secrets-store-e2e-kind 13 | steps: 14 | - template: ../az-login.yaml 15 | - template: setup.yaml 16 | - script: | 17 | make install-helm install-kubectl setup-kind 18 | displayName: "install dependencies and setup kind" 19 | condition: succeeded() 20 | env: 21 | SERVICE_ACCOUNT_ISSUER: $(SERVICE_ACCOUNT_ISSUER) 22 | SERVICE_ACCOUNT_KEYVAULT_NAME: $(SERVICE_ACCOUNT_KEYVAULT_NAME) 23 | - template: cluster-connect.yaml 24 | - template: extension-create.yaml 25 | parameters: 26 | azureClusterName: $(AZURE_CLUSTER_NAME) 27 | releaseTrain: preview 28 | configurationSettings: "'secrets-store-csi-driver.enableSecretRotation=true' \ 29 | 'secrets-store-csi-driver.rotationPollInterval=30s' \ 30 | 'secrets-store-csi-driver.syncSecret.enabled=true' \ 31 | 'linux.volumes[0].name=$(VOLUME_NAME)' \ 32 | 'linux.volumes[0].hostPath.path=$(AZURE_ENVIRONMENT_FILEPATH)' \ 33 | 'linux.volumes[0].hostPath.type=File' \ 34 | 'linux.volumeMounts[0].name=$(VOLUME_NAME)' \ 35 | 'linux.volumeMounts[0].mountPath=$(AZURE_ENVIRONMENT_FILEPATH)'" 36 | - template: ../e2e-test.yaml 37 | parameters: 38 | testName: "arc extension e2e test" 39 | ciKindCluster: true 40 | isArcTest: true 41 | - template: ../teardown.yaml 42 | -------------------------------------------------------------------------------- /.pipelines/templates/arc/extension-create.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: azureClusterName 3 | type: string 4 | - name: extensionVersion 5 | type: string 6 | default: "" 7 | - name: releaseTrain 8 | type: string 9 | - name: configurationSettings 10 | type: string 11 | 12 | steps: 13 | - script: | 14 | echo "Installing extension..." 15 | echo "version - '${{ parameters.extensionVersion }}'" 16 | if [[ "${{ parameters.extensionVersion }}" != "" ]]; then 17 | EXTRA_ARGS="--version ${{ parameters.extensionVersion }}" 18 | fi 19 | az k8s-extension create \ 20 | --name ${{ parameters.azureClusterName }} \ 21 | --extension-type Microsoft.AzureKeyVaultSecretsProvider \ 22 | --scope cluster \ 23 | --cluster-name ${{ parameters.azureClusterName }} \ 24 | --resource-group ${{ parameters.azureClusterName }} \ 25 | --cluster-type connectedClusters \ 26 | --release-train ${{ parameters.releaseTrain }} \ 27 | --release-namespace kube-system \ 28 | --configuration-settings ${{ parameters.configurationSettings }} \ 29 | $EXTRA_ARGS 30 | 31 | # Arc extensions will go through different phases (Pending, Installed etc.) of installation. We want to make sure extension is 'Installed' before running e2e tests. 32 | echo "verifying extension install status..." 33 | for i in $(seq 1 30); do 34 | provisioningState=$(az k8s-extension show -c ${{ parameters.azureClusterName }} -t connectedClusters -n ${{ parameters.azureClusterName }} -g ${{ parameters.azureClusterName }} --query "provisioningState" -otsv) 35 | if [ "$provisioningState" == "Succeeded" ]; then 36 | echo "AzureKeyVaultSecretsProvider extension is 'Installed'" 37 | break 38 | else 39 | echo "Provisioning state: '$provisioningState'" 40 | sleep 2 41 | fi 42 | done 43 | if [ "$provisioningState" != "Succeeded" ]; then 44 | echo "failed to install extension." 45 | exit 1 46 | fi 47 | 48 | # validate if monitoring pod is Running 49 | echo "validating monitoring pod..." 50 | kubectl wait --for=condition=Ready pod -l app=arc-monitoring --timeout=1m -n kube-system 51 | 52 | helm ls -A 53 | helm get values ${{ parameters.azureClusterName }} -n kube-system 54 | kubectl get pods -n kube-system 55 | kubectl get pods -n azure-arc 56 | displayName: "install AzureKeyVaultSecretsProvider extension" 57 | condition: succeeded() 58 | -------------------------------------------------------------------------------- /.pipelines/templates/arc/setup.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | version: 0.12.0 3 | 4 | steps: 5 | - script: | 6 | az extension add --name connectedk8s 7 | az extension add --name k8s-extension --version 1.3.5 8 | echo "az version:" 9 | az version 10 | displayName: "add cli extensions" 11 | condition: succeeded() 12 | - script: | 13 | clusterName=sscd-arc-e2e-$(openssl rand -hex 6) 14 | echo "##vso[task.setvariable variable=AZURE_CLUSTER_NAME]$clusterName" 15 | echo "cluster name is set to - $clusterName" 16 | displayName: "set cluster name" 17 | condition: succeeded() 18 | - bash: | 19 | mkdir -p oras/ 20 | curl -LO https://github.com/deislabs/oras/releases/download/v${{ parameters.version }}/oras_${{ parameters.version }}_linux_amd64.tar.gz 21 | tar xvzf oras_${{ parameters.version }}_linux_amd64.tar.gz -C oras/ 22 | displayName: Install ORAS 23 | workingDirectory: $(Pipeline.Workspace) 24 | condition: succeeded() 25 | - bash: | 26 | tree $(Pipeline.Workspace) 27 | echo "##vso[task.setvariable variable=PATH]${PATH}:$(Pipeline.Workspace)/oras" 28 | displayName: Add oras to PATH 29 | - bash: oras version 30 | displayName: Print oras version 31 | condition: succeeded() 32 | -------------------------------------------------------------------------------- /.pipelines/templates/assign-user-identity.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: identitySubscriptionID 3 | displayName: 'user assigned identity subscription id' 4 | type: string 5 | 6 | steps: 7 | - script: | 8 | for i in $(seq 1 180); do 9 | VMSS_NAMES=$(az vmss list -g ${CLUSTER_RESOURCE_GROUP} --query "[].name" -o tsv) 10 | if [[ ! -z $VMSS_NAMES ]]; then 11 | break 12 | else 13 | echo "Waiting for VMSS names in ${CLUSTER_RESOURCE_GROUP}" 14 | sleep 3 15 | fi 16 | done 17 | 18 | echo "Assigning /subscriptions/${{ parameters.identitySubscriptionID }}/resourcegroups/$(USER_MSI_RESOURCE_GROUP)/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$(USER_MSI_NAME) to ${VMSS_NAMES}" 19 | # Assign user assigned identity to the VMSS 20 | echo "$VMSS_NAMES" | while IFS= read -r VMSS_NAME; do az vmss identity assign -g ${CLUSTER_RESOURCE_GROUP} -n $VMSS_NAME --identities /subscriptions/${{ parameters.identitySubscriptionID }}/resourcegroups/$(USER_MSI_RESOURCE_GROUP)/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$(USER_MSI_NAME) >> /dev/null; done 21 | displayName: "Assign User MSI to the VMSS" 22 | -------------------------------------------------------------------------------- /.pipelines/templates/az-login.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | az login -i > /dev/null 4 | az account set -s=$(SUBSCRIPTION_ID) 5 | displayName: "az login" 6 | condition: succeeded() 7 | -------------------------------------------------------------------------------- /.pipelines/templates/build-images.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: registry 3 | type: string 4 | default: "" 5 | - name: ciKindCluster 6 | type: boolean 7 | default: false 8 | 9 | steps: 10 | - template: az-login.yaml 11 | - script: | 12 | if [[ ${{ parameters.ciKindCluster }} == True ]]; then 13 | export CI_KIND_CLUSTER=true 14 | fi 15 | 16 | if [[ -n "${{ parameters.registry }}" ]]; then 17 | export REGISTRY=${{ parameters.registry }} 18 | echo "##vso[task.setvariable variable=REGISTRY]${REGISTRY}" 19 | fi 20 | 21 | # Generate image version 22 | if [[ ${{ parameters.ciKindCluster }} == True ]]; then 23 | IMAGE_VERSION="$(git describe --tags --exclude 'conformance-*' $(git rev-list --tags --max-count=1))-$(git rev-parse --short HEAD)-e2e" 24 | elif [[ -n "${CLUSTER_CONFIG:-}" ]]; then 25 | IMAGE_VERSION="$(git describe --tags --exclude 'conformance-*' $(git rev-list --tags --max-count=1))-$(git rev-parse --short HEAD)-${CLUSTER_CONFIG}" 26 | else 27 | IMAGE_VERSION="$(git describe --tags --exclude 'conformance-*' $(git rev-list --tags --max-count=1))-$(git rev-parse --short HEAD)-load" 28 | fi 29 | echo "Image version: ${IMAGE_VERSION}" 30 | export IMAGE_VERSION="${IMAGE_VERSION}" 31 | echo "##vso[task.setvariable variable=IMAGE_VERSION]${IMAGE_VERSION}" 32 | 33 | az acr login -n $(REGISTRY_NAME) 34 | make e2e-bootstrap 35 | displayName: "Build and push azure keyvault provider image" 36 | condition: succeeded() 37 | env: 38 | SERVICE_ACCOUNT_ISSUER: $(SERVICE_ACCOUNT_ISSUER) 39 | SERVICE_ACCOUNT_KEYVAULT_NAME: $(SERVICE_ACCOUNT_KEYVAULT_NAME) 40 | -------------------------------------------------------------------------------- /.pipelines/templates/cleanup-images.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: imageVersion 3 | type: string 4 | - name: registryRepo 5 | type: string 6 | - name: subscriptionId 7 | type: string 8 | - name: registryName 9 | type: string 10 | - name: isMultiArch 11 | type: boolean 12 | default: true 13 | 14 | steps: 15 | - script: | 16 | # an empty tag will result in deleting the whole repo. 17 | if [[ -n "${{ parameters.imageVersion }}" ]]; then 18 | # Allow errors in case the images do not exist 19 | set +e 20 | az account set -s=${{ parameters.subscriptionId }} 21 | az acr login -n ${{ parameters.registryName }} 22 | 23 | if [[ ${{ parameters.isMultiArch }} == True ]]; then 24 | for suffix in linux-amd64 linux-arm64 windows-1809-amd64 windows-ltsc2022-amd64 windows-ltsc2025-amd64; do 25 | az acr repository delete --name ${{ parameters.registryName }} --image ${{ parameters.registryRepo }}:${{ parameters.imageVersion }}-$suffix -y || true 26 | done 27 | fi 28 | 29 | echo "deleting image: ${{ parameters.registryRepo }}:${{ parameters.imageVersion }}" 30 | az acr repository delete --name ${{ parameters.registryName }} --image ${{ parameters.registryRepo }}:${{ parameters.imageVersion }} -y || true 31 | fi 32 | condition: always() 33 | displayName: "Cleanup" 34 | -------------------------------------------------------------------------------- /.pipelines/templates/cleanup-role-assignments.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - bash: | 3 | az role assignment delete --ids ${ROLE_ASSIGNMENT_IDS} > /dev/null 4 | condition: always() 5 | displayName: Cleanup role assignments 6 | -------------------------------------------------------------------------------- /.pipelines/templates/e2e-test-kind.yaml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: 3 | pool: 4 | name: staging-pool-amd64-mariner-2 5 | demands: 6 | - ImageOverride -equals azcu-agent-amd64-mariner-2-cgv2-img 7 | timeoutInMinutes: 20 8 | cancelTimeoutInMinutes: 5 9 | dependsOn: 10 | - lint 11 | - unit_test 12 | - build 13 | workspace: 14 | clean: all 15 | variables: 16 | - group: csi-secrets-store-e2e-kind 17 | strategy: 18 | matrix: 19 | kind_v1_30_10_helm: 20 | KIND_K8S_VERSION: v1.30.10 21 | IS_HELM_TEST: true 22 | kind_v1_31_6_helm: 23 | KIND_K8S_VERSION: v1.31.6 24 | IS_HELM_TEST: true 25 | kind_v1_32_3_helm: 26 | KIND_K8S_VERSION: v1.32.3 27 | IS_HELM_TEST: true 28 | kind_v1_30_10_deployment_manifest: 29 | KIND_K8S_VERSION: v1.30.10 30 | IS_HELM_TEST: false 31 | kind_v1_31_6_deployment_manifest: 32 | KIND_K8S_VERSION: v1.31.6 33 | IS_HELM_TEST: false 34 | kind_v1_32_3_deployment_manifest: 35 | KIND_K8S_VERSION: v1.32.3 36 | IS_HELM_TEST: false 37 | 38 | steps: 39 | # logging in to download the sa.pub and sa.key used for creating the kind cluster 40 | # with OIDC issuer enabled 41 | - template: az-login.yaml 42 | - script: | 43 | export REGISTRY="e2e" 44 | export IMAGE_VERSION=e2e-$(git rev-parse --short HEAD) 45 | echo "Image version: ${IMAGE_VERSION}" 46 | echo "##vso[task.setvariable variable=IMAGE_VERSION]${IMAGE_VERSION}" 47 | echo "##vso[task.setvariable variable=REGISTRY]${REGISTRY}" 48 | make e2e-bootstrap 49 | displayName: "Build image" 50 | env: 51 | CI_KIND_CLUSTER: true 52 | SERVICE_ACCOUNT_ISSUER: $(SERVICE_ACCOUNT_ISSUER) 53 | SERVICE_ACCOUNT_KEYVAULT_NAME: $(SERVICE_ACCOUNT_KEYVAULT_NAME) 54 | - script: | 55 | make e2e-test 56 | displayName: Run e2e tests 57 | env: 58 | AZURE_CLIENT_ID: $(AZURE_CLIENT_ID) 59 | AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET) 60 | KEY_NAME: $(KEY_NAME) 61 | KEY_VERSION: $(KEY_VERSION) 62 | KEYVAULT_NAME: $(KEYVAULT_NAME) 63 | SECRET_NAME: $(SECRET_NAME) 64 | TENANT_ID: $(TENANT_ID) 65 | CI_KIND_CLUSTER: true 66 | AZURE_ENVIRONMENT_FILEPATH: "/etc/kubernetes/custom_environment.json" 67 | 68 | - script: | 69 | make e2e-kind-cleanup 70 | displayName: Delete kind cluster 71 | condition: always() 72 | -------------------------------------------------------------------------------- /.pipelines/templates/e2e-test.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: testName 3 | type: string 4 | - name: testReleasedVersion 5 | type: boolean 6 | default: false 7 | - name: resetImageVersion 8 | type: boolean 9 | default: false 10 | - name: testClusterUpgrade 11 | type: boolean 12 | default: false 13 | - name: isBackwardCompatibilityTest 14 | type: boolean 15 | default: false 16 | - name: setRegistry 17 | type: boolean 18 | default: false 19 | - name: testWithGPU 20 | type: boolean 21 | default: false 22 | - name: ciKindCluster 23 | type: boolean 24 | default: false 25 | - name: isArcTest 26 | type: boolean 27 | default: false 28 | - name: osType 29 | type: string 30 | default: "" 31 | 32 | steps: 33 | - script: | 34 | # set AZURE_ENVIRONMENT_FILEPATH 35 | if [[ ! -z "$(AZURE_ENVIRONMENT_FILEPATH)" ]]; then 36 | export AZURE_ENVIRONMENT_FILEPATH=$(AZURE_ENVIRONMENT_FILEPATH) 37 | fi 38 | # Set TEST_WiNDOWS=true to run the test on Windows 39 | if [ "${{ parameters.osType }}" == "windows" ] ; then 40 | export TEST_WINDOWS=true 41 | fi 42 | # Set TEST_GPU=true to run the test on GPU nodepool 43 | if [[ ${{ parameters.testWithGPU }} == True ]]; then 44 | export TEST_GPU=true 45 | fi 46 | if [ "${{ parameters.setRegistry }}" == True ] ; then 47 | export REGISTRY="${REGISTRY:-$(REGISTRY_NAME).azurecr.io/k8s/csi/secrets-store}" 48 | fi 49 | if [ "${{ parameters.testReleasedVersion }}" == True ] ; then 50 | export HELM_CHART_DIR=https://azure.github.io/secrets-store-csi-driver-provider-azure/charts 51 | fi 52 | make e2e-test 53 | displayName: "${{ parameters.testName }}" 54 | env: 55 | AZURE_CLIENT_ID: $(AZURE_CLIENT_ID) 56 | AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET) 57 | KEY_NAME: $(KEY_NAME) 58 | KEY_VERSION: $(KEY_VERSION) 59 | KEYVAULT_NAME: $(KEYVAULT_NAME) 60 | RESOURCE_GROUP: $(RESOURCE_GROUP) 61 | SECRET_NAME: $(SECRET_NAME) 62 | SUBSCRIPTION_ID: $(SUBSCRIPTION_ID) 63 | TENANT_ID: $(TENANT_ID) 64 | IS_UPGRADE_TEST: ${{ parameters.testClusterUpgrade }} 65 | ${{ if parameters.resetImageVersion }}: 66 | CONFIG_IMAGE_VERSION: "" 67 | ${{ if parameters.isBackwardCompatibilityTest }}: 68 | IS_BACKWARD_COMPATIBILITY_TEST: ${{ parameters.isBackwardCompatibilityTest }} 69 | ${{ if parameters.ciKindCluster }}: 70 | CI_KIND_CLUSTER: ${{ parameters.ciKindCluster }} 71 | ${{ if parameters.isArcTest }}: 72 | IS_ARC_TEST: ${{ parameters.isArcTest }} 73 | # If the image is a released versions (i.e <= v1.3), it still doesn't support the 74 | # split cert/key feature, so we need to skip tests for those versions. 75 | ${{ if parameters.testReleasedVersion }}: 76 | GINKGO_SKIP: WriteCertAndKeyInSeparateFiles 77 | -------------------------------------------------------------------------------- /.pipelines/templates/get-logs.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: logUID 3 | displayName: 'uid for log directory' 4 | type: string 5 | 6 | steps: 7 | - script: | 8 | mkdir -p $(Build.ArtifactStagingDirectory)/logs/${{ parameters.logUID }} 9 | kubectl logs -n kube-system -l app.kubernetes.io/name=secrets-store-csi-driver -c secrets-store --tail=-1 > $(Build.ArtifactStagingDirectory)/logs/${{ parameters.logUID }}/secrets-store-csi-driver.json 10 | kubectl logs -n kube-system -l app.kubernetes.io/name=secrets-store-csi-driver -c node-driver-registrar --tail=-1 > $(Build.ArtifactStagingDirectory)/logs/${{ parameters.logUID }}/node-driver-registrar.json 11 | kubectl logs -n kube-system -l app.kubernetes.io/name=secrets-store-csi-driver -c liveness-probe --tail=-1 > $(Build.ArtifactStagingDirectory)/logs/${{ parameters.logUID }}/liveness-probe.json 12 | kubectl logs -n kube-system -l app.kubernetes.io/name=csi-secrets-store-provider-azure --tail=-1 > $(Build.ArtifactStagingDirectory)/logs/${{ parameters.logUID }}/csi-secrets-store-provider-azure.json 13 | 14 | kubectl get pods -A -o wide 15 | condition: always() 16 | displayName: "Get logs" 17 | - task: PublishBuildArtifacts@1 18 | displayName: Save artifacts 19 | condition: succeededOrFailed() 20 | -------------------------------------------------------------------------------- /.pipelines/templates/publish-load-test-result.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: PublishTestResults@2 3 | displayName: Publish Test Results 4 | inputs: 5 | testResultsFormat: "JUnit" 6 | testResultsFiles: $(perf-tests.repo.path)/_artifacts/junit.xml 7 | condition: succeededOrFailed() 8 | 9 | - script: | 10 | # rename : to - 11 | for f in *:*; do mv -v "$f" $(echo "$f" | tr ':' '-'); done 12 | mkdir -p $(Build.ArtifactStagingDirectory)/results 13 | cp * $(Build.ArtifactStagingDirectory)/results 14 | workingDirectory: "$(perf-tests.repo.path)/_artifacts" 15 | displayName: Copy artifacts 16 | condition: succeededOrFailed() 17 | - task: PublishBuildArtifacts@1 18 | displayName: Save artifacts 19 | condition: succeededOrFailed() 20 | -------------------------------------------------------------------------------- /.pipelines/templates/role-assignment.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | ASSIGNEE_OBJECT_ID="$(az identity show -g $(CLUSTER_RESOURCE_GROUP) -n ${AZURE_CLUSTER_NAME}-agentpool --query principalId -otsv)" 4 | echo "ASSIGNEE_OBJECT_ID='${ASSIGNEE_OBJECT_ID}'" 5 | echo "##vso[task.setvariable variable=ASSIGNEE_OBJECT_ID]${ASSIGNEE_OBJECT_ID}" 6 | 7 | ROLE_ASSIGNMENT_IDS="" 8 | 9 | az role assignment create --assignee-object-id "${ASSIGNEE_OBJECT_ID}" --assignee-principal-type "ServicePrincipal" --role "Virtual Machine Contributor" --scope "/subscriptions/$(SUBSCRIPTION_ID)/resourcegroups/$(CLUSTER_RESOURCE_GROUP)" 10 | az role assignment create --assignee-object-id "${ASSIGNEE_OBJECT_ID}" --assignee-principal-type "ServicePrincipal" --role "Managed Identity Operator" --scope "/subscriptions/$(SUBSCRIPTION_ID)/resourcegroups/$(CLUSTER_RESOURCE_GROUP)" 11 | 12 | if [[ -n "$(USER_MSI_RESOURCE_GROUP)" ]]; then 13 | ID="$(az role assignment create --assignee-object-id "${ASSIGNEE_OBJECT_ID}" --assignee-principal-type "ServicePrincipal" --role "Managed Identity Operator" --scope "/subscriptions/$(SUBSCRIPTION_ID)/resourcegroups/$(USER_MSI_RESOURCE_GROUP)" --query id -otsv)" 14 | ROLE_ASSIGNMENT_IDS+="${ID} " 15 | fi 16 | 17 | echo "##vso[task.setvariable variable=ROLE_ASSIGNMENT_IDS]${ROLE_ASSIGNMENT_IDS}" 18 | displayName: "Add role assignment" 19 | -------------------------------------------------------------------------------- /.pipelines/templates/soak-test.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: clusterConfigs 3 | type: object 4 | 5 | jobs: 6 | - ${{ each clusterConfig in parameters.clusterConfigs }}: 7 | - job: 8 | displayName: ${{ format('soak/{0}', clusterConfig) }} 9 | timeoutInMinutes: 60 10 | cancelTimeoutInMinutes: 5 11 | workspace: 12 | clean: all 13 | variables: 14 | - group: csi-secrets-store-e2e 15 | - group: ${{ format('{0}', clusterConfig) }} 16 | - name: CLUSTER_CONFIG 17 | value: ${{ format('{0}', clusterConfig) }} 18 | - name: IS_SOAK_TEST 19 | value: "true" 20 | steps: 21 | - script: | 22 | # Download kubectl 23 | echo "Installing kubectl..." 24 | curl -LO https://dl.k8s.io/release/`curl -sL https://dl.k8s.io/release/stable.txt`/bin/linux/amd64/kubectl 25 | chmod +x kubectl 26 | sudo mv kubectl /usr/local/bin/ 27 | displayName: "Install kubectl" 28 | 29 | - script: | 30 | # Download kubeconfig for soak cluster 31 | az login -i > /dev/null 32 | az account set -s=$(SUBSCRIPTION_ID) 33 | 34 | az aks get-credentials \ 35 | --resource-group $(CLUSTER_RESOURCE_GROUP) \ 36 | --name $(CLUSTER_CONFIG) 37 | displayName: "Set KUBECONFIG" 38 | 39 | - ${{ if eq(clusterConfig, 'csi-secrets-store-soak-linux-aks') }}: 40 | - script: | 41 | make install-helm 42 | displayName: "Install helm" 43 | 44 | - script: | 45 | kubectl wait --for=condition=ready node --all 46 | kubectl wait pod -n kube-system --for=condition=Ready --all 47 | kubectl get nodes -o wide 48 | displayName: "Check cluster's health" 49 | 50 | - script: | 51 | export REGISTRY="${REGISTRY:-$(REGISTRY_NAME).azurecr.io/k8s/csi/secrets-store}" 52 | export IMAGE_VERSION="${IMAGE_VERSION}" 53 | make e2e-test 54 | env: 55 | AZURE_CLIENT_ID: $(AZURE_CLIENT_ID) 56 | AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET) 57 | KEY_NAME: $(KEY_NAME) 58 | KEY_VERSION: $(KEY_VERSION) 59 | KEYVAULT_NAME: $(KEYVAULT_NAME) 60 | RESOURCE_GROUP: $(RESOURCE_GROUP) 61 | SECRET_NAME: $(SECRET_NAME) 62 | SUBSCRIPTION_ID: $(SUBSCRIPTION_ID) 63 | TENANT_ID: $(TENANT_ID) 64 | ${{ if eq(clusterConfig, 'csi-secrets-store-soak-win-aks') }}: 65 | TEST_WINDOWS: true 66 | displayName: "Run E2E tests" 67 | 68 | - script: | 69 | kubectl top pods -n kube-system 70 | displayName: "Get pod metrics" 71 | condition: always() 72 | -------------------------------------------------------------------------------- /.pipelines/templates/teardown.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | az login -i > /dev/null 4 | echo "deleting '${AZURE_CLUSTER_NAME}' resource group" 5 | az group delete -n ${AZURE_CLUSTER_NAME} --subscription $(SUBSCRIPTION_ID) --yes --no-wait 6 | displayName: "Teardown cluster" 7 | condition: and(always(), eq(variables['TEARDOWN_CLUSTER'], 'true')) 8 | -------------------------------------------------------------------------------- /.pipelines/templates/unit-test.yaml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: lint 3 | timeoutInMinutes: 5 4 | workspace: 5 | clean: all 6 | steps: 7 | - script: make lint 8 | displayName: golangci-lint 9 | - script: make helm-lint 10 | displayName: helm lint 11 | - script: make shellcheck 12 | displayName: shellcheck 13 | - job: build 14 | timeoutInMinutes: 5 15 | workspace: 16 | clean: all 17 | steps: 18 | - script: make build build-windows 19 | displayName: build 20 | - job: unit_test 21 | timeoutInMinutes: 5 22 | workspace: 23 | clean: all 24 | steps: 25 | - script: make unit-test 26 | displayName: unit test 27 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Ref: https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners 2 | 3 | * @aramase @enj 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 5 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 6 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 7 | 8 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 9 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 10 | provided by the bot. You will only need to do this once across all repos using our CLA. 11 | 12 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 13 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 14 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/cbl-mariner/distroless/minimal:2.0 2 | ARG TARGETARCH 3 | COPY ./_output/${TARGETARCH}/secrets-store-csi-driver-provider-azure /bin/ 4 | 5 | LABEL maintainers="aramase" 6 | LABEL description="Secrets Store CSI Driver Provider Azure" 7 | 8 | ENTRYPOINT ["secrets-store-csi-driver-provider-azure"] 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [many more](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /arc/conformance/plugin/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG STEP_CLI_VERSION=0.22.0 2 | ARG STEP_CLI_IMAGE=smallstep/step-cli:${STEP_CLI_VERSION} 3 | FROM $STEP_CLI_IMAGE as step-cli 4 | 5 | FROM registry.k8s.io/build-image/debian-base:bullseye-v1.4.2@sha256:e6e8c911b1566556ae105d3600df04c600241f43b06d22c73592ebb446d60c49 6 | ARG KUBE_VERSION=v1.25.3 7 | ARG TARGETARCH 8 | 9 | # install dependencies 10 | RUN apt-get update -y && apt-get upgrade -y 11 | RUN DEBIAN_FRONTEND=noninteractive TZ=America/Los_Angeles apt-get install python3-pip bash curl apt-transport-https lsb-release jq gnupg -y --allow-change-held-packages && python3 -m pip install junit_xml 12 | 13 | # install helm 14 | RUN curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash 15 | 16 | # install azcli 17 | RUN curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /etc/apt/trusted.gpg.d/microsoft.asc.gpg && \ 18 | CLI_REPO=$(lsb_release -cs) && \ 19 | echo "deb [arch=${TARGETARCH}] https://packages.microsoft.com/repos/azure-cli/ ${CLI_REPO} main" \ 20 | > /etc/apt/sources.list.d/azure-cli.list && \ 21 | apt-get update && \ 22 | apt-get install -y azure-cli && \ 23 | rm -rf /var/lib/apt/lists/* 24 | 25 | # install kubectl 26 | RUN curl -LO https://dl.k8s.io/release/${KUBE_VERSION}/bin/linux/${TARGETARCH}/kubectl && \ 27 | chmod +x kubectl && \ 28 | mv kubectl /usr/local/bin/kubectl 29 | 30 | # install step cli to create self signed certificates 31 | COPY --from=step-cli /usr/local/bin/step /usr/local/bin/step 32 | 33 | COPY arc/conformance/plugin/arc_conformance.sh /arc/arc_conformance.sh 34 | COPY arc/conformance/plugin/setup_failure_handler.py /arc/setup_failure_handler.py 35 | COPY test/e2e/_output/${TARGETARCH}/e2e /arc/e2e 36 | 37 | RUN ["chmod", "+x", "/arc/arc_conformance.sh"] 38 | ENTRYPOINT ["/arc/arc_conformance.sh"] 39 | -------------------------------------------------------------------------------- /arc/conformance/plugin/conformance.yaml: -------------------------------------------------------------------------------- 1 | sonobuoy-config: 2 | driver: Job 3 | plugin-name: azure-arc-akv-secrets-provider 4 | result-format: junit 5 | spec: 6 | image: mcr.microsoft.com/oss/azure/secrets-store/provider-azure-arc-conformance:v1.0.1 7 | imagePullPolicy: IfNotPresent 8 | name: plugin 9 | resources: {} 10 | volumes: 11 | - name: results 12 | emptyDir: {} 13 | volumeMounts: 14 | - mountPath: /tmp/results 15 | name: results 16 | -------------------------------------------------------------------------------- /arc/conformance/plugin/setup_failure_handler.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from junit_xml import TestSuite, TestCase 3 | 4 | # Reading error message from error file 5 | with open('/tmp/results/error', 'r') as f: 6 | error_message = f.read() 7 | 8 | # Creating a junit report for setup failure 9 | test_case = TestCase('akv_secrets_provider_conformance_setup', 'akv_secrets_provider_conformance_setup') 10 | test_case.add_failure_info(error_message) 11 | test_cases = [test_case] 12 | test_suite = TestSuite("akv_secrets_provider_conformance", test_cases) 13 | 14 | with open('/tmp/results/results.xml', 'w') as f: 15 | TestSuite.to_file(f, [test_suite], prettyprint=False) 16 | 17 | # Exit with non-zero return code 18 | sys.exit(1) 19 | -------------------------------------------------------------------------------- /arc/portal_gallery_package/DeploymentTemplates/DefaultTemplate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "releaseTrain": { 6 | "defaultValue": "stable", 7 | "type": "String" 8 | }, 9 | "releaseNamespace": { 10 | "defaultValue": "kube-system", 11 | "type": "String" 12 | }, 13 | "extensionName": { 14 | "defaultValue": "akvsecretsprovider", 15 | "type": "String" 16 | }, 17 | "connectedClusterName": { 18 | "type": "String" 19 | }, 20 | "enableSecretRotation": { 21 | "defaultValue": "false", 22 | "type": "String" 23 | }, 24 | "rotationPollInterval": { 25 | "defaultValue": "2m", 26 | "type": "String" 27 | }, 28 | "enableSyncSecret": { 29 | "defaultValue": "false", 30 | "type": "String" 31 | }, 32 | "tagsByResource": { 33 | "type": "object", 34 | "defaultValue": {} 35 | } 36 | }, 37 | "variables": {}, 38 | "functions": [], 39 | "resources": [ 40 | { 41 | "type": "Microsoft.KubernetesConfiguration/extensions", 42 | "apiVersion": "2021-09-01", 43 | "name": "[parameters('extensionName')]", 44 | "scope": "[concat('Microsoft.Kubernetes/connectedClusters/', parameters('connectedClusterName'))]", 45 | "identity": { 46 | "type": "SystemAssigned" 47 | }, 48 | "tags": "[ if(contains(parameters('tagsByResource'), 'Microsoft.KubernetesConfiguration/extensions'), parameters('tagsByResource')['Microsoft.KubernetesConfiguration/extensions'], json('{}')) ]", 49 | "properties": { 50 | "extensionType": "Microsoft.AzureKeyVaultSecretsProvider", 51 | "releaseTrain": "[parameters('releaseTrain')]", 52 | "scope": { 53 | "cluster": { 54 | "releaseNamespace": "[parameters('releaseNamespace')]" 55 | } 56 | }, 57 | "configurationSettings": { 58 | "secrets-store-csi-driver.enableSecretRotation": "[parameters('enableSecretRotation')]", 59 | "secrets-store-csi-driver.rotationPollInterval": "[parameters('rotationPollInterval')]", 60 | "secrets-store-csi-driver.syncSecret.enabled": "[parameters('enableSyncSecret')]" 61 | } 62 | } 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /arc/portal_gallery_package/Icons/Large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/869b250595dc26dc663f54c06b6134e5bbd2b449/arc/portal_gallery_package/Icons/Large.png -------------------------------------------------------------------------------- /arc/portal_gallery_package/Icons/Medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/869b250595dc26dc663f54c06b6134e5bbd2b449/arc/portal_gallery_package/Icons/Medium.png -------------------------------------------------------------------------------- /arc/portal_gallery_package/Icons/Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/869b250595dc26dc663f54c06b6134e5bbd2b449/arc/portal_gallery_package/Icons/Small.png -------------------------------------------------------------------------------- /arc/portal_gallery_package/Icons/Wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/869b250595dc26dc663f54c06b6134e5bbd2b449/arc/portal_gallery_package/Icons/Wide.png -------------------------------------------------------------------------------- /arc/portal_gallery_package/Manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://gallery.azure.com/schemas/2015-04-01/manifest.json#", 3 | "name": "AzureKeyVaultSecretsProvider", 4 | "publisher": "Microsoft", 5 | "version": "1.0.1", 6 | "displayName": "Azure Key Vault Secrets Provider", 7 | "publisherDisplayName": "Microsoft", 8 | "publisherLegalName": "Microsoft", 9 | "summary": "Azure Key Vault Secrets Provider provides integration of Azure Key Vault with a Kubernetes cluster", 10 | "longSummary": "Azure Key Vault Secrets Provider for Secrets Store CSI Driver allows you to get secret contents stored in an Azure Key Vault instance and use the Secrets Store CSI driver interface to mount them into Kubernetes pods.", 11 | "description": "Azure Key Vault Secrets Provider arc extension for Secrets Store CSI Driver allows you to get secret contents stored in an Azure Key Vault instance and use the Secrets Store CSI driver interface to mount them into Kubernetes pods. Some of the features of this extension includes mounting secrets, keys and certs to pod using a CSI Inline volume, supports pod portability with the SecretProviderClass custom resource definition, support for Linux, supports sync with Kubernetes Secrets, supports auto rotation of secrets.", 12 | "uiDefinition": { 13 | "path": "UIDefinition.json" 14 | }, 15 | "artifacts": [ 16 | { 17 | "name": "DefaultTemplate", 18 | "type": "Template", 19 | "path": "DeploymentTemplates\\DefaultTemplate.json", 20 | "isDefault": true 21 | }, 22 | { 23 | "name": "CreateUiDefinition", 24 | "type": "Custom", 25 | "path": "DeploymentTemplates\\CreateUiDefinition.json", 26 | "isDefault": false 27 | } 28 | ], 29 | "icons": { 30 | "small": "Icons\\Small.png", 31 | "medium": "Icons\\Medium.png", 32 | "wide": "Icons\\Wide.png", 33 | "large": "Icons\\Large.png" 34 | }, 35 | "links": [ 36 | { 37 | "displayName": "Documentation", 38 | "uri": "https://docs.microsoft.com/en-us/azure/azure-arc/kubernetes/tutorial-akv-secrets-provider" 39 | } 40 | ], 41 | "screenshots": [], 42 | "categories": [ 43 | "containerservice-extension-connectedclusters", 44 | "hideFromSearch" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /arc/portal_gallery_package/UIDefinition.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://gallery.azure.com/schemas/2018-02-12/UIDefinition.json#", 3 | "createDefinition": { 4 | "createBlade": { 5 | "name": "CreateUIDefinitionBlade", 6 | "extension": "Microsoft_Azure_CreateUIDef" 7 | } 8 | }, 9 | "initialData": {}, 10 | "options": {} 11 | } 12 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | 3 | pr: 4 | branches: 5 | include: 6 | - master 7 | - release-* 8 | paths: 9 | exclude: 10 | - docs/* 11 | - website/* 12 | - README.md 13 | - .github/* 14 | 15 | pool: staging-pool-amd64-mariner-2 16 | 17 | jobs: 18 | - template: .pipelines/templates/unit-test.yaml 19 | - template: .pipelines/templates/e2e-test-kind.yaml 20 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: secrets-store-csi-driver 3 | repository: https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts 4 | version: 1.5.0 5 | digest: sha256:21eab3137b2b4b4ec060073fab1c1d48adfda6e64d8e8eeaf4a434bdae32b444 6 | generated: "2025-04-14T20:57:20.381405874Z" 7 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: csi-secrets-store-provider-azure 3 | version: 1.7.0 4 | appVersion: 1.7.0 5 | kubeVersion: ">=1.16.0-0" 6 | description: A Helm chart to install the Secrets Store CSI Driver and the Azure Keyvault Provider inside a Kubernetes cluster. 7 | sources: 8 | - https://github.com/Azure/secrets-store-csi-driver-provider-azure 9 | home: https://github.com/Azure/secrets-store-csi-driver-provider-azure 10 | maintainers: 11 | - name: Anish Ramasekar 12 | email: anish.ramasekar@gmail.com 13 | dependencies: 14 | - name: secrets-store-csi-driver 15 | repository: https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts 16 | version: 1.5.0 17 | condition: secrets-store-csi-driver.install 18 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/config/fluentd-kubernetes.conf: -------------------------------------------------------------------------------- 1 | # This file collects and filters all Kubernetes container logs. Should rarely need to modify it. 2 | 3 | # Do not directly collect fluentd's own logs to avoid infinite loops. 4 | 9 | 10 | 11 | @type tail 12 | @log_level debug 13 | path /var/log/containers/*.log 14 | pos_file /var/log/fluentd-containers.log.pos 15 | tag kubernetes.* 16 | read_from_head true 17 | 18 | @type multi_format 19 | # Read logs in JSON format for Kubernetes v1.18- 20 | 21 | format json 22 | time_format "%Y-%m-%dT%H:%M:%S.%NZ" 23 | keep_time_key true 24 | 25 | # Reads logs in CRI format for Kubernetes v1.19+ 26 | # The CRI format is documented here: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/node/kubelet-cri-logging.md 27 | 28 | format regexp 29 | expression /^(? 33 | 34 | 35 | 36 | 37 | @type kubernetes_metadata 38 | 39 | 40 | # Exclude events from Geneva containers since they just seem to echo events from other containers 41 | 42 | @type grep 43 | 44 | key log 45 | pattern .* 46 | 47 | 48 | 49 | # Flatten fields nested within the 'log' field if it is JSON 50 | 51 | @type parser 52 | key_name log 53 | 54 | @type json 55 | json_parser json 56 | 57 | reserve_data true # this preserves fields from the original record 58 | remove_key_name_field true # this removes the log field if successfully parsed as JSON 59 | reserve_time # the time was already parsed in the source, we don't want to overwrite it with current time. 60 | emit_invalid_record_to_error false # In case of unparsable log lines or CRI logs. Keep fluentd's error log clean 61 | 62 | 63 | # Flatten fields nested within the 'kubernetes' field and remove unnecessary fields 64 | 65 | @type record_transformer 66 | enable_ruby 67 | 68 | ContainerName ${record["kubernetes"]["container_name"]} 69 | NamespaceName ${record["kubernetes"]["namespace_name"]} 70 | PodName ${record["kubernetes"]["pod_name"]} 71 | Node ${record["kubernetes"]["host"]} 72 | MasterUrl ${record["kubernetes"]["master_url"]} 73 | 74 | # The logtag field is used in CRI to support multi-line logs. It is usually noise, so remove by default. 75 | # https://github.com/kubernetes/community/blob/master/contributors/design-proposals/node/kubelet-cri-logging.md 76 | remove_keys docker,kubernetes,stream,logtag 77 | 78 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/config/fluentd.conf: -------------------------------------------------------------------------------- 1 | @include kubernetes.conf 2 | 3 | # Retag to prefix driver container events with akvssd 4 | 5 | @type rewrite_tag_filter 6 | 7 | key ContainerName 8 | pattern ^(.+)$ 9 | tag akvssd.$1 10 | 11 | 12 | 13 | # Retag to prefix provider container events with akvssp 14 | 15 | @type rewrite_tag_filter 16 | 17 | key ContainerName 18 | pattern ^(.+)$ 19 | tag akvssp.$1 20 | 21 | 22 | 23 | # Send akvssd events to MDSD 24 | 25 | @type mdsd 26 | @log_level info 27 | djsonsocket /var/run/mdsd/default_djson.socket # Full path to mdsd dynamic json socket file 28 | acktimeoutms 5000 # max time in milliseconds to wait for mdsd acknowledge response. If 0, no wait. 29 | mdsd_tag_regex_patterns ["^akvssd"] # fluentd tag patterns whose match will be used as mdsd source name 30 | num_threads 1 31 | buffer_chunk_limit 1000k 32 | buffer_type file 33 | buffer_path /var/log/td-agent/buffer/out_akvssd*.buffer 34 | buffer_queue_limit 128 35 | flush_interval 10s 36 | retry_limit 3 37 | retry_wait 10s 38 | 39 | 40 | # Send akvssp events to MDSD 41 | 42 | @type mdsd 43 | @log_level info 44 | djsonsocket /var/run/mdsd/default_djson.socket # Full path to mdsd dynamic json socket file 45 | acktimeoutms 5000 # max time in milliseconds to wait for mdsd acknowledge response. If 0, no wait. 46 | mdsd_tag_regex_patterns ["^akvssp"] # fluentd tag patterns whose match will be used as mdsd source name 47 | num_threads 1 48 | buffer_chunk_limit 1000k 49 | buffer_type file 50 | buffer_path /var/log/td-agent/buffer/out_akvssp*.buffer 51 | buffer_queue_limit 128 52 | flush_interval 10s 53 | retry_limit 3 54 | retry_wait 10s 55 | 56 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/config/gcstenant-conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "ServiceArguments": { 3 | "Version": "1.0" 4 | }, 5 | 6 | "UserArguments": { 7 | "GcsAuthIdType": "AuthMSIToken", 8 | "GcsEnvironment": "DiagnosticsPROD", 9 | "GcsGenevaAccount": "akvsecretsprovider", 10 | "GcsNamespace": "akvsecretsprovider", 11 | "GenevaConfigVersion": "2.2", 12 | "GcsRegion": "westus2" 13 | }, 14 | "EndpointConfigurations": [ 15 | { 16 | "EndpointPath": "", 17 | "EndpointPort": 15000, 18 | "EndpointOtlp": "localhost:4319" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/config/telegraf-conf.tmpl: -------------------------------------------------------------------------------- 1 | [agent] 2 | interval = "300s" 3 | flush_interval = "10s" 4 | metric_batch_size = 250 5 | metric_buffer_limit = 1000 6 | 7 | [[inputs.prometheus]] 8 | metric_version = 2 9 | 10 | monitor_kubernetes_pods = true 11 | kubernetes_label_selector = "app.kubernetes.io/name=csi-secrets-store-provider-azure" 12 | monitor_kubernetes_pods_namespace = "{{ .Release.Namespace }}" 13 | bearer_token = "/var/run/secrets/kubernetes.io/serviceaccount/token" 14 | tls_ca = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" 15 | insecure_skip_verify = true 16 | timeout = "15s" 17 | 18 | [[inputs.prometheus]] 19 | metric_version = 2 20 | 21 | monitor_kubernetes_pods = true 22 | kubernetes_label_selector = "app.kubernetes.io/name=secrets-store-csi-driver" 23 | monitor_kubernetes_pods_namespace = "{{ .Release.Namespace }}" 24 | bearer_token = "/var/run/secrets/kubernetes.io/serviceaccount/token" 25 | tls_ca = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" 26 | insecure_skip_verify = true 27 | timeout = "15s" 28 | 29 | [[outputs.http]] 30 | ## URL is the address to send metrics to 31 | url = "http://127.0.0.1:8090/push" 32 | 33 | ## Data format to output. 34 | data_format = "prometheusremotewrite" 35 | 36 | [outputs.http.headers] 37 | Content-Type = "application/x-protobuf" 38 | Content-Encoding = "snappy" 39 | X-Prometheus-Remote-Write-Version = "0.1.0" 40 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "sscdpa.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "sscdpa.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Common labels for helm resources 29 | */}} 30 | {{- define "sscdpa.common.labels" -}} 31 | app.kubernetes.io/instance: "{{ .Release.Name }}" 32 | app.kubernetes.io/managed-by: "{{ .Release.Service }}" 33 | app.kubernetes.io/version: "{{ .Chart.AppVersion }}" 34 | helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" 35 | {{- end -}} 36 | 37 | {{/* 38 | Standard labels for helm resources 39 | */}} 40 | {{- define "sscdpa.labels" -}} 41 | labels: 42 | {{ include "sscdpa.common.labels" . | indent 2 }} 43 | app.kubernetes.io/name: "{{ template "sscdpa.name" . }}" 44 | app: {{ template "sscdpa.name" . }} 45 | {{- end -}} 46 | 47 | {{- define "sscdpa.psp.fullname" -}} 48 | {{- printf "%s-psp" (include "sscdpa.fullname" .) -}} 49 | {{- end }} 50 | 51 | {{/* 52 | Arc specific templates 53 | */}} 54 | 55 | {{- define "sscdpa.arc.labels" -}} 56 | {{ include "sscdpa.common.labels" . }} 57 | app.kubernetes.io/name: "arc-{{ template "sscdpa.fullname" . }}" 58 | {{- end -}} 59 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/templates/arc-extension-identity.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.enableArcExtension }} 2 | {{- if and .Values.Azure.Identity.isEnabled .Values.arc.enableMonitoring }} 3 | apiVersion: clusterconfig.azure.com/v1beta1 4 | kind: AzureExtensionIdentity 5 | metadata: 6 | name: {{ .Values.Azure.Extension.Name }} 7 | namespace: azure-arc 8 | spec: 9 | serviceAccounts: 10 | - name: csi-secrets-store-provider-azure 11 | namespace: {{ .Release.Namespace }} 12 | tokenNamespace: {{ .Release.Namespace }} 13 | {{- end }} 14 | {{- end }} 15 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/templates/arc-monitoring-config.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.enableArcExtension }} 2 | {{- if .Values.arc.enableMonitoring }} 3 | apiVersion: v1 4 | kind: ConfigMap 5 | metadata: 6 | name: arc-telegraf-config 7 | namespace: {{ .Release.Namespace }} 8 | labels: 9 | {{ include "sscdpa.arc.labels" . | indent 4 }} 10 | data: 11 | telegraf.conf: | 12 | {{ (tpl ( .Files.Get "config/telegraf-conf.tmpl") .) | indent 4 }} 13 | --- 14 | apiVersion: v1 15 | kind: ConfigMap 16 | metadata: 17 | name: arc-mdm-config 18 | namespace: {{ .Release.Namespace }} 19 | labels: 20 | {{ include "sscdpa.arc.labels" . | indent 4 }} 21 | data: 22 | mdmconfig.json: "{\"imdsInfo\":[{ \"account\": \"akvsecretsprovider\" }]}" 23 | --- 24 | apiVersion: v1 25 | kind: ConfigMap 26 | metadata: 27 | name: arc-fluentd-conf 28 | namespace: {{ .Release.Namespace }} 29 | labels: 30 | {{ include "sscdpa.arc.labels" . | indent 4 }} 31 | data: 32 | fluentd.conf: {{ .Files.Get "config/fluentd.conf" | quote }} 33 | kubernetes.conf: {{ .Files.Get "config/fluentd-kubernetes.conf" | quote }} 34 | --- 35 | apiVersion: v1 36 | kind: ConfigMap 37 | metadata: 38 | name: arc-gcstenant-conf 39 | namespace: {{ .Release.Namespace }} 40 | labels: 41 | {{ include "sscdpa.arc.labels" . | indent 4 }} 42 | data: 43 | gcstenant1.json: {{ .Files.Get "config/gcstenant-conf.json" | quote }} 44 | {{- end }} 45 | {{- end }} 46 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/templates/arc-monitoring-service.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.enableArcExtension }} 2 | {{- if .Values.arc.enableMonitoring }} 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | name: arc-geneva-logging-service 7 | namespace: {{ .Release.Namespace }} 8 | labels: 9 | {{ include "sscdpa.arc.labels" . | indent 4 }} 10 | spec: 11 | type: ClusterIP 12 | ports: 13 | - name: mds-fluentd-tcp 14 | protocol: TCP 15 | port: 8130 16 | targetPort: 8130 17 | selector: 18 | app: "arc-monitoring" 19 | {{- end }} 20 | {{- end }} 21 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/templates/arc-proxy-secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.enableArcExtension }} 2 | {{- if .Values.Azure.proxySettings.isProxyEnabled }} 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: arc-proxy-config 7 | namespace: {{ .Release.Namespace }} 8 | type: Opaque 9 | data: 10 | {{- if .Values.Azure.proxySettings.httpProxy }} 11 | HTTP_PROXY: {{.Values.Azure.proxySettings.httpProxy | b64enc | quote}} 12 | {{- end }} 13 | {{- if .Values.Azure.proxySettings.httpsProxy }} 14 | HTTPS_PROXY: {{.Values.Azure.proxySettings.httpsProxy | b64enc | quote}} 15 | {{- end }} 16 | {{- if .Values.Azure.proxySettings.noProxy }} 17 | NO_PROXY: {{.Values.Azure.proxySettings.noProxy | b64enc | quote}} 18 | {{- end }} 19 | --- 20 | {{- if .Values.Azure.proxySettings.proxyCert }} 21 | apiVersion: v1 22 | kind: Secret 23 | metadata: 24 | name: arc-proxy-cert 25 | namespace: {{ .Release.Namespace }} 26 | type: Opaque 27 | data: 28 | proxy-cert.crt: {{.Values.Azure.proxySettings.proxyCert | b64enc | quote}} 29 | {{- end }} 30 | {{- end }} 31 | {{- end }} 32 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/templates/podsecuritypolicy.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.rbac.pspEnabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} 2 | apiVersion: policy/v1beta1 3 | kind: PodSecurityPolicy 4 | metadata: 5 | name: {{ template "sscdpa.psp.fullname" . }} 6 | {{ include "sscdpa.labels" . | indent 2 }} 7 | spec: 8 | seLinux: 9 | rule: RunAsAny 10 | privileged: true 11 | volumes: 12 | - hostPath 13 | - secret 14 | hostNetwork: true 15 | hostPorts: 16 | - min: 0 17 | max: 65535 18 | fsGroup: 19 | rule: RunAsAny 20 | runAsUser: 21 | rule: RunAsAny 22 | supplementalGroups: 23 | rule: RunAsAny 24 | {{- end }} 25 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/templates/role.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.rbac.pspEnabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: {{ template "sscdpa.psp.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | {{ include "sscdpa.labels" . | indent 2 }} 8 | rules: 9 | - apiGroups: [ 'policy' ] 10 | resources: [ 'podsecuritypolicies' ] 11 | verbs: [ 'use' ] 12 | resourceNames: 13 | - {{ template "sscdpa.psp.fullname" . }} 14 | {{- end }} 15 | --- 16 | {{- if .Values.enableArcExtension }} 17 | {{- if and .Values.Azure.Identity.isEnabled .Values.arc.enableMonitoring }} 18 | apiVersion: rbac.authorization.k8s.io/v1 19 | kind: Role 20 | metadata: 21 | name: arc-extension-identity-role 22 | namespace: {{ .Release.Namespace }} 23 | labels: 24 | {{ include "sscdpa.arc.labels" . | indent 4 }} 25 | rules: 26 | - apiGroups: [ 'clusterconfig.azure.com' ] 27 | resources: [ 'azureclusteridentityrequests' ] 28 | verbs: [ 'get', 'create' ] 29 | --- 30 | apiVersion: rbac.authorization.k8s.io/v1 31 | kind: ClusterRole 32 | metadata: 33 | name: arc-telegraf-role 34 | rules: 35 | - apiGroups: [""] 36 | resources: 37 | - pods 38 | verbs: ["get", "list", "watch"] 39 | --- 40 | apiVersion: rbac.authorization.k8s.io/v1 41 | kind: ClusterRole 42 | metadata: 43 | name: arc-geneva-role 44 | rules: 45 | - apiGroups: [""] 46 | resources: ["pods", "namespaces"] 47 | verbs: ["get", "watch", "list"] 48 | {{- end }} 49 | {{- end }} 50 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/templates/rolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.rbac.pspEnabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: RoleBinding 4 | metadata: 5 | name: {{ template "sscdpa.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | {{ include "sscdpa.labels" . | indent 2 }} 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: Role 11 | name: {{ template "sscdpa.psp.fullname" . }} 12 | subjects: 13 | - kind: ServiceAccount 14 | name: csi-secrets-store-provider-azure 15 | namespace: {{ .Release.Namespace }} 16 | {{- end }} 17 | --- 18 | {{- if .Values.enableArcExtension }} 19 | {{- if and .Values.Azure.Identity.isEnabled .Values.arc.enableMonitoring }} 20 | apiVersion: rbac.authorization.k8s.io/v1 21 | kind: RoleBinding 22 | metadata: 23 | name: arc-extension-identity-role-binding 24 | namespace: {{ .Release.Namespace }} 25 | labels: 26 | {{ include "sscdpa.arc.labels" . | indent 4 }} 27 | roleRef: 28 | apiGroup: rbac.authorization.k8s.io 29 | kind: Role 30 | name: arc-extension-identity-role 31 | subjects: 32 | - kind: ServiceAccount 33 | name: csi-secrets-store-provider-azure 34 | namespace: {{ .Release.Namespace }} 35 | --- 36 | apiVersion: rbac.authorization.k8s.io/v1 37 | kind: RoleBinding 38 | metadata: 39 | name: arc-telegraf-role-binding 40 | namespace: {{ .Release.Namespace }} 41 | labels: 42 | {{ include "sscdpa.arc.labels" . | indent 4 }} 43 | roleRef: 44 | apiGroup: rbac.authorization.k8s.io 45 | kind: ClusterRole 46 | name: arc-telegraf-role 47 | subjects: 48 | - kind: ServiceAccount 49 | name: csi-secrets-store-provider-azure 50 | namespace: {{ .Release.Namespace }} 51 | --- 52 | apiVersion: rbac.authorization.k8s.io/v1 53 | kind: ClusterRoleBinding 54 | metadata: 55 | name: arc-geneva-role-binding 56 | labels: 57 | {{ include "sscdpa.arc.labels" . | indent 4 }} 58 | roleRef: 59 | kind: ClusterRole 60 | name: arc-geneva-role 61 | apiGroup: rbac.authorization.k8s.io 62 | subjects: 63 | - kind: ServiceAccount 64 | name: csi-secrets-store-provider-azure 65 | namespace: {{ .Release.Namespace }} 66 | {{- end }} 67 | {{- end }} 68 | -------------------------------------------------------------------------------- /charts/csi-secrets-store-provider-azure/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.rbac.install }} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: csi-secrets-store-provider-azure 6 | namespace: {{ .Release.Namespace }} 7 | {{ include "sscdpa.labels" . | indent 2 }} 8 | {{ end }} 9 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: no 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: "50...100" 9 | status: 10 | project: 11 | default: 12 | threshold: 0.5 13 | patch: 14 | default: 15 | threshold: 0.5 16 | changes: no 17 | 18 | parsers: 19 | gcov: 20 | branch_detection: 21 | conditional: yes 22 | loop: yes 23 | method: no 24 | macro: no 25 | 26 | comment: 27 | layout: "header, diff" 28 | behavior: default 29 | require_changes: no 30 | 31 | ignore: 32 | - "test/**/*.go" 33 | -------------------------------------------------------------------------------- /deployment/provider-azure-installer-windows.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: csi-secrets-store-provider-azure 5 | --- 6 | apiVersion: apps/v1 7 | kind: DaemonSet 8 | metadata: 9 | labels: 10 | app: csi-secrets-store-provider-azure 11 | name: csi-secrets-store-provider-azure-windows 12 | spec: 13 | updateStrategy: 14 | type: RollingUpdate 15 | selector: 16 | matchLabels: 17 | app: csi-secrets-store-provider-azure 18 | template: 19 | metadata: 20 | labels: 21 | app: csi-secrets-store-provider-azure 22 | spec: 23 | serviceAccountName: csi-secrets-store-provider-azure 24 | containers: 25 | - name: provider-azure-installer 26 | image: mcr.microsoft.com/oss/v2/azure/secrets-store/provider-azure:v1.7.0 27 | imagePullPolicy: IfNotPresent 28 | args: 29 | - --endpoint=unix://C:\\provider\\azure.sock 30 | - --construct-pem-chain=true 31 | - --healthz-port=8989 32 | - --healthz-path=/healthz 33 | - --healthz-timeout=5s 34 | livenessProbe: 35 | httpGet: 36 | path: /healthz 37 | port: 8989 38 | failureThreshold: 3 39 | initialDelaySeconds: 5 40 | timeoutSeconds: 10 41 | periodSeconds: 30 42 | resources: 43 | requests: 44 | cpu: 100m 45 | memory: 200Mi 46 | limits: 47 | cpu: 100m 48 | memory: 200Mi 49 | ports: 50 | - containerPort: 8898 51 | name: metrics 52 | protocol: TCP 53 | volumeMounts: 54 | - mountPath: "C:\\provider" 55 | name: providervol 56 | affinity: 57 | nodeAffinity: 58 | requiredDuringSchedulingIgnoredDuringExecution: 59 | nodeSelectorTerms: 60 | - matchExpressions: 61 | - key: type 62 | operator: NotIn 63 | values: 64 | - virtual-kubelet 65 | volumes: 66 | - name: providervol 67 | hostPath: 68 | path: "C:\\k\\secrets-store-csi-providers" 69 | type: DirectoryOrCreate 70 | tolerations: 71 | - operator: Exists 72 | nodeSelector: 73 | kubernetes.io/os: windows 74 | -------------------------------------------------------------------------------- /deployment/provider-azure-installer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: csi-secrets-store-provider-azure 5 | --- 6 | apiVersion: apps/v1 7 | kind: DaemonSet 8 | metadata: 9 | labels: 10 | app: csi-secrets-store-provider-azure 11 | name: csi-secrets-store-provider-azure 12 | spec: 13 | updateStrategy: 14 | type: RollingUpdate 15 | selector: 16 | matchLabels: 17 | app: csi-secrets-store-provider-azure 18 | template: 19 | metadata: 20 | labels: 21 | app: csi-secrets-store-provider-azure 22 | spec: 23 | serviceAccountName: csi-secrets-store-provider-azure 24 | hostNetwork: true 25 | containers: 26 | - name: provider-azure-installer 27 | image: mcr.microsoft.com/oss/v2/azure/secrets-store/provider-azure:v1.7.0 28 | imagePullPolicy: IfNotPresent 29 | args: 30 | - --endpoint=unix:///provider/azure.sock 31 | - --construct-pem-chain=true 32 | - --healthz-port=8989 33 | - --healthz-path=/healthz 34 | - --healthz-timeout=5s 35 | livenessProbe: 36 | httpGet: 37 | path: /healthz 38 | port: 8989 39 | failureThreshold: 3 40 | initialDelaySeconds: 5 41 | timeoutSeconds: 10 42 | periodSeconds: 30 43 | resources: 44 | requests: 45 | cpu: 50m 46 | memory: 100Mi 47 | limits: 48 | cpu: 50m 49 | memory: 100Mi 50 | ports: 51 | - containerPort: 8898 52 | name: metrics 53 | protocol: TCP 54 | securityContext: 55 | allowPrivilegeEscalation: false 56 | readOnlyRootFilesystem: true 57 | runAsUser: 0 58 | capabilities: 59 | drop: 60 | - ALL 61 | volumeMounts: 62 | - mountPath: "/provider" 63 | name: providervol 64 | affinity: 65 | nodeAffinity: 66 | requiredDuringSchedulingIgnoredDuringExecution: 67 | nodeSelectorTerms: 68 | - matchExpressions: 69 | - key: type 70 | operator: NotIn 71 | values: 72 | - virtual-kubelet 73 | volumes: 74 | - name: providervol 75 | hostPath: 76 | path: "/var/run/secrets-store-csi-providers" 77 | tolerations: 78 | - operator: Exists 79 | nodeSelector: 80 | kubernetes.io/os: linux 81 | -------------------------------------------------------------------------------- /docs/Release_Management.md: -------------------------------------------------------------------------------- 1 | # Release Management 2 | > Note: This document is work in progress. 3 | 4 | ## Overview 5 | This document describes **Azure Key Vault Provider for Secrets Store CSI Driver** project release management, which talks about versioning, branching and cadence. 6 | 7 | ## Legend 8 | 9 | - **X.Y.Z** refers to the version (git tag) of AKV provider for Secrets Store CSI Driver that is released. This is the version of the AKV provider for Secrets Store CSI Driver image. 10 | 11 | - **Milestone** should be designed to include feature sets to accommodate monthly release cycles including test gates. GitHub milestones are used by maintainers to manage each release. PRs and Issues for each release should be created as part of a corresponding milestone. 12 | 13 | - **Test gates** should include soak tests and upgrade tests from the last minor version. 14 | 15 | 16 | ## Versioning 17 | This project strictly follows [semantic versioning](https://semver.org/spec/v2.0.0.html). All releases will be of the form _vX.Y.Z_ where X is the major version, Y is the minor version and Z is the patch version. Current releases do not have version prefix *_`v`_*. Starting 0.1.0 we will add the version prefix, viz., _`v0.1.0`_ 18 | 19 | ### Patch releases 20 | - Patch releases provide users with bug fixes and security fixes. They do not contain new features. 21 | 22 | ### Minor releases 23 | - Minor releases contain security and bug fixes as well as _**new features**_. 24 | 25 | - They are backwards compatible. 26 | 27 | ### Major releases 28 | - Major releases contain breaking changes. Breaking changes refer to schema changes and behavior changes of Secrets Store CSI Driver that may require a clean install during upgrade and it may introduce changes that could break backward compatibility. 29 | 30 | - Ideally we will avoid making multiple major releases to be always backward compatible, unless project evolves in important new directions and such release is necessary. 31 | 32 | 33 | ## Release Cadence and Branching 34 | - AKV provider for Secrets Store CSI Driver follows `monthly` release schedule. 35 | 36 | - A new release would be created in _`second week`_ of each month. This schedule not only allows us to do bug fixes, but also provides an opportunity to address underlying image vulnerabilities if any. 37 | 38 | - The release candidate (RC) images will be published from the master branch with tags. Once we validate RC image, we'll cut a release branch. 39 | 40 | - The new version is decided as per above guideline and release branch should be created from `master` with name `release-`. For eg. `release-v0.1.0`. Then build the image from release branch. 41 | 42 | - Any `fixes` or `patches` should be merged to master and then `cherry pick` to the release branch. 43 | 44 | 45 | ## Acknowledgement 46 | 47 | This document builds on the ideas and implementations of release processes from projects like [Gatekeeper](https://github.com/open-policy-agent/gatekeeper/blob/master/docs/Release_Management.md), [Helm](https://helm.sh/docs/topics/release_policy/#helm) and Kubernetes. -------------------------------------------------------------------------------- /docs/images/bottom-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/869b250595dc26dc663f54c06b6134e5bbd2b449/docs/images/bottom-left.png -------------------------------------------------------------------------------- /docs/images/container_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/869b250595dc26dc663f54c06b6134e5bbd2b449/docs/images/container_open.png -------------------------------------------------------------------------------- /docs/images/debug_console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/869b250595dc26dc663f54c06b6134e5bbd2b449/docs/images/debug_console.png -------------------------------------------------------------------------------- /docs/images/reopen-container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/869b250595dc26dc663f54c06b6134e5bbd2b449/docs/images/reopen-container.png -------------------------------------------------------------------------------- /docs/keyvault-test-setup.md: -------------------------------------------------------------------------------- 1 | # Commands for creating secrets, keys and certificates for testing 2 | 3 | ```bash 4 | export KEYVAULT_NAME= 5 | export LOCATION= 6 | export RESOURCE_GROUP= 7 | ``` 8 | 9 | ## Create Key Vault 10 | 11 | ```bash 12 | az keyvault create --resource-group "${RESOURCE_GROUP}" \ 13 | --location "${LOCATION}" \ 14 | --name "${KEYVAULT_NAME}" \ 15 | --sku premium 16 | ``` 17 | 18 | ## Create the secrets 19 | 20 | ```bash 21 | az keyvault secret set --vault-name $KEYVAULT_NAME --name secret1 --value test 22 | ``` 23 | 24 | ## Create the keys 25 | 26 | ### RSA Key 27 | 28 | ```bash 29 | az keyvault key create --vault-name $KEYVAULT_NAME --name key1 --kty RSA --size 2048 30 | ``` 31 | 32 | ### RSA-HSM Key 33 | 34 | ```bash 35 | az keyvault key create --vault-name $KEYVAULT_NAME --name rsahsmkey1 --kty RSA-HSM --size 2048 36 | ``` 37 | 38 | ### EC-HSM Key 39 | 40 | ```bash 41 | az keyvault key create --vault-name $KEYVAULT_NAME --name echsmkey1 --kty EC-HSM --curve P-256 42 | ``` 43 | 44 | ## Create the certificates 45 | 46 | The certificates are generated using [step-cli](https://smallstep.com/cli/) so the SAN can be specified. 47 | 48 | ### PEM and PKCS12 certificates 49 | 50 | ```bash 51 | step certificate create test.domain.com test.crt test.key --profile self-signed --subtle --san test.domain.com --kty RSA --not-after 86400h --no-password --insecure 52 | # export to pfx so we can import it into Azure Key Vault 53 | openssl pkcs12 -export -in test.crt -inkey test.key -out test.pfx -passout pass: 54 | az keyvault certificate import --vault-name $KEYVAULT_NAME --name pemcert1 --file test.pfx 55 | az keyvault certificate import --vault-name $KEYVAULT_NAME --name pkcs12cert1 --file test.pfx 56 | ``` 57 | 58 | ### ECC certificates 59 | 60 | ```bash 61 | step certificate create test.domain.com testec.crt testec.key --profile self-signed --subtle --san test.domain.com --kty EC --not-after 86400h --no-password --insecure 62 | # export to pfx so we can import it into Azure Key Vault 63 | openssl pkcs12 -export -in testec.crt -inkey testec.key -out testec.pfx -passout pass: 64 | az keyvault certificate import --vault-name $KEYVAULT_NAME --name ecccert1 --file testec.pfx 65 | ``` 66 | -------------------------------------------------------------------------------- /docs/remote-devcontainer.md: -------------------------------------------------------------------------------- 1 | # Contributing with VS Code Remote Container Extension 2 | 3 | We have laid out steps for contributing to the **Azure Key Vault Provider for Secrets Store CSI Driver** using the `VS Code - Remote Container Extension`. 4 | 5 | ## Prerequisites 6 | 7 | 1. Azure Subscription 8 | 9 | ## Fork and Clone Repository 10 | 11 | Before we dive into setting up a remote container environment, fork and clone the repository first. Once cloned, enter into the `root` folder of the project: 12 | 13 | ```bash 14 | cd secrets-store-csi-driver-provider-azure 15 | ``` 16 | 17 | ## VS Code with Remote Container Extension 18 | 19 | The [VS Code Remote Container Extension](https://code.visualstudio.com/docs/remote/containers) utilizes the `.devcontainer` folder to build a remote container that will have all necessary dependencies installed to contribute to the **Azure Key Vault Provider for Secrets Store CSI Driver**. 20 | 21 | ### Dependencies Included Inside The Remote Container 22 | 23 | - `yq and jq` command line utilities for manipulating YAML and JSON files 24 | - `Azure CLI` for access to your Azure Subscription 25 | - Your `.azure` folder on your host machine is mounted into the container, so you will be logged in to the same Azure Subscription. 26 | - `kind` for to allow configuring and using KinD clusters 27 | - `helm` is installed to allow deployment of the Secrets Store CSI Driver and Provider helm charts 28 | - Go 1.15+ 29 | 30 | ### Set Up 31 | 32 | 1. Open up the project in VS Code. 33 | 2. In the bottom-left corner of VS Code click on the remote window icon as shown below: 34 | 35 | ![open a remote window icon](/docs/images/bottom-left.png) 36 | 37 | 3. Select `Remote-Containers: Reopen in Container` from the drop-down list 38 | 39 | ![Reopen in Container](/docs/images/reopen-container.png) 40 | 41 | 4. The Azure Key Vault Provider should now be opened inside a Remote Container! 42 | - In the bottom-left you should see the tag updated to show: `Dev Container: Secrets Store CSI Driver Provider Azure` 43 | - Open the [integrated terminal](https://code.visualstudio.com/docs/editor/integrated-terminal) with `ctrl + `\`. 44 | - You can open up a bash shell in the container such as shown below: 45 | 46 | ![remote dev container](/docs/images/container_open.png) 47 | 48 | Your Environment is now set up using the VS Code Remote Devcontainer Extension. 49 | -------------------------------------------------------------------------------- /docs/sample/ingress-controller-tls/deployment-app-one.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: busybox-one 5 | labels: 6 | app: busybox-one 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: busybox-one 12 | template: 13 | metadata: 14 | labels: 15 | app: busybox-one 16 | spec: 17 | containers: 18 | - name: busybox 19 | image: registry.k8s.io/e2e-test-images/busybox:1.29-4 20 | command: 21 | - "/bin/sleep" 22 | - "10000" 23 | volumeMounts: 24 | - name: secrets-store-inline 25 | mountPath: "/mnt/secrets-store" 26 | readOnly: true 27 | volumes: 28 | - name: secrets-store-inline 29 | csi: 30 | driver: secrets-store.csi.k8s.io 31 | readOnly: true 32 | volumeAttributes: 33 | secretProviderClass: "azure-tls" 34 | nodePublishSecretRef: 35 | name: secrets-store-creds 36 | --- 37 | apiVersion: v1 38 | kind: Service 39 | metadata: 40 | name: busybox-one 41 | spec: 42 | type: ClusterIP 43 | ports: 44 | - port: 80 45 | selector: 46 | app: busybox-one 47 | -------------------------------------------------------------------------------- /docs/sample/ingress-controller-tls/deployment-app-two.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: busybox-two 5 | labels: 6 | app: busybox-two 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: busybox-two 12 | template: 13 | metadata: 14 | labels: 15 | app: busybox-two 16 | spec: 17 | containers: 18 | - name: busybox 19 | image: registry.k8s.io/e2e-test-images/busybox:1.29-4 20 | command: 21 | - "/bin/sleep" 22 | - "10000" 23 | volumeMounts: 24 | - name: secrets-store-inline 25 | mountPath: "/mnt/secrets-store" 26 | readOnly: true 27 | volumes: 28 | - name: secrets-store-inline 29 | csi: 30 | driver: secrets-store.csi.k8s.io 31 | readOnly: true 32 | volumeAttributes: 33 | secretProviderClass: "azure-tls" 34 | nodePublishSecretRef: 35 | name: secrets-store-creds 36 | --- 37 | apiVersion: v1 38 | kind: Service 39 | metadata: 40 | name: busybox-two 41 | spec: 42 | type: ClusterIP 43 | ports: 44 | - port: 80 45 | selector: 46 | app: busybox-two 47 | -------------------------------------------------------------------------------- /docs/sample/ingress-controller-tls/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: ingress-tls 5 | annotations: 6 | kubernetes.io/ingress.class: nginx 7 | nginx.ingress.kubernetes.io/rewrite-target: /$1 8 | spec: 9 | tls: 10 | - hosts: 11 | - demo.test.com 12 | secretName: ingress-tls-csi 13 | rules: 14 | - host: demo.test.com 15 | http: 16 | paths: 17 | - backend: 18 | service: 19 | name: busybox-one 20 | port: 21 | number: 80 22 | path: /(.*) 23 | - backend: 24 | service: 25 | name: busybox-two 26 | port: 27 | number: 80 28 | path: /two(/|$)(.*) 29 | -------------------------------------------------------------------------------- /docs/sample/ingress-controller-tls/readme.md: -------------------------------------------------------------------------------- 1 | # Using Secrets Store CSI to Enable NGINX Ingress Controller with TLS 2 | 3 | Manifests files demonstrating how [Secrets Store CSI driver and Azure Key Vault Provider](https://azure.github.io/secrets-store-csi-driver-provider-azure/) enable applications to work with NGINX Ingress Controller with TLS certificates stored in Key Vault. 4 | 5 | Checkout [Using Secrets Store CSI to Enable NGINX Ingress Controller with TLS](https://azure.github.io/secrets-store-csi-driver-provider-azure/docs/configurations/ingress-tls) for a walkthrough. 6 | -------------------------------------------------------------------------------- /examples/keyvault-certs/v1alpha1_secretproviderclass_certs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: secrets-store.csi.x-k8s.io/v1 2 | kind: SecretProviderClass 3 | metadata: 4 | name: azure-certs 5 | spec: 6 | provider: azure 7 | parameters: 8 | usePodIdentity: "false" 9 | useVMManagedIdentity: "false" 10 | userAssignedIdentityID: "" 11 | keyvaultName: "kvname" 12 | objects: | 13 | array: 14 | - | 15 | objectName: cert1 16 | objectType: cert # Setting objectType: cert will fetch and write only the certificate from keyvault 17 | objectVersion: "" # [OPTIONAL] object versions, default to latest if empty 18 | - | 19 | objectName: cert1 20 | objectAlias: cert1-pub-key 21 | objectType: key # Setting objectType: key will fetch and write only the public key from keyvault 22 | objectVersion: "" # [OPTIONAL] object versions, default to latest if empty 23 | - | 24 | objectName: cert1 25 | objectAlias: cert1-secret 26 | objectType: secret # Setting objectType: secret will fetch and write the certificate and private key from keyvault. The private key and certificate are written to a single file. 27 | objectVersion: "" # [OPTIONAL] object versions, default to latest if empty 28 | - | 29 | objectName: cert2-pfx 30 | objectAlias: cert2-secret-pfx 31 | objectType: secret 32 | objectVersion: "" # [OPTIONAL] object versions, default to latest if empty 33 | objectFormat: pfx # [OPTIONAL] the format of the Azure Key Vault object, supported types are pem and pfx. objectFormat: pfx is only supported with objectType: secret and PKCS12 or ECC certificates. Default format for certificates is pem. 34 | - | 35 | objectName: cert2-pfx 36 | objectAlias: cert2-secret-pfx-binary 37 | objectType: secret 38 | objectVersion: "" # [OPTIONAL] object versions, default to latest if empty 39 | objectFormat: pfx 40 | objectEncoding: base64 # Setting object encoding to base64 and object format to pfx will fetch and write the base64 decoded pfx binary 41 | tenantId: "tid" # the tenant ID of the KeyVault 42 | -------------------------------------------------------------------------------- /examples/keyvault-keys/v1alpha1_secretproviderclass_keys.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: secrets-store.csi.x-k8s.io/v1 2 | kind: SecretProviderClass 3 | metadata: 4 | name: azure-keys 5 | spec: 6 | provider: azure 7 | parameters: 8 | usePodIdentity: "false" 9 | keyvaultName: "kvname" 10 | cloudName: "" 11 | objects: | 12 | array: 13 | - | 14 | objectName: key1 15 | objectType: key # object types: secret, key or cert 16 | objectVersion: "" # [OPTIONAL] object versions, default to latest if empty 17 | - | 18 | objectName: key2 19 | objectType: key 20 | objectVersion: "" 21 | objectAlias: "KEY_ALIAS" # [OPTIONAL] specify the filename of the object when written to disk. Defaults to objectName if not provided. 22 | tenantId: "tid" 23 | -------------------------------------------------------------------------------- /examples/keyvault-secrets/v1alpha1_secretproviderclass_secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: secrets-store.csi.x-k8s.io/v1 2 | kind: SecretProviderClass 3 | metadata: 4 | name: azure-secrets 5 | spec: 6 | provider: azure 7 | parameters: 8 | usePodIdentity: "false" 9 | keyvaultName: "kvname" 10 | cloudName: "" 11 | objects: | 12 | array: 13 | - | 14 | objectName: secret1 15 | objectType: secret # object types: secret, key or cert 16 | objectVersion: "" # [OPTIONAL] object versions, default to latest if empty 17 | - | 18 | objectName: secret2 19 | objectType: secret 20 | objectVersion: "" 21 | objectAlias: "SECRET_ALIAS" # [OPTIONAL] specify the filename of the object when written to disk. Defaults to objectName if not provided. 22 | - | 23 | objectName: secret3 24 | objectType: secret 25 | objectVersion: "" 26 | objectEncoding: "SECRET_ALIAS" # [OPTIONAL] the encoding of the Azure Key Vault secret object. Supported types are utf-8, hex and base64. This option is supported only with objectType: secret. Default encoding is utf-8. 27 | tenantId: "tid" 28 | -------------------------------------------------------------------------------- /examples/kind/kind-demo.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CLIENT_ID=$1 4 | CLIENT_SECRET=$2 5 | 6 | # create kind cluster 7 | kind create cluster --name kind-csi-demo 8 | 9 | # install csi-secrets-store-provider-azure 10 | helm repo add csi-secrets-store-provider-azure https://azure.github.io/secrets-store-csi-driver-provider-azure/charts 11 | helm install csi-secrets-store-provider-azure/csi-secrets-store-provider-azure --generate-name 12 | 13 | # create secret in k8 14 | kubectl create secret generic secrets-store-creds --from-literal clientid="$CLIENT_ID" --from-literal clientsecret="$CLIENT_SECRET" 15 | 16 | # label the secret 17 | kubectl label secret secrets-store-creds secrets-store.csi.k8s.io/used=true 18 | 19 | # Deploy app 20 | kubectl apply -f v1alpha1_secretproviderclass.yaml 21 | kubectl apply -f pod-secrets-store-inline-volume-secretproviderclass.yaml 22 | 23 | # wait for deployment 24 | kubectl wait --for=condition=ready pods/busybox-secrets-store-inline --timeout=300s 25 | 26 | # validate 27 | kubectl exec busybox-secrets-store-inline ls /mnt/secrets-store/ 28 | -------------------------------------------------------------------------------- /examples/pod-identity/pod-inline-volume-pod-identity.yaml: -------------------------------------------------------------------------------- 1 | # This is a sample pod definition for using SecretProviderClass and aad-pod-identity to access Keyvault 2 | kind: Pod 3 | apiVersion: v1 4 | metadata: 5 | name: busybox-secrets-store-inline-podid 6 | labels: 7 | aadpodidbinding: "demo" # Set the label value to the selector defined in AzureIdentityBinding 8 | spec: 9 | containers: 10 | - name: busybox 11 | image: registry.k8s.io/e2e-test-images/busybox:1.29-4 12 | command: 13 | - "/bin/sleep" 14 | - "10000" 15 | volumeMounts: 16 | - name: secrets-store01-inline 17 | mountPath: "/mnt/secrets-store" 18 | readOnly: true 19 | volumes: 20 | - name: secrets-store01-inline 21 | csi: 22 | driver: secrets-store.csi.k8s.io 23 | readOnly: true 24 | volumeAttributes: 25 | secretProviderClass: "azure-kvname-podid" 26 | -------------------------------------------------------------------------------- /examples/pod-identity/v1alpha1_secretproviderclass_pod_identity.yaml: -------------------------------------------------------------------------------- 1 | # This is a SecretProviderClass example using aad-pod-identity to access Keyvault 2 | apiVersion: secrets-store.csi.x-k8s.io/v1 3 | kind: SecretProviderClass 4 | metadata: 5 | name: azure-kvname-podid 6 | spec: 7 | provider: azure 8 | parameters: 9 | usePodIdentity: "true" # Set to true for using aad-pod-identity to access keyvault 10 | keyvaultName: "kvname" 11 | cloudName: "" # [OPTIONAL for Azure] if not provided, azure environment will default to AzurePublicCloud 12 | objects: | 13 | array: 14 | - | 15 | objectName: secret1 16 | objectType: secret # object types: secret, key or cert 17 | objectVersion: "" # [OPTIONAL] object versions, default to latest if empty 18 | - | 19 | objectName: key1 20 | objectType: key 21 | objectVersion: "" 22 | tenantId: "tid" # the tenant ID of the KeyVault 23 | -------------------------------------------------------------------------------- /examples/service-principal/pod-inline-volume-service-principal.yaml: -------------------------------------------------------------------------------- 1 | # This is a sample pod definition for using SecretProviderClass and service-principal to access Keyvault 2 | kind: Pod 3 | apiVersion: v1 4 | metadata: 5 | name: busybox-secrets-store-inline 6 | spec: 7 | containers: 8 | - name: busybox 9 | image: registry.k8s.io/e2e-test-images/busybox:1.29-4 10 | command: 11 | - "/bin/sleep" 12 | - "10000" 13 | volumeMounts: 14 | - name: secrets-store-inline 15 | mountPath: "/mnt/secrets-store" 16 | readOnly: true 17 | volumes: 18 | - name: secrets-store-inline 19 | csi: 20 | driver: secrets-store.csi.k8s.io 21 | readOnly: true 22 | volumeAttributes: 23 | secretProviderClass: "azure-kvname" 24 | nodePublishSecretRef: # Only required when using service principal mode 25 | name: secrets-store-creds # Only required when using service principal mode. The name of the Kubernetes secret that contains the service principal credentials to access keyvault. 26 | -------------------------------------------------------------------------------- /examples/service-principal/v1alpha1_secretproviderclass_service_principal.yaml: -------------------------------------------------------------------------------- 1 | # This is a SecretProviderClass example using a service principal to access Keyvault 2 | apiVersion: secrets-store.csi.x-k8s.io/v1 3 | kind: SecretProviderClass 4 | metadata: 5 | name: azure-kvname 6 | spec: 7 | provider: azure 8 | parameters: 9 | usePodIdentity: "false" # [OPTIONAL] if not provided, will default to "false" 10 | keyvaultName: "kvname" # the name of the KeyVault 11 | cloudName: "" # [OPTIONAL for Azure] if not provided, azure environment will default to AzurePublicCloud 12 | objects: | 13 | array: 14 | - | 15 | objectName: secret1 16 | objectType: secret # object types: secret, key or cert 17 | objectVersion: "" # [OPTIONAL] object versions, default to latest if empty 18 | - | 19 | objectName: key1 20 | objectType: key 21 | objectVersion: "" 22 | tenantId: "tid" # the tenant ID of the KeyVault 23 | -------------------------------------------------------------------------------- /examples/sync-as-kubernetes-secret/deployment-synck8s.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: busybox-deployment 5 | labels: 6 | app: busybox 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: busybox 12 | template: 13 | metadata: 14 | labels: 15 | app: busybox 16 | spec: 17 | terminationGracePeriodSeconds: 0 18 | containers: 19 | - name: busybox 20 | image: registry.k8s.io/e2e-test-images/busybox:1.29-4 21 | command: 22 | - "/bin/sleep" 23 | - "10000" 24 | env: 25 | - name: SECRET_USERNAME 26 | valueFrom: 27 | secretKeyRef: 28 | name: foosecret 29 | key: username 30 | volumeMounts: 31 | - name: secrets-store-inline 32 | mountPath: "/mnt/secrets-store" 33 | readOnly: true 34 | volumes: 35 | - name: secrets-store-inline 36 | csi: 37 | driver: secrets-store.csi.k8s.io 38 | readOnly: true 39 | volumeAttributes: 40 | secretProviderClass: "azure-sync" 41 | nodePublishSecretRef: 42 | name: secrets-store-creds 43 | -------------------------------------------------------------------------------- /examples/sync-as-kubernetes-secret/dockerconfigjson_synck8s_v1alpha1_secretproviderclass.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: secrets-store.csi.x-k8s.io/v1 2 | kind: SecretProviderClass 3 | metadata: 4 | name: azure-docker-config 5 | spec: 6 | provider: azure 7 | secretObjects: # secretObjects defines the desired state of synced K8s secret objects 8 | - secretName: dockerconfig 9 | type: kubernetes.io/dockerconfigjson 10 | data: 11 | - objectName: dockerconfigjson 12 | key: .dockerconfigjson 13 | parameters: 14 | usePodIdentity: "false" 15 | keyvaultName: "kvname" # the name of the Keyvault 16 | objects: | 17 | array: 18 | - | 19 | objectName: dockerconfigjson 20 | objectType: secret # Setting the objectType to secret will retrieve the certificate and private key from keyvault. 21 | tenantId: "tid" # the tenant ID of the KeyVault 22 | -------------------------------------------------------------------------------- /examples/sync-as-kubernetes-secret/synck8s_v1alpha1_secretproviderclass.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: secrets-store.csi.x-k8s.io/v1 2 | kind: SecretProviderClass 3 | metadata: 4 | name: azure-sync 5 | spec: 6 | provider: azure 7 | secretObjects: # [OPTIONAL] SecretObject defines the desired state of synced K8s secret objects 8 | - secretName: foosecret 9 | type: Opaque 10 | labels: 11 | environment: "test" 12 | data: 13 | - objectName: secretalias # name of the mounted content to sync. this could be the object name or object alias. The mount is mandatory for the content to be synced as Kubernetes secret. 14 | key: username 15 | parameters: 16 | usePodIdentity: "false" # [OPTIONAL] if not provided, will default to "false" 17 | keyvaultName: "kvname" # the name of the KeyVault 18 | objects: | 19 | array: 20 | - | 21 | objectName: $SECRET_NAME 22 | objectType: secret # object types: secret, key or cert 23 | objectAlias: secretalias 24 | objectVersion: $SECRET_VERSION # [OPTIONAL] object versions, default to latest if empty 25 | - | 26 | objectName: $KEY_NAME 27 | objectType: key 28 | objectVersion: $KEY_VERSION 29 | tenantId: "tid" # the tenant ID of the KeyVault 30 | -------------------------------------------------------------------------------- /examples/sync-as-kubernetes-secret/tls_synck8s_v1alpha1_secretproviderclass.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: secrets-store.csi.x-k8s.io/v1 2 | kind: SecretProviderClass 3 | metadata: 4 | name: azure-tls 5 | spec: 6 | provider: azure 7 | secretObjects: # secretObjects defines the desired state of synced K8s secret objects 8 | - secretName: ingress-tls-csi 9 | type: kubernetes.io/tls 10 | data: 11 | - objectName: tls-cert 12 | key: tls.key 13 | - objectName: tls-cert 14 | key: tls.crt 15 | parameters: 16 | usePodIdentity: "false" 17 | keyvaultName: "kvname" # the name of the Keyvault 18 | objects: | 19 | array: 20 | - | 21 | objectName: tls-cert 22 | objectType: secret # Setting the objectType to secret will retrieve the certificate and private key from keyvault. Refer to https://azure.github.io/secrets-store-csi-driver-provider-azure/configurations/getting-certs-and-keys/ for more details. 23 | tenantId: "tid" # the tenant ID of the KeyVault 24 | -------------------------------------------------------------------------------- /examples/system-assigned-managed-identity/pod-inline-volume-system-assigned-identity.yaml: -------------------------------------------------------------------------------- 1 | # This is a sample pod definition for using SecretProviderClass and system-assigned identity to access Keyvault 2 | kind: Pod 3 | apiVersion: v1 4 | metadata: 5 | name: busybox-secrets-store-inline-system-msi 6 | spec: 7 | containers: 8 | - name: busybox 9 | image: registry.k8s.io/e2e-test-images/busybox:1.29-4 10 | command: 11 | - "/bin/sleep" 12 | - "10000" 13 | volumeMounts: 14 | - name: secrets-store01-inline 15 | mountPath: "/mnt/secrets-store" 16 | readOnly: true 17 | volumes: 18 | - name: secrets-store01-inline 19 | csi: 20 | driver: secrets-store.csi.k8s.io 21 | readOnly: true 22 | volumeAttributes: 23 | secretProviderClass: "azure-kvname-system-msi" 24 | -------------------------------------------------------------------------------- /examples/system-assigned-managed-identity/v1alpha1_secretproviderclass_system_assigned_identity.yaml: -------------------------------------------------------------------------------- 1 | # This is a SecretProviderClass example using system-assigned identity to access Keyvault 2 | apiVersion: secrets-store.csi.x-k8s.io/v1 3 | kind: SecretProviderClass 4 | metadata: 5 | name: azure-kvname-system-msi 6 | spec: 7 | provider: azure 8 | parameters: 9 | usePodIdentity: "false" 10 | useVMManagedIdentity: "true" # Set to true for using managed identity 11 | userAssignedIdentityID: "" # If empty, then defaults to use the system assigned identity on the VM 12 | keyvaultName: "kvname" 13 | cloudName: "" # [OPTIONAL for Azure] if not provided, azure environment will default to AzurePublicCloud 14 | objects: | 15 | array: 16 | - | 17 | objectName: secret1 18 | objectType: secret # object types: secret, key or cert 19 | objectVersion: "" # [OPTIONAL] object versions, default to latest if empty 20 | - | 21 | objectName: key1 22 | objectType: key 23 | objectVersion: "" 24 | tenantId: "tid" # the tenant ID of the KeyVault 25 | -------------------------------------------------------------------------------- /examples/user-assigned-managed-identity/pod-inline-volume-user-assigned-identity.yaml: -------------------------------------------------------------------------------- 1 | # This is a sample pod definition for using SecretProviderClass and user-assigned identity to access Keyvault 2 | kind: Pod 3 | apiVersion: v1 4 | metadata: 5 | name: busybox-secrets-store-inline-user-msi 6 | spec: 7 | containers: 8 | - name: busybox 9 | image: registry.k8s.io/e2e-test-images/busybox:1.29-4 10 | command: 11 | - "/bin/sleep" 12 | - "10000" 13 | volumeMounts: 14 | - name: secrets-store01-inline 15 | mountPath: "/mnt/secrets-store" 16 | readOnly: true 17 | volumes: 18 | - name: secrets-store01-inline 19 | csi: 20 | driver: secrets-store.csi.k8s.io 21 | readOnly: true 22 | volumeAttributes: 23 | secretProviderClass: "azure-kvname-user-msi" 24 | -------------------------------------------------------------------------------- /examples/user-assigned-managed-identity/v1alpha1_secretproviderclass_user_assigned_identity.yaml: -------------------------------------------------------------------------------- 1 | # This is a SecretProviderClass example using user-assigned identity to access Keyvault 2 | apiVersion: secrets-store.csi.x-k8s.io/v1 3 | kind: SecretProviderClass 4 | metadata: 5 | name: azure-kvname-user-msi 6 | spec: 7 | provider: azure 8 | parameters: 9 | usePodIdentity: "false" 10 | useVMManagedIdentity: "true" # Set to true for using managed identity 11 | userAssignedIdentityID: "" # Set the clientID of the user-assigned managed identity to use 12 | keyvaultName: "kvname" 13 | cloudName: "" # [OPTIONAL for Azure] if not provided, azure environment will default to AzurePublicCloud 14 | objects: | 15 | array: 16 | - | 17 | objectName: secret1 18 | objectType: secret # object types: secret, key or cert 19 | objectVersion: "" # [OPTIONAL] object versions, default to latest if empty 20 | - | 21 | objectName: key1 22 | objectType: key 23 | objectVersion: "" 24 | tenantId: "tid" # the tenant ID of the KeyVault 25 | -------------------------------------------------------------------------------- /images/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/869b250595dc26dc663f54c06b6134e5bbd2b449/images/demo.gif -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: secrets-store-csi-driver 3 | repository: https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts 4 | version: 1.5.0 5 | digest: sha256:21eab3137b2b4b4ec060073fab1c1d48adfda6e64d8e8eeaf4a434bdae32b444 6 | generated: "2025-04-14T20:57:20.381405874Z" 7 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: csi-secrets-store-provider-azure 3 | version: 1.7.0 4 | appVersion: 1.7.0 5 | kubeVersion: ">=1.16.0-0" 6 | description: A Helm chart to install the Secrets Store CSI Driver and the Azure Keyvault Provider inside a Kubernetes cluster. 7 | sources: 8 | - https://github.com/Azure/secrets-store-csi-driver-provider-azure 9 | home: https://github.com/Azure/secrets-store-csi-driver-provider-azure 10 | maintainers: 11 | - name: Anish Ramasekar 12 | email: anish.ramasekar@gmail.com 13 | dependencies: 14 | - name: secrets-store-csi-driver 15 | repository: https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts 16 | version: 1.5.0 17 | condition: secrets-store-csi-driver.install 18 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/config/fluentd-kubernetes.conf: -------------------------------------------------------------------------------- 1 | # This file collects and filters all Kubernetes container logs. Should rarely need to modify it. 2 | 3 | # Do not directly collect fluentd's own logs to avoid infinite loops. 4 | 9 | 10 | 11 | @type tail 12 | @log_level debug 13 | path /var/log/containers/*.log 14 | pos_file /var/log/fluentd-containers.log.pos 15 | tag kubernetes.* 16 | read_from_head true 17 | 18 | @type multi_format 19 | # Read logs in JSON format for Kubernetes v1.18- 20 | 21 | format json 22 | time_format "%Y-%m-%dT%H:%M:%S.%NZ" 23 | keep_time_key true 24 | 25 | # Reads logs in CRI format for Kubernetes v1.19+ 26 | # The CRI format is documented here: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/node/kubelet-cri-logging.md 27 | 28 | format regexp 29 | expression /^(? 33 | 34 | 35 | 36 | 37 | @type kubernetes_metadata 38 | 39 | 40 | # Exclude events from Geneva containers since they just seem to echo events from other containers 41 | 42 | @type grep 43 | 44 | key log 45 | pattern .* 46 | 47 | 48 | 49 | # Flatten fields nested within the 'log' field if it is JSON 50 | 51 | @type parser 52 | key_name log 53 | 54 | @type json 55 | json_parser json 56 | 57 | reserve_data true # this preserves fields from the original record 58 | remove_key_name_field true # this removes the log field if successfully parsed as JSON 59 | reserve_time # the time was already parsed in the source, we don't want to overwrite it with current time. 60 | emit_invalid_record_to_error false # In case of unparsable log lines or CRI logs. Keep fluentd's error log clean 61 | 62 | 63 | # Flatten fields nested within the 'kubernetes' field and remove unnecessary fields 64 | 65 | @type record_transformer 66 | enable_ruby 67 | 68 | ContainerName ${record["kubernetes"]["container_name"]} 69 | NamespaceName ${record["kubernetes"]["namespace_name"]} 70 | PodName ${record["kubernetes"]["pod_name"]} 71 | Node ${record["kubernetes"]["host"]} 72 | MasterUrl ${record["kubernetes"]["master_url"]} 73 | 74 | # The logtag field is used in CRI to support multi-line logs. It is usually noise, so remove by default. 75 | # https://github.com/kubernetes/community/blob/master/contributors/design-proposals/node/kubelet-cri-logging.md 76 | remove_keys docker,kubernetes,stream,logtag 77 | 78 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/config/fluentd.conf: -------------------------------------------------------------------------------- 1 | @include kubernetes.conf 2 | 3 | # Retag to prefix driver container events with akvssd 4 | 5 | @type rewrite_tag_filter 6 | 7 | key ContainerName 8 | pattern ^(.+)$ 9 | tag akvssd.$1 10 | 11 | 12 | 13 | # Retag to prefix provider container events with akvssp 14 | 15 | @type rewrite_tag_filter 16 | 17 | key ContainerName 18 | pattern ^(.+)$ 19 | tag akvssp.$1 20 | 21 | 22 | 23 | # Send akvssd events to MDSD 24 | 25 | @type mdsd 26 | @log_level info 27 | djsonsocket /var/run/mdsd/default_djson.socket # Full path to mdsd dynamic json socket file 28 | acktimeoutms 5000 # max time in milliseconds to wait for mdsd acknowledge response. If 0, no wait. 29 | mdsd_tag_regex_patterns ["^akvssd"] # fluentd tag patterns whose match will be used as mdsd source name 30 | num_threads 1 31 | buffer_chunk_limit 1000k 32 | buffer_type file 33 | buffer_path /var/log/td-agent/buffer/out_akvssd*.buffer 34 | buffer_queue_limit 128 35 | flush_interval 10s 36 | retry_limit 3 37 | retry_wait 10s 38 | 39 | 40 | # Send akvssp events to MDSD 41 | 42 | @type mdsd 43 | @log_level info 44 | djsonsocket /var/run/mdsd/default_djson.socket # Full path to mdsd dynamic json socket file 45 | acktimeoutms 5000 # max time in milliseconds to wait for mdsd acknowledge response. If 0, no wait. 46 | mdsd_tag_regex_patterns ["^akvssp"] # fluentd tag patterns whose match will be used as mdsd source name 47 | num_threads 1 48 | buffer_chunk_limit 1000k 49 | buffer_type file 50 | buffer_path /var/log/td-agent/buffer/out_akvssp*.buffer 51 | buffer_queue_limit 128 52 | flush_interval 10s 53 | retry_limit 3 54 | retry_wait 10s 55 | 56 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/config/gcstenant-conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "ServiceArguments": { 3 | "Version": "1.0" 4 | }, 5 | 6 | "UserArguments": { 7 | "GcsAuthIdType": "AuthMSIToken", 8 | "GcsEnvironment": "DiagnosticsPROD", 9 | "GcsGenevaAccount": "akvsecretsprovider", 10 | "GcsNamespace": "akvsecretsprovider", 11 | "GenevaConfigVersion": "2.2", 12 | "GcsRegion": "westus2" 13 | }, 14 | "EndpointConfigurations": [ 15 | { 16 | "EndpointPath": "", 17 | "EndpointPort": 15000, 18 | "EndpointOtlp": "localhost:4319" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/config/telegraf-conf.tmpl: -------------------------------------------------------------------------------- 1 | [agent] 2 | interval = "300s" 3 | flush_interval = "10s" 4 | metric_batch_size = 250 5 | metric_buffer_limit = 1000 6 | 7 | [[inputs.prometheus]] 8 | metric_version = 2 9 | 10 | monitor_kubernetes_pods = true 11 | kubernetes_label_selector = "app.kubernetes.io/name=csi-secrets-store-provider-azure" 12 | monitor_kubernetes_pods_namespace = "{{ .Release.Namespace }}" 13 | bearer_token = "/var/run/secrets/kubernetes.io/serviceaccount/token" 14 | tls_ca = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" 15 | insecure_skip_verify = true 16 | timeout = "15s" 17 | 18 | [[inputs.prometheus]] 19 | metric_version = 2 20 | 21 | monitor_kubernetes_pods = true 22 | kubernetes_label_selector = "app.kubernetes.io/name=secrets-store-csi-driver" 23 | monitor_kubernetes_pods_namespace = "{{ .Release.Namespace }}" 24 | bearer_token = "/var/run/secrets/kubernetes.io/serviceaccount/token" 25 | tls_ca = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" 26 | insecure_skip_verify = true 27 | timeout = "15s" 28 | 29 | [[outputs.http]] 30 | ## URL is the address to send metrics to 31 | url = "http://127.0.0.1:8090/push" 32 | 33 | ## Data format to output. 34 | data_format = "prometheusremotewrite" 35 | 36 | [outputs.http.headers] 37 | Content-Type = "application/x-protobuf" 38 | Content-Encoding = "snappy" 39 | X-Prometheus-Remote-Write-Version = "0.1.0" 40 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "sscdpa.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "sscdpa.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Common labels for helm resources 29 | */}} 30 | {{- define "sscdpa.common.labels" -}} 31 | app.kubernetes.io/instance: "{{ .Release.Name }}" 32 | app.kubernetes.io/managed-by: "{{ .Release.Service }}" 33 | app.kubernetes.io/version: "{{ .Chart.AppVersion }}" 34 | helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" 35 | {{- end -}} 36 | 37 | {{/* 38 | Standard labels for helm resources 39 | */}} 40 | {{- define "sscdpa.labels" -}} 41 | labels: 42 | {{ include "sscdpa.common.labels" . | indent 2 }} 43 | app.kubernetes.io/name: "{{ template "sscdpa.name" . }}" 44 | app: {{ template "sscdpa.name" . }} 45 | {{- end -}} 46 | 47 | {{- define "sscdpa.psp.fullname" -}} 48 | {{- printf "%s-psp" (include "sscdpa.fullname" .) -}} 49 | {{- end }} 50 | 51 | {{/* 52 | Arc specific templates 53 | */}} 54 | 55 | {{- define "sscdpa.arc.labels" -}} 56 | {{ include "sscdpa.common.labels" . }} 57 | app.kubernetes.io/name: "arc-{{ template "sscdpa.fullname" . }}" 58 | {{- end -}} 59 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/templates/arc-extension-identity.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.enableArcExtension }} 2 | {{- if and .Values.Azure.Identity.isEnabled .Values.arc.enableMonitoring }} 3 | apiVersion: clusterconfig.azure.com/v1beta1 4 | kind: AzureExtensionIdentity 5 | metadata: 6 | name: {{ .Values.Azure.Extension.Name }} 7 | namespace: azure-arc 8 | spec: 9 | serviceAccounts: 10 | - name: csi-secrets-store-provider-azure 11 | namespace: {{ .Release.Namespace }} 12 | tokenNamespace: {{ .Release.Namespace }} 13 | {{- end }} 14 | {{- end }} 15 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/templates/arc-monitoring-config.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.enableArcExtension }} 2 | {{- if .Values.arc.enableMonitoring }} 3 | apiVersion: v1 4 | kind: ConfigMap 5 | metadata: 6 | name: arc-telegraf-config 7 | namespace: {{ .Release.Namespace }} 8 | labels: 9 | {{ include "sscdpa.arc.labels" . | indent 4 }} 10 | data: 11 | telegraf.conf: | 12 | {{ (tpl ( .Files.Get "config/telegraf-conf.tmpl") .) | indent 4 }} 13 | --- 14 | apiVersion: v1 15 | kind: ConfigMap 16 | metadata: 17 | name: arc-mdm-config 18 | namespace: {{ .Release.Namespace }} 19 | labels: 20 | {{ include "sscdpa.arc.labels" . | indent 4 }} 21 | data: 22 | mdmconfig.json: "{\"imdsInfo\":[{ \"account\": \"akvsecretsprovider\" }]}" 23 | --- 24 | apiVersion: v1 25 | kind: ConfigMap 26 | metadata: 27 | name: arc-fluentd-conf 28 | namespace: {{ .Release.Namespace }} 29 | labels: 30 | {{ include "sscdpa.arc.labels" . | indent 4 }} 31 | data: 32 | fluentd.conf: {{ .Files.Get "config/fluentd.conf" | quote }} 33 | kubernetes.conf: {{ .Files.Get "config/fluentd-kubernetes.conf" | quote }} 34 | --- 35 | apiVersion: v1 36 | kind: ConfigMap 37 | metadata: 38 | name: arc-gcstenant-conf 39 | namespace: {{ .Release.Namespace }} 40 | labels: 41 | {{ include "sscdpa.arc.labels" . | indent 4 }} 42 | data: 43 | gcstenant1.json: {{ .Files.Get "config/gcstenant-conf.json" | quote }} 44 | {{- end }} 45 | {{- end }} 46 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/templates/arc-monitoring-service.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.enableArcExtension }} 2 | {{- if .Values.arc.enableMonitoring }} 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | name: arc-geneva-logging-service 7 | namespace: {{ .Release.Namespace }} 8 | labels: 9 | {{ include "sscdpa.arc.labels" . | indent 4 }} 10 | spec: 11 | type: ClusterIP 12 | ports: 13 | - name: mds-fluentd-tcp 14 | protocol: TCP 15 | port: 8130 16 | targetPort: 8130 17 | selector: 18 | app: "arc-monitoring" 19 | {{- end }} 20 | {{- end }} 21 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/templates/arc-proxy-secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.enableArcExtension }} 2 | {{- if .Values.Azure.proxySettings.isProxyEnabled }} 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: arc-proxy-config 7 | namespace: {{ .Release.Namespace }} 8 | type: Opaque 9 | data: 10 | {{- if .Values.Azure.proxySettings.httpProxy }} 11 | HTTP_PROXY: {{.Values.Azure.proxySettings.httpProxy | b64enc | quote}} 12 | {{- end }} 13 | {{- if .Values.Azure.proxySettings.httpsProxy }} 14 | HTTPS_PROXY: {{.Values.Azure.proxySettings.httpsProxy | b64enc | quote}} 15 | {{- end }} 16 | {{- if .Values.Azure.proxySettings.noProxy }} 17 | NO_PROXY: {{.Values.Azure.proxySettings.noProxy | b64enc | quote}} 18 | {{- end }} 19 | --- 20 | {{- if .Values.Azure.proxySettings.proxyCert }} 21 | apiVersion: v1 22 | kind: Secret 23 | metadata: 24 | name: arc-proxy-cert 25 | namespace: {{ .Release.Namespace }} 26 | type: Opaque 27 | data: 28 | proxy-cert.crt: {{.Values.Azure.proxySettings.proxyCert | b64enc | quote}} 29 | {{- end }} 30 | {{- end }} 31 | {{- end }} 32 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/templates/podsecuritypolicy.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.rbac.pspEnabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} 2 | apiVersion: policy/v1beta1 3 | kind: PodSecurityPolicy 4 | metadata: 5 | name: {{ template "sscdpa.psp.fullname" . }} 6 | {{ include "sscdpa.labels" . | indent 2 }} 7 | spec: 8 | seLinux: 9 | rule: RunAsAny 10 | privileged: true 11 | volumes: 12 | - hostPath 13 | - secret 14 | hostNetwork: true 15 | hostPorts: 16 | - min: 0 17 | max: 65535 18 | fsGroup: 19 | rule: RunAsAny 20 | runAsUser: 21 | rule: RunAsAny 22 | supplementalGroups: 23 | rule: RunAsAny 24 | {{- end }} 25 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/templates/role.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.rbac.pspEnabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: {{ template "sscdpa.psp.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | {{ include "sscdpa.labels" . | indent 2 }} 8 | rules: 9 | - apiGroups: [ 'policy' ] 10 | resources: [ 'podsecuritypolicies' ] 11 | verbs: [ 'use' ] 12 | resourceNames: 13 | - {{ template "sscdpa.psp.fullname" . }} 14 | {{- end }} 15 | --- 16 | {{- if .Values.enableArcExtension }} 17 | {{- if and .Values.Azure.Identity.isEnabled .Values.arc.enableMonitoring }} 18 | apiVersion: rbac.authorization.k8s.io/v1 19 | kind: Role 20 | metadata: 21 | name: arc-extension-identity-role 22 | namespace: {{ .Release.Namespace }} 23 | labels: 24 | {{ include "sscdpa.arc.labels" . | indent 4 }} 25 | rules: 26 | - apiGroups: [ 'clusterconfig.azure.com' ] 27 | resources: [ 'azureclusteridentityrequests' ] 28 | verbs: [ 'get', 'create' ] 29 | --- 30 | apiVersion: rbac.authorization.k8s.io/v1 31 | kind: ClusterRole 32 | metadata: 33 | name: arc-telegraf-role 34 | rules: 35 | - apiGroups: [""] 36 | resources: 37 | - pods 38 | verbs: ["get", "list", "watch"] 39 | --- 40 | apiVersion: rbac.authorization.k8s.io/v1 41 | kind: ClusterRole 42 | metadata: 43 | name: arc-geneva-role 44 | rules: 45 | - apiGroups: [""] 46 | resources: ["pods", "namespaces"] 47 | verbs: ["get", "watch", "list"] 48 | {{- end }} 49 | {{- end }} 50 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/templates/rolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.rbac.pspEnabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: RoleBinding 4 | metadata: 5 | name: {{ template "sscdpa.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | {{ include "sscdpa.labels" . | indent 2 }} 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: Role 11 | name: {{ template "sscdpa.psp.fullname" . }} 12 | subjects: 13 | - kind: ServiceAccount 14 | name: csi-secrets-store-provider-azure 15 | namespace: {{ .Release.Namespace }} 16 | {{- end }} 17 | --- 18 | {{- if .Values.enableArcExtension }} 19 | {{- if and .Values.Azure.Identity.isEnabled .Values.arc.enableMonitoring }} 20 | apiVersion: rbac.authorization.k8s.io/v1 21 | kind: RoleBinding 22 | metadata: 23 | name: arc-extension-identity-role-binding 24 | namespace: {{ .Release.Namespace }} 25 | labels: 26 | {{ include "sscdpa.arc.labels" . | indent 4 }} 27 | roleRef: 28 | apiGroup: rbac.authorization.k8s.io 29 | kind: Role 30 | name: arc-extension-identity-role 31 | subjects: 32 | - kind: ServiceAccount 33 | name: csi-secrets-store-provider-azure 34 | namespace: {{ .Release.Namespace }} 35 | --- 36 | apiVersion: rbac.authorization.k8s.io/v1 37 | kind: RoleBinding 38 | metadata: 39 | name: arc-telegraf-role-binding 40 | namespace: {{ .Release.Namespace }} 41 | labels: 42 | {{ include "sscdpa.arc.labels" . | indent 4 }} 43 | roleRef: 44 | apiGroup: rbac.authorization.k8s.io 45 | kind: ClusterRole 46 | name: arc-telegraf-role 47 | subjects: 48 | - kind: ServiceAccount 49 | name: csi-secrets-store-provider-azure 50 | namespace: {{ .Release.Namespace }} 51 | --- 52 | apiVersion: rbac.authorization.k8s.io/v1 53 | kind: ClusterRoleBinding 54 | metadata: 55 | name: arc-geneva-role-binding 56 | labels: 57 | {{ include "sscdpa.arc.labels" . | indent 4 }} 58 | roleRef: 59 | kind: ClusterRole 60 | name: arc-geneva-role 61 | apiGroup: rbac.authorization.k8s.io 62 | subjects: 63 | - kind: ServiceAccount 64 | name: csi-secrets-store-provider-azure 65 | namespace: {{ .Release.Namespace }} 66 | {{- end }} 67 | {{- end }} 68 | -------------------------------------------------------------------------------- /manifest_staging/charts/csi-secrets-store-provider-azure/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.rbac.install }} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: csi-secrets-store-provider-azure 6 | namespace: {{ .Release.Namespace }} 7 | {{ include "sscdpa.labels" . | indent 2 }} 8 | {{ end }} 9 | -------------------------------------------------------------------------------- /manifest_staging/deployment/provider-azure-installer-windows.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: csi-secrets-store-provider-azure 5 | --- 6 | apiVersion: apps/v1 7 | kind: DaemonSet 8 | metadata: 9 | labels: 10 | app: csi-secrets-store-provider-azure 11 | name: csi-secrets-store-provider-azure-windows 12 | spec: 13 | updateStrategy: 14 | type: RollingUpdate 15 | selector: 16 | matchLabels: 17 | app: csi-secrets-store-provider-azure 18 | template: 19 | metadata: 20 | labels: 21 | app: csi-secrets-store-provider-azure 22 | spec: 23 | serviceAccountName: csi-secrets-store-provider-azure 24 | containers: 25 | - name: provider-azure-installer 26 | image: mcr.microsoft.com/oss/v2/azure/secrets-store/provider-azure:v1.7.0 27 | imagePullPolicy: IfNotPresent 28 | args: 29 | - --endpoint=unix://C:\\provider\\azure.sock 30 | - --construct-pem-chain=true 31 | - --healthz-port=8989 32 | - --healthz-path=/healthz 33 | - --healthz-timeout=5s 34 | livenessProbe: 35 | httpGet: 36 | path: /healthz 37 | port: 8989 38 | failureThreshold: 3 39 | initialDelaySeconds: 5 40 | timeoutSeconds: 10 41 | periodSeconds: 30 42 | resources: 43 | requests: 44 | cpu: 100m 45 | memory: 200Mi 46 | limits: 47 | cpu: 100m 48 | memory: 200Mi 49 | ports: 50 | - containerPort: 8898 51 | name: metrics 52 | protocol: TCP 53 | volumeMounts: 54 | - mountPath: "C:\\provider" 55 | name: providervol 56 | affinity: 57 | nodeAffinity: 58 | requiredDuringSchedulingIgnoredDuringExecution: 59 | nodeSelectorTerms: 60 | - matchExpressions: 61 | - key: type 62 | operator: NotIn 63 | values: 64 | - virtual-kubelet 65 | volumes: 66 | - name: providervol 67 | hostPath: 68 | path: "C:\\k\\secrets-store-csi-providers" 69 | type: DirectoryOrCreate 70 | tolerations: 71 | - operator: Exists 72 | nodeSelector: 73 | kubernetes.io/os: windows 74 | -------------------------------------------------------------------------------- /manifest_staging/deployment/provider-azure-installer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: csi-secrets-store-provider-azure 5 | --- 6 | apiVersion: apps/v1 7 | kind: DaemonSet 8 | metadata: 9 | labels: 10 | app: csi-secrets-store-provider-azure 11 | name: csi-secrets-store-provider-azure 12 | spec: 13 | updateStrategy: 14 | type: RollingUpdate 15 | selector: 16 | matchLabels: 17 | app: csi-secrets-store-provider-azure 18 | template: 19 | metadata: 20 | labels: 21 | app: csi-secrets-store-provider-azure 22 | spec: 23 | serviceAccountName: csi-secrets-store-provider-azure 24 | hostNetwork: true 25 | containers: 26 | - name: provider-azure-installer 27 | image: mcr.microsoft.com/oss/v2/azure/secrets-store/provider-azure:v1.7.0 28 | imagePullPolicy: IfNotPresent 29 | args: 30 | - --endpoint=unix:///provider/azure.sock 31 | - --construct-pem-chain=true 32 | - --healthz-port=8989 33 | - --healthz-path=/healthz 34 | - --healthz-timeout=5s 35 | livenessProbe: 36 | httpGet: 37 | path: /healthz 38 | port: 8989 39 | failureThreshold: 3 40 | initialDelaySeconds: 5 41 | timeoutSeconds: 10 42 | periodSeconds: 30 43 | resources: 44 | requests: 45 | cpu: 50m 46 | memory: 100Mi 47 | limits: 48 | cpu: 50m 49 | memory: 100Mi 50 | ports: 51 | - containerPort: 8898 52 | name: metrics 53 | protocol: TCP 54 | securityContext: 55 | allowPrivilegeEscalation: false 56 | readOnlyRootFilesystem: true 57 | runAsUser: 0 58 | capabilities: 59 | drop: 60 | - ALL 61 | volumeMounts: 62 | - mountPath: "/provider" 63 | name: providervol 64 | affinity: 65 | nodeAffinity: 66 | requiredDuringSchedulingIgnoredDuringExecution: 67 | nodeSelectorTerms: 68 | - matchExpressions: 69 | - key: type 70 | operator: NotIn 71 | values: 72 | - virtual-kubelet 73 | volumes: 74 | - name: providervol 75 | hostPath: 76 | path: "/var/run/secrets-store-csi-providers" 77 | tolerations: 78 | - operator: Exists 79 | nodeSelector: 80 | kubernetes.io/os: linux 81 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | base = "website/" 3 | publish = "/public" 4 | 5 | [context.deploy-preview] 6 | command = "make preview-build" 7 | 8 | [context.deploy-preview.environment] 9 | HUGO_VERSION = "0.75.1" 10 | HUGO_ENABLEGITINFO = "true" 11 | -------------------------------------------------------------------------------- /pkg/metrics/exporter.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "k8s.io/klog/v2" 8 | ) 9 | 10 | const prometheusExporter = "prometheus" 11 | 12 | func InitMetricsExporter(metricsBackend string, prometheusPort int) error { 13 | mb := strings.ToLower(metricsBackend) 14 | klog.InfoS("intializing metrics backend", "backend", mb) 15 | switch mb { 16 | // Prometheus is the only exporter for now 17 | case prometheusExporter: 18 | return initPrometheusExporter(prometheusPort) 19 | default: 20 | return fmt.Errorf("unsupported metrics backend %v", metricsBackend) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pkg/metrics/prometheus_exporter.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "time" 7 | 8 | "go.opentelemetry.io/otel/exporters/metric/prometheus" 9 | "k8s.io/klog/v2" 10 | ) 11 | 12 | const ( 13 | readHeaderTimeout = 5 * time.Second 14 | ) 15 | 16 | func initPrometheusExporter(port int) error { 17 | pusher, err := prometheus.InstallNewPipeline(prometheus.Config{ 18 | DefaultHistogramBoundaries: []float64{ 19 | 0.1, 0.2, 0.3, 0.4, 0.5, 1, 1.5, 2, 2.5, 3.0, 5.0, 10.0, 15.0, 30.0, 20 | }}) 21 | if err != nil { 22 | return err 23 | } 24 | http.HandleFunc("/metrics", pusher.ServeHTTP) 25 | go func() { 26 | server := &http.Server{ 27 | Addr: fmt.Sprintf(":%v", port), 28 | ReadHeaderTimeout: readHeaderTimeout, 29 | } 30 | klog.ErrorS(server.ListenAndServe(), "listen and server error") 31 | }() 32 | 33 | return err 34 | } 35 | -------------------------------------------------------------------------------- /pkg/metrics/stats_reporter.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "context" 5 | "runtime" 6 | 7 | "go.opentelemetry.io/otel/attribute" 8 | "go.opentelemetry.io/otel/metric" 9 | "go.opentelemetry.io/otel/metric/global" 10 | ) 11 | 12 | var ( 13 | providerAttr = attribute.String("provider", "azure") 14 | osTypeAttr = attribute.String("os_type", runtime.GOOS) 15 | // set service.name attribute explicitly to the provider name as the default service name is "unknown_service:" 16 | serviceNameAttr = attribute.String("service.name", "csi-secrets-store-provider-azure") 17 | objectTypeKey = "object_type" 18 | objectNameKey = "object_name" 19 | errorKey = "error" 20 | grpcMethodKey = "grpc_method" 21 | grpcCodeKey = "grpc_code" 22 | grpcMessageKey = "grpc_message" 23 | keyvaultRequest metric.Float64ValueRecorder 24 | grpcRequest metric.Float64ValueRecorder 25 | ) 26 | 27 | type reporter struct { 28 | meter metric.Meter 29 | } 30 | 31 | // StatsReporter is the interface for reporting metrics 32 | type StatsReporter interface { 33 | ReportKeyvaultRequest(ctx context.Context, duration float64, objectType, objectName, err string) 34 | ReportGRPCRequest(ctx context.Context, duration float64, method, code, message string) 35 | } 36 | 37 | // NewStatsReporter creates a new StatsReporter 38 | func NewStatsReporter() StatsReporter { 39 | meter := global.Meter("csi-secrets-store-provider-azure") 40 | 41 | keyvaultRequest = metric.Must(meter).NewFloat64ValueRecorder("keyvault_request", metric.WithDescription("Distribution of how long it took to get from keyvault")) 42 | grpcRequest = metric.Must(meter).NewFloat64ValueRecorder("grpc_request", metric.WithDescription("Distribution of how long it took for the gRPC requests")) 43 | return &reporter{meter: meter} 44 | } 45 | 46 | // ReportKeyvaultRequest reports the duration of the keyvault request 47 | // objectType and objectName are used to identify the object being accessed 48 | // err is used to identify the error if any 49 | func (r *reporter) ReportKeyvaultRequest(ctx context.Context, duration float64, objectType, objectName, err string) { 50 | attributes := []attribute.KeyValue{ 51 | serviceNameAttr, 52 | providerAttr, 53 | osTypeAttr, 54 | attribute.String(objectTypeKey, objectType), 55 | attribute.String(objectNameKey, objectName), 56 | attribute.String(errorKey, err), 57 | } 58 | r.meter.RecordBatch(ctx, 59 | attributes, 60 | keyvaultRequest.Measurement(duration), 61 | ) 62 | } 63 | 64 | // ReportGRPCRequest reports the duration of the gRPC request 65 | // method and code are used to identify the gRPC request 66 | func (r *reporter) ReportGRPCRequest(ctx context.Context, duration float64, method, code, message string) { 67 | attributes := []attribute.KeyValue{ 68 | serviceNameAttr, 69 | providerAttr, 70 | osTypeAttr, 71 | attribute.String(grpcMethodKey, method), 72 | attribute.String(grpcCodeKey, code), 73 | attribute.String(grpcMessageKey, message), 74 | } 75 | r.meter.RecordBatch(ctx, 76 | attributes, 77 | grpcRequest.Measurement(duration), 78 | ) 79 | } 80 | -------------------------------------------------------------------------------- /pkg/provider/mock_keyvault/doc.go: -------------------------------------------------------------------------------- 1 | // Run go generate to regenerate this mock. 2 | // 3 | //go:generate ../../../.tools/mockgen -destination keyvault_mock.go -package mock_keyvault -source ../keyvault.go 4 | package mock_keyvault //nolint 5 | -------------------------------------------------------------------------------- /pkg/provider/mock_provider/doc.go: -------------------------------------------------------------------------------- 1 | // Run go generate to regenerate this mock. 2 | // 3 | //go:generate ../../../.tools/mockgen -destination provider_mock.go -package mock_provider -source ../provider.go 4 | package mock_provider //nolint 5 | -------------------------------------------------------------------------------- /pkg/provider/mock_provider/provider_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: ../provider.go 3 | 4 | // Package mock_provider is a generated GoMock package. 5 | package mock_provider 6 | 7 | import ( 8 | os "os" 9 | reflect "reflect" 10 | 11 | types "github.com/Azure/secrets-store-csi-driver-provider-azure/pkg/provider/types" 12 | gomock "github.com/golang/mock/gomock" 13 | context "golang.org/x/net/context" 14 | ) 15 | 16 | // MockInterface is a mock of Interface interface. 17 | type MockInterface struct { 18 | ctrl *gomock.Controller 19 | recorder *MockInterfaceMockRecorder 20 | } 21 | 22 | // MockInterfaceMockRecorder is the mock recorder for MockInterface. 23 | type MockInterfaceMockRecorder struct { 24 | mock *MockInterface 25 | } 26 | 27 | // NewMockInterface creates a new mock instance. 28 | func NewMockInterface(ctrl *gomock.Controller) *MockInterface { 29 | mock := &MockInterface{ctrl: ctrl} 30 | mock.recorder = &MockInterfaceMockRecorder{mock} 31 | return mock 32 | } 33 | 34 | // EXPECT returns an object that allows the caller to indicate expected use. 35 | func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { 36 | return m.recorder 37 | } 38 | 39 | // GetSecretsStoreObjectContent mocks base method. 40 | func (m *MockInterface) GetSecretsStoreObjectContent(ctx context.Context, attrib, secrets map[string]string, defaultFilePermission os.FileMode) ([]types.SecretFile, error) { 41 | m.ctrl.T.Helper() 42 | ret := m.ctrl.Call(m, "GetSecretsStoreObjectContent", ctx, attrib, secrets, defaultFilePermission) 43 | ret0, _ := ret[0].([]types.SecretFile) 44 | ret1, _ := ret[1].(error) 45 | return ret0, ret1 46 | } 47 | 48 | // GetSecretsStoreObjectContent indicates an expected call of GetSecretsStoreObjectContent. 49 | func (mr *MockInterfaceMockRecorder) GetSecretsStoreObjectContent(ctx, attrib, secrets, defaultFilePermission interface{}) *gomock.Call { 50 | mr.mock.ctrl.T.Helper() 51 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSecretsStoreObjectContent", reflect.TypeOf((*MockInterface)(nil).GetSecretsStoreObjectContent), ctx, attrib, secrets, defaultFilePermission) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/server/healthz.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "net/http" 8 | "net/url" 9 | "os" 10 | "time" 11 | 12 | "github.com/pkg/errors" 13 | "google.golang.org/grpc" 14 | "google.golang.org/grpc/credentials/insecure" 15 | "google.golang.org/grpc/health/grpc_health_v1" 16 | "k8s.io/klog/v2" 17 | ) 18 | 19 | const ( 20 | readHeaderTimeout = 5 * time.Second 21 | ) 22 | 23 | type HealthZ struct { 24 | HealthCheckURL *url.URL 25 | UnixSocketPath string 26 | RPCTimeout time.Duration 27 | } 28 | 29 | // Serve creates the http handler for serving health requests 30 | func (h *HealthZ) Serve() { 31 | serveMux := http.NewServeMux() 32 | serveMux.HandleFunc(h.HealthCheckURL.EscapedPath(), h.ServeHTTP) 33 | server := &http.Server{ 34 | Addr: h.HealthCheckURL.Host, 35 | ReadHeaderTimeout: readHeaderTimeout, 36 | Handler: serveMux, 37 | } 38 | if err := server.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) { 39 | klog.ErrorS(err, "failed to start health check server") 40 | os.Exit(1) 41 | } 42 | } 43 | 44 | func (h *HealthZ) ServeHTTP(w http.ResponseWriter, _ *http.Request) { 45 | klog.V(5).Infof("Started health check") 46 | ctx, cancel := context.WithTimeout(context.Background(), h.RPCTimeout) 47 | defer cancel() 48 | 49 | conn, err := h.dialUnixSocket() 50 | if err != nil { 51 | http.Error(w, err.Error(), http.StatusServiceUnavailable) 52 | return 53 | } 54 | defer conn.Close() 55 | 56 | // create the health check grpc client 57 | client := grpc_health_v1.NewHealthClient(conn) 58 | // check health check response against gRPC endpoint. 59 | err = h.checkRPC(ctx, client) 60 | if err != nil { 61 | http.Error(w, err.Error(), http.StatusServiceUnavailable) 62 | return 63 | } 64 | w.WriteHeader(http.StatusOK) 65 | w.Write([]byte("ok")) 66 | klog.V(5).Infof("Completed health check") 67 | } 68 | 69 | // checkRPC initiates a grpc request to validate the socket is responding 70 | // sends a gRPC HealthCheckRequest and checks if the HealthCheckResponse is valid. 71 | func (h *HealthZ) checkRPC(ctx context.Context, client grpc_health_v1.HealthClient) error { 72 | v, err := client.Check(ctx, &grpc_health_v1.HealthCheckRequest{}) 73 | if err != nil { 74 | return err 75 | } 76 | if v == nil || v.Status != grpc_health_v1.HealthCheckResponse_SERVING { 77 | return fmt.Errorf("expected health check response serving") 78 | } 79 | return nil 80 | } 81 | 82 | func (h *HealthZ) dialUnixSocket() (*grpc.ClientConn, error) { 83 | return grpc.Dial( 84 | h.UnixSocketPath, 85 | grpc.WithTransportCredentials(insecure.NewCredentials()), 86 | grpc.WithContextDialer(func(ctx context.Context, target string) (net.Conn, error) { 87 | return (&net.Dialer{}).DialContext(ctx, "unix", target) 88 | }), 89 | ) 90 | } 91 | -------------------------------------------------------------------------------- /pkg/utils/grpc.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | "time" 8 | 9 | "github.com/Azure/secrets-store-csi-driver-provider-azure/pkg/metrics" 10 | 11 | "google.golang.org/grpc" 12 | "google.golang.org/grpc/status" 13 | "k8s.io/klog/v2" 14 | ) 15 | 16 | func ParseEndpoint(ep string) (string, string, error) { 17 | if strings.HasPrefix(strings.ToLower(ep), "unix://") || strings.HasPrefix(strings.ToLower(ep), "tcp://") { 18 | s := strings.SplitN(ep, "://", 2) 19 | if s[1] != "" { 20 | return s[0], s[1], nil 21 | } 22 | } 23 | return "", "", fmt.Errorf("invalid endpoint: %v", ep) 24 | } 25 | 26 | // LogInterceptor is a gRPC interceptor that logs the gRPC requests and responses. 27 | // It also publishes metrics for the gRPC requests. 28 | func LogInterceptor() grpc.UnaryServerInterceptor { 29 | return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 30 | start := time.Now() 31 | reporter := metrics.NewStatsReporter() 32 | 33 | ctxDeadline, _ := ctx.Deadline() 34 | klog.V(5).InfoS("request", "method", info.FullMethod, "deadline", time.Until(ctxDeadline).String()) 35 | 36 | resp, err := handler(ctx, req) 37 | s, _ := status.FromError(err) 38 | klog.V(5).InfoS("response", "method", info.FullMethod, "duration", time.Since(start).String(), "code", s.Code().String(), "message", s.Message()) 39 | reporter.ReportGRPCRequest(ctx, time.Since(start).Seconds(), info.FullMethod, s.Code().String(), s.Message()) 40 | 41 | return resp, err 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pkg/utils/helpers.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "regexp" 4 | 5 | // RedactSecureString applies regex to a sensitive string and return the redacted value 6 | func RedactSecureString(sensitiveString string) string { 7 | r, _ := regexp.Compile(`^(\S{4})(\S|\s)*(\S{4})$`) 8 | return r.ReplaceAllString(sensitiveString, "$1##### REDACTED #####$3") 9 | } 10 | -------------------------------------------------------------------------------- /pkg/utils/helpers_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "testing" 4 | 5 | func TestRedactSecureString(t *testing.T) { 6 | tests := []struct { 7 | name string 8 | sensitiveString string 9 | expected string 10 | }{ 11 | { 12 | name: "should redact client id", 13 | sensitiveString: "aabc0000-a83v-9h4m-000j-2c0a66b0c1f9", 14 | expected: "aabc##### REDACTED #####c1f9", 15 | }, 16 | { 17 | name: "should redact access token", 18 | sensitiveString: `eyJhbGciOiJSUzI1NiIsImtpZCI6InRhVDBxbzhQVEZ1ajB1S3BYUUxIclRsR01XakxjemJNOTlzWVMxSlNwbWcifQ.eyJhdWQiOlsiYXBpOi8vQXp1cmVBRGlUb2tlbkV4Y2hhbmdlIl0sImV4cCI6MTY0MzIzNDY0NywiaWF0IjoxNjQzMjMxMDQ3LCJpc3MiOiJodHRwczovL2t1YmVybmV0ZXMuZGVmYXVsdC5zdmMuY2x1c3Rlci5sb2NhbCIsImt1YmVybmV0ZXMuaW8iOnsibmFtZXNwYWNlIjoidGVzdC12MWFscGhhMSIsInBvZCI6eyJuYW1lIjoic2VjcmV0cy1zdG9yZS1pbmxpbmUtY3JkIiwidWlkIjoiYjBlYmZjMzUtZjEyNC00ZTEyLWI3N2UtYjM0MjM2N2IyMDNmIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJkZWZhdWx0IiwidWlkIjoiMjViNGY1NzgtM2U4MC00NTczLWJlOGQtZTdmNDA5ZDI0MmI2In19LCJuYmYiOjE2NDMyMzEwNDcsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDp0ZXN0LXYxYWxwaGExOmRlZmF1bHQifQ.ALE46aKmtTV7dsuFOwDZqvEjdHFUTNP-JVjMxexTemmPA78fmPTUZF0P6zANumA03fjX3L-MZNR3PxmEZgKA9qEGIDsljLsUWsVBEquowuBh8yoBYkGkMJmRfmbfS3y7_4Q7AU3D9Drw4iAHcn1GwedjOQC0i589y3dkNNqf8saqHfXkbSSLtSE0f2uzI-PjuTKvR1kuojEVNKlEcA4wsKfoiRpkua17sHkHU0q9zxCMDCr_1f8xbigRnRx0wscU3vy-8KhF3zQtpcWkk3r4C5YSXut9F3xjz5J9DUQn2vNMfZg4tOdcR-9Xv9fbY5iujiSlS58GEktSEa3SE9wrCw`, 19 | expected: "eyJh##### REDACTED #####wrCw", 20 | }, 21 | } 22 | 23 | for _, test := range tests { 24 | t.Run(test.name, func(t *testing.T) { 25 | actual := RedactSecureString(test.sensitiveString) 26 | if actual != test.expected { 27 | t.Fatalf("expected: %s, got %s", test.expected, actual) 28 | } 29 | }) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "fmt" 7 | "runtime" 8 | 9 | "k8s.io/klog/v2" 10 | ) 11 | 12 | var ( 13 | // BuildDate is date when binary was built 14 | BuildDate string 15 | // BuildVersion is the version of binary 16 | BuildVersion string 17 | // Vcs is is the commit hash for the binary build 18 | Vcs string 19 | 20 | // custom user agent to append for adal and keyvault calls 21 | customUserAgent = flag.String("custom-user-agent", "", "user agent to append in addition to akv provider versions") 22 | ) 23 | 24 | // providerVersion holds current provider version 25 | type providerVersion struct { 26 | Version string `json:"version"` 27 | BuildDate string `json:"buildDate"` 28 | } 29 | 30 | func PrintVersion() (err error) { 31 | pv := providerVersion{ 32 | Version: BuildVersion, 33 | BuildDate: BuildDate, 34 | } 35 | 36 | var res []byte 37 | if res, err = json.Marshal(pv); err != nil { 38 | return 39 | } 40 | 41 | fmt.Printf("%s\n", res) 42 | return 43 | } 44 | 45 | // GetUserAgent returns UserAgent string to append to the agent identifier. 46 | func GetUserAgent() string { 47 | if *customUserAgent != "" { 48 | klog.V(5).InfoS("Appending custom user agent", "userAgent", *customUserAgent) 49 | return fmt.Sprintf("csi-secrets-store/%s (%s/%s) %s/%s %s", BuildVersion, runtime.GOOS, runtime.GOARCH, Vcs, BuildDate, *customUserAgent) 50 | } 51 | return fmt.Sprintf("csi-secrets-store/%s (%s/%s) %s/%s", BuildVersion, runtime.GOOS, runtime.GOARCH, Vcs, BuildDate) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/version/version_test.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | "runtime" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func TestPrintVersion(t *testing.T) { 14 | BuildDate = "Now" 15 | BuildVersion = "version" 16 | 17 | old := os.Stdout // keep backup of the real stdout 18 | r, w, _ := os.Pipe() 19 | os.Stdout = w 20 | 21 | err := PrintVersion() 22 | 23 | outC := make(chan string) 24 | // copy the output in a separate goroutine so printing can't block indefinitely 25 | go func() { 26 | var buf bytes.Buffer 27 | io.Copy(&buf, r) 28 | outC <- strings.TrimSpace(buf.String()) 29 | }() 30 | 31 | // back to normal state 32 | w.Close() 33 | os.Stdout = old // restoring the real stdout 34 | out := <-outC 35 | 36 | if err != nil { 37 | t.Fatalf("expected no error, got %v", err) 38 | } 39 | expected := `{"version":"version","buildDate":"Now"}` 40 | if !strings.EqualFold(out, expected) { 41 | t.Fatalf("string doesn't match, expected %s, got %s", expected, out) 42 | } 43 | } 44 | 45 | func TestGetUserAgent(t *testing.T) { 46 | BuildDate = "now" 47 | Vcs = "commit" 48 | BuildVersion = "version" 49 | 50 | tests := []struct { 51 | name string 52 | customUserAgent string 53 | expectedUserAgent string 54 | }{ 55 | { 56 | name: "default user agent", 57 | customUserAgent: "", 58 | expectedUserAgent: fmt.Sprintf("csi-secrets-store/version (%s/%s) commit/now", runtime.GOOS, runtime.GOARCH), 59 | }, 60 | { 61 | name: "default user agent and custom user agent", 62 | customUserAgent: "managedBy:aks", 63 | expectedUserAgent: fmt.Sprintf("csi-secrets-store/version (%s/%s) commit/now managedBy:aks", runtime.GOOS, runtime.GOARCH), 64 | }, 65 | } 66 | 67 | for _, test := range tests { 68 | test := test 69 | t.Run(test.name, func(t *testing.T) { 70 | customUserAgent = &test.customUserAgent 71 | actualUserAgent := GetUserAgent() 72 | if !strings.EqualFold(test.expectedUserAgent, actualUserAgent) { 73 | t.Fatalf("expected user agent: %s, got: %s.", test.expectedUserAgent, actualUserAgent) 74 | } 75 | }) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /scripts/create-kind-cluster.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | : "${SERVICE_ACCOUNT_ISSUER:?Environment variable empty or not defined.}" 8 | 9 | REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. 10 | cd "${REPO_ROOT}" || exit 1 11 | 12 | SERVICE_ACCOUNT_SIGNING_KEY_FILE="$(pwd)/sa.key" 13 | SERVICE_ACCOUNT_KEY_FILE="$(pwd)/sa.pub" 14 | 15 | KIND_CLUSTER_NAME="${KIND_CLUSTER_NAME:-kind}" 16 | 17 | create_kind_cluster() { 18 | # create a kind cluster 19 | cat < "${SERVICE_ACCOUNT_KEY_FILE}" 62 | az keyvault secret show --vault-name "${SERVICE_ACCOUNT_KEYVAULT_NAME}" --name workload-identity-sa-key | jq -r .value | base64 -d > "${SERVICE_ACCOUNT_SIGNING_KEY_FILE}" 63 | } 64 | 65 | download_service_account_keys 66 | create_kind_cluster 67 | -------------------------------------------------------------------------------- /test/custom_environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AzureStackCloud", 3 | "activeDirectoryEndpoint": "https://login.microsoftonline.com/", 4 | "keyVaultEndpoint": "https://vault.azure.net/", 5 | "keyVaultDNSSuffix": "vault.azure.net" 6 | } 7 | 8 | -------------------------------------------------------------------------------- /test/e2e/Makefile: -------------------------------------------------------------------------------- 1 | REPO_ROOT := $(shell git rev-parse --show-toplevel) 2 | TEST_E2E_DIR := $(REPO_ROOT)/test/e2e 3 | GINKGO_FOCUS ?= 4 | GINKGO_SKIP ?= 5 | GINKGO_FAIL_FAST ?= true 6 | GINKGO_DRY_RUN ?= false 7 | 8 | # NOTE: junit report can be simply created by executing your tests with the new --junit-report flags instead. 9 | JUNIT_OUTPUT_FILEPATH ?= $(PWD)/_artifacts 10 | GINKGO_JUNIT_REPORT := $(shell echo $(JUNIT_OUTPUT_FILEPATH) | sed 's/\/$$//')/junit.xml 11 | 12 | .PHONY: run 13 | run: 14 | mkdir -p $(JUNIT_OUTPUT_FILEPATH) 15 | cd $(TEST_E2E_DIR); go test -tags=e2e -timeout=90m -v -ginkgo.v \ 16 | -ginkgo.focus=$(GINKGO_FOCUS) \ 17 | -ginkgo.skip=$(GINKGO_SKIP) \ 18 | -ginkgo.fail-fast=$(GINKGO_FAIL_FAST) \ 19 | -ginkgo.dry-run=$(GINKGO_DRY_RUN) \ 20 | -ginkgo.junit-report=$(GINKGO_JUNIT_REPORT) 21 | 22 | .PHONY: build 23 | build: 24 | cd $(TEST_E2E_DIR); CGO_ENABLED=0 GOARCH=${ARCH} GOOS=linux go test -c -tags=e2e -timeout=90m -v -o _output/${ARCH}/e2e 25 | -------------------------------------------------------------------------------- /test/e2e/arc_test.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package e2e 5 | 6 | import ( 7 | "time" 8 | 9 | "github.com/Azure/secrets-store-csi-driver-provider-azure/test/e2e/framework" 10 | "github.com/Azure/secrets-store-csi-driver-provider-azure/test/e2e/framework/daemonset" 11 | 12 | . "github.com/onsi/ginkgo/v2" 13 | . "github.com/onsi/gomega" 14 | appsv1 "k8s.io/api/apps/v1" 15 | ) 16 | 17 | var _ = Describe("When extension arguments are manually overridden", func() { 18 | var ( 19 | daemonSet *appsv1.DaemonSet 20 | newRotationPollIntervalValue = "--rotation-poll-interval=1m" 21 | secretStoreCSIDriverName = "secrets-store-csi-driver" 22 | ) 23 | 24 | It("should reconcile them to original values", func() { 25 | if !config.IsArcTest { 26 | Skip("test case only runs while testing arc extension") 27 | } 28 | 29 | daemonSet = daemonset.Get(daemonset.GetInput{ 30 | Namespace: framework.NamespaceKubeSystem, 31 | Name: secretStoreCSIDriverName, 32 | Getter: kubeClient, 33 | }) 34 | Expect(daemonSet).NotTo(BeNil()) 35 | 36 | daemonSet.Spec.Template.Spec.Containers[1].Args = append(daemonSet.Spec.Template.Spec.Containers[1].Args, newRotationPollIntervalValue) 37 | daemonSet = daemonset.Update(daemonset.UpdateInput{ 38 | Updater: kubeClient, 39 | DaemonSet: daemonSet, 40 | }) 41 | Expect(daemonSet).NotTo(BeNil()) 42 | 43 | // waiting for 300 seconds since 'reconcilerIntervalInSeconds' is set to this value in extension configuration 44 | By("Waiting for arc extension to reconcile the arguments") 45 | time.Sleep(time.Second * 300) 46 | 47 | daemonSet = daemonset.Get(daemonset.GetInput{ 48 | Namespace: framework.NamespaceKubeSystem, 49 | Name: secretStoreCSIDriverName, 50 | Getter: kubeClient, 51 | }) 52 | Expect(daemonSet).NotTo(BeNil()) 53 | 54 | for _, arg := range daemonSet.Spec.Template.Spec.Containers[1].Args { 55 | if arg == newRotationPollIntervalValue { 56 | // Manually overridden value should be reverted by arc extension reconciliation 57 | Expect(arg).NotTo(Equal(newRotationPollIntervalValue)) 58 | } 59 | } 60 | }) 61 | }) 62 | -------------------------------------------------------------------------------- /test/e2e/cleanup.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package e2e 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | 10 | "github.com/Azure/secrets-store-csi-driver-provider-azure/test/e2e/framework" 11 | "github.com/Azure/secrets-store-csi-driver-provider-azure/test/e2e/framework/namespace" 12 | 13 | . "github.com/onsi/ginkgo/v2" 14 | . "github.com/onsi/gomega" 15 | corev1 "k8s.io/api/core/v1" 16 | "sigs.k8s.io/controller-runtime/pkg/client" 17 | ) 18 | 19 | // CleanupInput is the input for Cleanup. 20 | type CleanupInput struct { 21 | Namespace *corev1.Namespace 22 | Getter framework.Getter 23 | Lister framework.Lister 24 | Deleter framework.Deleter 25 | } 26 | 27 | // Cleanup cleans up the test environment after a test case is finished running. 28 | func Cleanup(input CleanupInput) { 29 | Expect(input.Namespace).NotTo(BeNil(), "input.Namespace is required for e2e.Cleanup") 30 | Expect(input.Getter).NotTo(BeNil(), "input.Getter is required for e2e.Cleanup") 31 | Expect(input.Lister).NotTo(BeNil(), "input.Lister is required for e2e.Cleanup") 32 | Expect(input.Deleter).NotTo(BeNil(), "input.Deleter is required for e2e.Cleanup") 33 | 34 | By(fmt.Sprintf("Deleting all pods in %s", input.Namespace.Name)) 35 | 36 | podList := &corev1.PodList{} 37 | Expect(input.Lister.List(context.TODO(), podList, client.InNamespace(input.Namespace.Name))).Should(Succeed()) 38 | for _, pod := range podList.Items { 39 | Expect(input.Deleter.Delete(context.TODO(), &pod)).Should(Succeed()) 40 | } 41 | 42 | Eventually(func() bool { 43 | podList := &corev1.PodList{} 44 | Expect(input.Lister.List(context.TODO(), podList, client.InNamespace(input.Namespace.Name))).Should(Succeed()) 45 | return len(podList.Items) == 0 46 | }, framework.Timeout, framework.Polling).Should(BeTrue()) 47 | 48 | namespace.Delete(namespace.DeleteInput{ 49 | Deleter: input.Deleter, 50 | Getter: input.Getter, 51 | Namespace: input.Namespace, 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /test/e2e/framework/cluster_proxy.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package framework 5 | 6 | import ( 7 | . "github.com/onsi/gomega" 8 | "k8s.io/apimachinery/pkg/runtime" 9 | "k8s.io/client-go/kubernetes" 10 | "k8s.io/client-go/rest" 11 | "k8s.io/client-go/tools/clientcmd" 12 | "sigs.k8s.io/controller-runtime/pkg/client" 13 | ) 14 | 15 | // ClusterProxy defines the behavior of a type that acts as an intermediary with an existing Kubernetes cluster. 16 | // It should work with any Kubernetes cluster, no matter if the Cluster was created by a bootstrap.ClusterProvider, 17 | // by Cluster API (a workload cluster or a self-hosted cluster) or else. 18 | type ClusterProxy interface { 19 | // GetClient returns a controller-runtime client to the Kubernetes cluster. 20 | GetClient() client.Client 21 | 22 | // GetClientSet returns a client-go client to the Kubernetes cluster. 23 | GetClientSet() *kubernetes.Clientset 24 | 25 | // GetKubeconfigPath returns the path to the kubeconfig file for the cluster. 26 | GetKubeconfigPath() string 27 | } 28 | 29 | type clusterProxy struct { 30 | kubeconfigPath string 31 | scheme *runtime.Scheme 32 | } 33 | 34 | // NewClusterProxy returns a clusterProxy given a KubeconfigPath and the scheme defining the types hosted in the cluster. 35 | // If a kubeconfig file isn't provided, standard kubeconfig locations will be used (kubectl loading rules apply). 36 | func NewClusterProxy(scheme *runtime.Scheme) ClusterProxy { 37 | Expect(scheme).NotTo(BeNil(), "scheme is required for NewClusterProxy") 38 | 39 | return &clusterProxy{ 40 | kubeconfigPath: clientcmd.NewDefaultClientConfigLoadingRules().GetDefaultFilename(), 41 | scheme: scheme, 42 | } 43 | } 44 | 45 | // GetClient returns a controller-runtime client for the cluster. 46 | func (p *clusterProxy) GetClient() client.Client { 47 | restConfig := p.getRestConfig() 48 | 49 | c, err := client.New(restConfig, client.Options{Scheme: p.scheme}) 50 | Expect(err).ToNot(HaveOccurred(), "Failed to get controller-runtime client") 51 | 52 | return c 53 | } 54 | 55 | // GetClientSet returns a client-go client for the cluster. 56 | func (p *clusterProxy) GetClientSet() *kubernetes.Clientset { 57 | restConfig := p.getRestConfig() 58 | 59 | cs, err := kubernetes.NewForConfig(restConfig) 60 | Expect(err).ToNot(HaveOccurred(), "Failed to get client-go client") 61 | 62 | return cs 63 | } 64 | 65 | // GetKubeconfigPath returns the path to the kubeconfig file for the cluster. 66 | func (p *clusterProxy) GetKubeconfigPath() string { 67 | return p.kubeconfigPath 68 | } 69 | 70 | func (p *clusterProxy) getRestConfig() *rest.Config { 71 | config, err := clientcmd.LoadFromFile(p.kubeconfigPath) 72 | Expect(err).ToNot(HaveOccurred(), "Failed to load Kubeconfig file from %q", p.kubeconfigPath) 73 | 74 | restConfig, err := clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}).ClientConfig() 75 | Expect(err).ToNot(HaveOccurred(), "Failed to get ClientConfig from %q", p.kubeconfigPath) 76 | 77 | restConfig.UserAgent = "sscdpa-e2e" 78 | return restConfig 79 | } 80 | -------------------------------------------------------------------------------- /test/e2e/framework/daemonset/daemonset.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package daemonset 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | 10 | "github.com/Azure/secrets-store-csi-driver-provider-azure/test/e2e/framework" 11 | 12 | . "github.com/onsi/ginkgo/v2" 13 | . "github.com/onsi/gomega" 14 | appsv1 "k8s.io/api/apps/v1" 15 | "k8s.io/apimachinery/pkg/types" 16 | ) 17 | 18 | // GetInput is the input for Get. 19 | type GetInput struct { 20 | Getter framework.Getter 21 | Name string 22 | Namespace string 23 | } 24 | 25 | // Get gets a DaemonSet resource. 26 | func Get(input GetInput) *appsv1.DaemonSet { 27 | Expect(input.Getter).NotTo(BeNil(), "input.Getter is required for DaemonSet.Get") 28 | Expect(input.Name).NotTo(BeEmpty(), "input.Name is required for DaemonSet.Get") 29 | Expect(input.Namespace).NotTo(BeEmpty(), "input.Namespace is required for DaemonSet.Get") 30 | 31 | By(fmt.Sprintf("Getting DaemonSet \"%s\"", input.Name)) 32 | 33 | daemonSet := &appsv1.DaemonSet{} 34 | Expect(input.Getter.Get(context.TODO(), types.NamespacedName{Name: input.Name, Namespace: input.Namespace}, daemonSet)).Should(Succeed()) 35 | return daemonSet 36 | } 37 | 38 | // UpdateInput is the input for Update. 39 | type UpdateInput struct { 40 | Updater framework.Updater 41 | DaemonSet *appsv1.DaemonSet 42 | } 43 | 44 | // Update updates a DaemonSet resource. 45 | func Update(input UpdateInput) *appsv1.DaemonSet { 46 | Expect(input.Updater).NotTo(BeNil(), "input.Updater is required for DaemonSet.Update") 47 | Expect(input.DaemonSet).NotTo(BeNil(), "input.DaemonSet is required for DaemonSet.Update") 48 | 49 | By(fmt.Sprintf("Updating DaemonSet \"%s/%s\"", input.DaemonSet.Namespace, input.DaemonSet.Name)) 50 | 51 | Expect(input.Updater.Update(context.TODO(), input.DaemonSet)).Should(Succeed()) 52 | return input.DaemonSet 53 | } 54 | -------------------------------------------------------------------------------- /test/e2e/framework/exec/kubectl.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package exec 5 | 6 | import ( 7 | "fmt" 8 | "os/exec" 9 | "strings" 10 | 11 | . "github.com/onsi/ginkgo/v2" 12 | ) 13 | 14 | // KubectlApply executes "kubectl apply" given a list of arguments. 15 | func KubectlApply(kubeconfigPath, namespace string, args []string) error { 16 | args = append([]string{ 17 | "apply", 18 | fmt.Sprintf("--kubeconfig=%s", kubeconfigPath), 19 | fmt.Sprintf("--namespace=%s", namespace), 20 | }, args...) 21 | 22 | _, err := kubectl(args) 23 | return err 24 | } 25 | 26 | // KubectlDelete executes "kubectl delete" given a list of arguments. 27 | func KubectlDelete(kubeconfigPath, namespace string, args []string) error { 28 | args = append([]string{ 29 | "delete", 30 | fmt.Sprintf("--kubeconfig=%s", kubeconfigPath), 31 | fmt.Sprintf("--namespace=%s", namespace), 32 | }, args...) 33 | 34 | _, err := kubectl(args) 35 | return err 36 | } 37 | 38 | // KubectlExec executes "kubectl exec" given a list of arguments. 39 | func KubectlExec(kubeconfigPath, podName, namespace string, args []string) (string, error) { 40 | args = append([]string{ 41 | "exec", 42 | fmt.Sprintf("--kubeconfig=%s", kubeconfigPath), 43 | fmt.Sprintf("--namespace=%s", namespace), 44 | fmt.Sprintf("--request-timeout=5s"), 45 | podName, 46 | "--", 47 | }, args...) 48 | 49 | return kubectl(args) 50 | } 51 | 52 | // KubectlLogs executes "kubectl logs" given a list of arguments. 53 | func KubectlLogs(kubeconfigPath, podName, containerName, namespace string) (string, error) { 54 | args := []string{ 55 | "logs", 56 | fmt.Sprintf("--kubeconfig=%s", kubeconfigPath), 57 | fmt.Sprintf("--namespace=%s", namespace), 58 | podName, 59 | } 60 | 61 | if containerName != "" { 62 | args = append(args, fmt.Sprintf("-c=%s", containerName)) 63 | } 64 | 65 | return kubectl(args) 66 | } 67 | 68 | // KubectlDescribe executes "kubectl describe" given a list of arguments. 69 | func KubectlDescribe(kubeconfigPath, podName, namespace string) (string, error) { 70 | args := []string{ 71 | "describe", 72 | "pod", 73 | podName, 74 | fmt.Sprintf("--kubeconfig=%s", kubeconfigPath), 75 | fmt.Sprintf("--namespace=%s", namespace), 76 | } 77 | return kubectl(args) 78 | } 79 | 80 | func kubectl(args []string) (string, error) { 81 | By(fmt.Sprintf("kubectl %s", strings.Join(args, " "))) 82 | 83 | cmd := exec.Command("kubectl", args...) 84 | stdoutStderr, err := cmd.CombinedOutput() 85 | 86 | if err != nil { 87 | By(fmt.Sprintf("kubectl %s failed: %s", strings.Join(args, " "), stdoutStderr)) 88 | } 89 | return strings.TrimSpace(string(stdoutStderr)), err 90 | } 91 | -------------------------------------------------------------------------------- /test/e2e/framework/interfaces.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package framework 5 | 6 | import ( 7 | "context" 8 | 9 | "sigs.k8s.io/controller-runtime/pkg/client" 10 | ) 11 | 12 | // Interfaces to scope down client.Client 13 | 14 | // Getter can get resources. 15 | type Getter interface { 16 | Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error 17 | } 18 | 19 | // Creator can create resources. 20 | type Creator interface { 21 | Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error 22 | } 23 | 24 | // Lister can list resources. 25 | type Lister interface { 26 | List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error 27 | } 28 | 29 | // Deleter can delete resources. 30 | type Deleter interface { 31 | Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error 32 | } 33 | 34 | // Updater can update resources. 35 | type Updater interface { 36 | Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error 37 | } 38 | 39 | // GetLister can get and list resources. 40 | type GetLister interface { 41 | Getter 42 | Lister 43 | } 44 | -------------------------------------------------------------------------------- /test/e2e/framework/keyvault/keyvault.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package keyvault 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | 10 | "github.com/Azure/secrets-store-csi-driver-provider-azure/test/e2e/framework" 11 | 12 | "github.com/Azure/azure-sdk-for-go/sdk/azcore" 13 | "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" 14 | "github.com/Azure/azure-sdk-for-go/sdk/azidentity" 15 | "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets" 16 | "github.com/Azure/go-autorest/autorest/azure" 17 | "github.com/Azure/go-autorest/autorest/to" 18 | . "github.com/onsi/ginkgo/v2" 19 | . "github.com/onsi/gomega" 20 | ) 21 | 22 | type Client interface { 23 | // SetSecret sets the secret in key vault 24 | SetSecret(name, value string) error 25 | // DeleteSecret deletes the secret in key vault 26 | DeleteSecret(name string) error 27 | } 28 | 29 | type client struct { 30 | config *framework.Config 31 | secretsClient *azsecrets.Client 32 | } 33 | 34 | func NewClient(config *framework.Config) Client { 35 | opts := &azidentity.ManagedIdentityCredentialOptions{ 36 | ClientOptions: azcore.ClientOptions{ 37 | Cloud: cloud.Configuration{ 38 | ActiveDirectoryAuthorityHost: azure.PublicCloud.ActiveDirectoryEndpoint, 39 | }, 40 | }, 41 | ID: azidentity.ClientID(config.KeyvaultClientID), 42 | } 43 | cred, err := azidentity.NewManagedIdentityCredential(opts) 44 | Expect(err).To(BeNil()) 45 | 46 | c, err := azsecrets.NewClient(getVaultURL(config.KeyvaultName), cred, nil) 47 | Expect(err).To(BeNil()) 48 | 49 | return &client{ 50 | config: config, 51 | secretsClient: c, 52 | } 53 | } 54 | 55 | // SetSecret sets the secret in key vault 56 | func (c *client) SetSecret(name, value string) error { 57 | params := azsecrets.SetSecretParameters{ 58 | Value: to.StringPtr(value), 59 | } 60 | 61 | By(fmt.Sprintf("Setting secret \"%s\" in keyvault \"%s\"", name, c.config.KeyvaultName)) 62 | _, err := c.secretsClient.SetSecret(context.Background(), name, params, &azsecrets.SetSecretOptions{}) 63 | return err 64 | } 65 | 66 | // DeleteSecret deletes the secret in key vault 67 | func (c *client) DeleteSecret(name string) error { 68 | By(fmt.Sprintf("Deleting secret \"%s\" in keyvault \"%s\"", name, c.config.KeyvaultName)) 69 | _, err := c.secretsClient.DeleteSecret(context.Background(), name, &azsecrets.DeleteSecretOptions{}) 70 | return err 71 | } 72 | 73 | func getVaultURL(vaultName string) string { 74 | return fmt.Sprintf("https://%s.%s/", vaultName, azure.PublicCloud.KeyVaultDNSSuffix) 75 | } 76 | -------------------------------------------------------------------------------- /test/e2e/framework/namespace/namespace.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package namespace 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | 10 | "github.com/Azure/secrets-store-csi-driver-provider-azure/test/e2e/framework" 11 | 12 | . "github.com/onsi/ginkgo/v2" 13 | . "github.com/onsi/gomega" 14 | corev1 "k8s.io/api/core/v1" 15 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 | "sigs.k8s.io/controller-runtime/pkg/client" 17 | ) 18 | 19 | // CreateInput is the input for Create. 20 | type CreateInput struct { 21 | Creator framework.Creator 22 | Name string 23 | } 24 | 25 | // Create creates a namespace with the provided name as generate name. 26 | func Create(input CreateInput) *corev1.Namespace { 27 | Expect(input.Creator).NotTo(BeNil(), "input.Creator is required for Namespace.Create") 28 | Expect(input.Name).NotTo(BeEmpty(), "input.Name is required for Namespace.Create") 29 | 30 | ns := &corev1.Namespace{ 31 | ObjectMeta: metav1.ObjectMeta{ 32 | GenerateName: fmt.Sprintf("%s-", input.Name), 33 | }, 34 | } 35 | 36 | Expect(input.Creator.Create(context.TODO(), ns)).Should(Succeed()) 37 | By(fmt.Sprintf("Creating namespace \"%s\"", ns.Name)) 38 | 39 | return ns 40 | } 41 | 42 | // CreateWithName creates a namespace with the provided name as the namespace name. 43 | func CreateWithName(input CreateInput) *corev1.Namespace { 44 | Expect(input.Creator).NotTo(BeNil(), "input.Creator is required for Namespace.CreateWithName") 45 | Expect(input.Name).NotTo(BeEmpty(), "input.Name is required for Namespace.CreateWithName") 46 | 47 | ns := &corev1.Namespace{ 48 | ObjectMeta: metav1.ObjectMeta{ 49 | Name: input.Name, 50 | }, 51 | } 52 | 53 | Expect(input.Creator.Create(context.TODO(), ns)).Should(Succeed()) 54 | By(fmt.Sprintf("Creating namespace \"%s\"", ns.Name)) 55 | 56 | return ns 57 | } 58 | 59 | // DeleteInput is the input for Delete. 60 | type DeleteInput struct { 61 | Deleter framework.Deleter 62 | Getter framework.Getter 63 | Namespace *corev1.Namespace 64 | } 65 | 66 | // Delete deletes a namespace. 67 | func Delete(input DeleteInput) { 68 | Expect(input.Deleter).NotTo(BeNil(), "input.Deleter is required for Namespace.Delete") 69 | Expect(input.Getter).NotTo(BeNil(), "input.Getter is required for Namespace.Delete") 70 | Expect(input.Namespace).NotTo(BeNil(), "input.Namespace is required for Namespace.Delete") 71 | 72 | By(fmt.Sprintf("Deleting namespace \"%s\"", input.Namespace.Name)) 73 | Expect(input.Deleter.Delete(context.TODO(), input.Namespace)).Should(Succeed()) 74 | 75 | Eventually(func() bool { 76 | return input.Getter.Get(context.TODO(), client.ObjectKey{Name: input.Namespace.Name}, &corev1.Namespace{}) != nil 77 | }, framework.Timeout, framework.Polling).Should(BeTrue()) 78 | } 79 | -------------------------------------------------------------------------------- /test/e2e/framework/openssl/openssl.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package openssl 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | "os/exec" 10 | "strings" 11 | 12 | . "github.com/onsi/ginkgo/v2" 13 | . "github.com/onsi/gomega" 14 | ) 15 | 16 | // ParsePKCS12 parses PKCS#12 pfx data and returns pem with private key 17 | // and certificate 18 | func ParsePKCS12(pfxData, password string) (string, error) { 19 | tmpFile, err := os.CreateTemp("", "") 20 | Expect(err).To(BeNil()) 21 | 22 | _, err = tmpFile.Write([]byte(pfxData)) 23 | Expect(err).To(BeNil()) 24 | defer os.Remove(tmpFile.Name()) 25 | 26 | args := append([]string{ 27 | "pkcs12", 28 | "-nodes", 29 | "-passin", 30 | fmt.Sprintf("pass:%s", password), 31 | "-in", 32 | tmpFile.Name(), 33 | }) 34 | 35 | out, err := openssl(args) 36 | return out, err 37 | } 38 | 39 | func openssl(args []string) (string, error) { 40 | By(fmt.Sprintf("openssl %s", strings.Join(args, " "))) 41 | 42 | cmd := exec.Command("openssl", args...) 43 | stdoutStderr, err := cmd.CombinedOutput() 44 | 45 | return string(stdoutStderr), err 46 | } 47 | -------------------------------------------------------------------------------- /test/e2e/framework/scheme.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package framework 5 | 6 | import ( 7 | appsv1 "k8s.io/api/apps/v1" 8 | corev1 "k8s.io/api/core/v1" 9 | rbacv1 "k8s.io/api/rbac/v1" 10 | apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 11 | apiextensionsv1beta "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 12 | "k8s.io/apimachinery/pkg/runtime" 13 | 14 | "sigs.k8s.io/secrets-store-csi-driver/apis/v1alpha1" 15 | ) 16 | 17 | const ( 18 | // NamespaceKubeSystem is the name of kube-system namespace. 19 | NamespaceKubeSystem = "kube-system" 20 | ) 21 | 22 | // TryAddDefaultSchemes tries to add various schemes. 23 | func TryAddDefaultSchemes(scheme *runtime.Scheme) { 24 | // Add the core schemes. 25 | _ = corev1.AddToScheme(scheme) 26 | 27 | // Add the apps schemes. 28 | _ = appsv1.AddToScheme(scheme) 29 | 30 | // Add the api extensions (CRD) to the scheme. 31 | _ = apiextensionsv1beta.AddToScheme(scheme) 32 | _ = apiextensionsv1.AddToScheme(scheme) 33 | 34 | // Add rbac to the scheme. 35 | _ = rbacv1.AddToScheme(scheme) 36 | 37 | // Add secrets-store-csi v1alpha1 to the scheme 38 | _ = v1alpha1.AddToScheme(scheme) 39 | } 40 | -------------------------------------------------------------------------------- /test/e2e/framework/secret/secret.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package secret 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | 10 | "github.com/Azure/secrets-store-csi-driver-provider-azure/test/e2e/framework" 11 | 12 | . "github.com/onsi/ginkgo/v2" 13 | . "github.com/onsi/gomega" 14 | v1 "k8s.io/api/core/v1" 15 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 | "k8s.io/apimachinery/pkg/types" 17 | ) 18 | 19 | // CreateInput is the input for Create. 20 | type CreateInput struct { 21 | Creator framework.Creator 22 | Name string 23 | Namespace string 24 | Data map[string][]byte 25 | Labels map[string]string 26 | } 27 | 28 | // Create creates a Secret resource. 29 | func Create(input CreateInput) *v1.Secret { 30 | Expect(input.Creator).NotTo(BeNil(), "input.Creator is required for Secret.Create") 31 | Expect(input.Name).NotTo(BeEmpty(), "input.Name is required for Secret.Create") 32 | Expect(input.Namespace).NotTo(BeEmpty(), "input.Namespace is required for Secret.Create") 33 | Expect(input.Data).NotTo(BeEmpty(), "input.Data is required for Secret.Create") 34 | 35 | By(fmt.Sprintf("Creating Secret \"%s\"", input.Name)) 36 | 37 | secret := &v1.Secret{ 38 | ObjectMeta: metav1.ObjectMeta{ 39 | Name: input.Name, 40 | Namespace: input.Namespace, 41 | Labels: input.Labels, 42 | }, 43 | Data: input.Data, 44 | } 45 | 46 | Expect(input.Creator.Create(context.TODO(), secret)).Should(Succeed()) 47 | return secret 48 | } 49 | 50 | // GetInput is the input for Get. 51 | type GetInput struct { 52 | Getter framework.Getter 53 | Name string 54 | Namespace string 55 | } 56 | 57 | func Get(input GetInput) *v1.Secret { 58 | Expect(input.Getter).NotTo(BeNil(), "input.Getter is required for Secret.Get") 59 | Expect(input.Name).NotTo(BeEmpty(), "input.Name is required for Secret.Get") 60 | Expect(input.Namespace).NotTo(BeEmpty(), "input.Namespace is required for Secret.Get") 61 | 62 | By(fmt.Sprintf("Getting Secret \"%s\"", input.Name)) 63 | 64 | secret := &v1.Secret{} 65 | Expect(input.Getter.Get(context.TODO(), types.NamespacedName{Name: input.Name, Namespace: input.Namespace}, secret)).Should(Succeed()) 66 | return secret 67 | } 68 | 69 | // DeleteInput is the input for Delete. 70 | type DeleteInput struct { 71 | Deleter framework.Deleter 72 | Secret *v1.Secret 73 | } 74 | 75 | // Delete deletes a Secret resource. 76 | func Delete(input DeleteInput) { 77 | Expect(input.Deleter).NotTo(BeNil(), "input.Deleter is required for Secret.Delete") 78 | Expect(input.Secret).NotTo(BeNil(), "input.Secret is required for Secret.Delete") 79 | 80 | By(fmt.Sprintf("Deleting Secret \"%s\"", input.Secret.Name)) 81 | Expect(input.Deleter.Delete(context.TODO(), input.Secret)).Should(Succeed()) 82 | } 83 | -------------------------------------------------------------------------------- /test/e2e/framework/serviceaccount/serviceaccount.go: -------------------------------------------------------------------------------- 1 | package serviceaccount 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/Azure/secrets-store-csi-driver-provider-azure/test/e2e/framework" 8 | 9 | . "github.com/onsi/ginkgo/v2" 10 | . "github.com/onsi/gomega" 11 | corev1 "k8s.io/api/core/v1" 12 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 | ) 14 | 15 | // CreateInput is the input for Create. 16 | type CreateInput struct { 17 | Creator framework.Creator 18 | Name string 19 | Namespace string 20 | } 21 | 22 | // Create creates a ServiceAccount resource. 23 | func Create(input CreateInput) *corev1.ServiceAccount { 24 | Expect(input.Creator).NotTo(BeNil(), "input.Creator is required for ServiceAccount.Create") 25 | Expect(input.Name).NotTo(BeEmpty(), "input.Name is required for ServiceAccount.Create") 26 | Expect(input.Namespace).NotTo(BeEmpty(), "input.Namespace is required for ServiceAccount.Create") 27 | 28 | By(fmt.Sprintf("Creating ServiceAccount \"%s/%s\"", input.Namespace, input.Name)) 29 | sa := &corev1.ServiceAccount{ 30 | ObjectMeta: metav1.ObjectMeta{ 31 | Name: input.Name, 32 | Namespace: input.Namespace, 33 | }, 34 | } 35 | 36 | Expect(input.Creator.Create(context.TODO(), sa)).Should(Succeed()) 37 | return sa 38 | } 39 | -------------------------------------------------------------------------------- /test/e2e/framework/timeout.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package framework 5 | 6 | import ( 7 | "time" 8 | ) 9 | 10 | const ( 11 | // Timeout represents the duration it waits until a long-running operation times out. 12 | // 10m is for windows nodes as pods can take time to come up 13 | Timeout = 10 * time.Minute 14 | 15 | // Polling represents the polling interval for a long-running operation. 16 | Polling = 5 * time.Second 17 | ) 18 | -------------------------------------------------------------------------------- /test/load/config-deployment-template.yaml: -------------------------------------------------------------------------------- 1 | name: csi-secrets-store-load 2 | automanagedNamespaces: 100 3 | tuningSets: 4 | - name: Uniform500qps 5 | qpsLoad: 6 | qps: 500 7 | steps: 8 | - name: Starting measurements 9 | measurements: 10 | - Identifier: ResourceUsageSummary 11 | Method: ResourceUsageSummary 12 | Params: 13 | action: start 14 | - Identifier: PodStartupLatency 15 | Method: PodStartupLatency 16 | Params: 17 | action: start 18 | labelSelector: group = deployment 19 | threshold: 15m 20 | - measurements: 21 | - Identifier: WaitForRunningDeployment 22 | Method: WaitForControlledPodsRunning 23 | Params: 24 | action: start 25 | apiVersion: apps/v1 26 | kind: Deployment 27 | labelSelector: group = deployment 28 | operationTimeout: 15m 29 | - name: Creating objects 30 | phases: 31 | - namespaceRange: 32 | min: 1 33 | max: 100 34 | replicasPerNamespace: 1 35 | tuningSet: Uniform500qps 36 | objectBundle: 37 | - basename: deployment 38 | objectTemplatePath: secret-provider-class.yaml 39 | - basename: deployment 40 | objectTemplatePath: deployment.yaml 41 | templateFillMap: 42 | Replicas: $POD_COUNT_PER_DEPLOYMENT 43 | Group: deployment 44 | - name: Waiting for pods to be running 45 | measurements: 46 | - Identifier: WaitForRunningDeployment 47 | Method: WaitForControlledPodsRunning 48 | Params: 49 | action: gather 50 | - name: Deleting objects 51 | phases: 52 | - namespaceRange: 53 | min: 1 54 | max: 1 55 | replicasPerNamespace: 0 56 | tuningSet: Uniform500qps 57 | objectBundle: 58 | - basename: deployment 59 | objectTemplatePath: deployment.yaml 60 | - measurements: 61 | - Identifier: WaitForRunningDeployment 62 | Method: WaitForControlledPodsRunning 63 | Params: 64 | action: gather 65 | - measurements: 66 | - Identifier: ResourceUsageSummary 67 | Method: ResourceUsageSummary 68 | Params: 69 | action: gather 70 | - measurements: 71 | - Identifier: PodStartupLatency 72 | Method: PodStartupLatency 73 | Params: 74 | action: gather 75 | -------------------------------------------------------------------------------- /test/load/deployment-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{.Name}} 5 | labels: 6 | group: {{.Group}} 7 | spec: 8 | replicas: {{.Replicas}} 9 | selector: 10 | matchLabels: 11 | name: {{.Name}} 12 | template: 13 | metadata: 14 | labels: 15 | name: {{.Name}} 16 | group: {{.Group}} 17 | spec: 18 | containers: 19 | - name: pause 20 | image: registry.k8s.io/pause-amd64:3.1 21 | imagePullPolicy: IfNotPresent 22 | volumeMounts: 23 | - name: secrets-store-inline 24 | mountPath: "/mnt/secrets-store" 25 | readOnly: true 26 | env: 27 | - name: FOO_SECRET1 28 | valueFrom: 29 | secretKeyRef: 30 | name: foosecret 31 | key: foo 32 | volumes: 33 | - name: secrets-store-inline 34 | csi: 35 | driver: secrets-store.csi.k8s.io 36 | readOnly: true 37 | volumeAttributes: 38 | secretProviderClass: {{.Name}} 39 | -------------------------------------------------------------------------------- /test/load/secret-provider-class-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: secrets-store.csi.x-k8s.io/v1 2 | kind: SecretProviderClass 3 | metadata: 4 | name: {{.Name}} 5 | spec: 6 | provider: azure 7 | secretObjects: 8 | - data: 9 | - key: foo 10 | objectName: secret1 11 | - key: bar 12 | objectName: secret2 13 | secretName: foosecret 14 | type: Opaque 15 | parameters: 16 | usePodIdentity: "false" 17 | useVMManagedIdentity: "true" 18 | userAssignedIdentityID: "$USER_ASSIGNED_IDENTITY_CLIENT_ID" 19 | keyvaultName: "$KEYVAULT_NAME" 20 | objects: | 21 | array: 22 | - | 23 | objectName: secret1 24 | objectType: secret 25 | objectVersion: "" 26 | - | 27 | objectName: secret2 28 | objectType: secret 29 | objectVersion: "" 30 | tenantId: $TENANT_ID 31 | -------------------------------------------------------------------------------- /tools/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package tools 5 | 6 | import ( 7 | _ "github.com/client9/misspell/cmd/misspell" 8 | _ "github.com/golang/mock/mockgen" 9 | _ "github.com/golangci/golangci-lint/cmd/golangci-lint" 10 | ) 11 | -------------------------------------------------------------------------------- /website/Makefile: -------------------------------------------------------------------------------- 1 | bootstrap: 2 | git submodule update --init --recursive 3 | npm i 4 | 5 | serve: bootstrap 6 | hugo server \ 7 | --buildDrafts \ 8 | --buildFuture \ 9 | --disableFastRender 10 | 11 | production-build: bootstrap 12 | hugo --minify 13 | 14 | preview-build: bootstrap 15 | hugo \ 16 | --baseURL $(DEPLOY_PRIME_URL) \ 17 | --buildDrafts \ 18 | --buildFuture \ 19 | --minify 20 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # Azure Key Vault Provider for Secrets Store CSI Driver documentation 2 | 3 | If you are looking to explore the Azure Key Vault Provider for Secrets Store CSI Driver documentation, please go to the documentation website: 4 | 5 | [**https://azure.github.io/secrets-store-csi-driver-provider-azure/**](https://azure.github.io/secrets-store-csi-driver-provider-azure/) 6 | 7 | This repo contains the markdown files which generate the above website. See below for guidance on running with a local environment to contribute to the docs. 8 | 9 | ## Contribution guidelines 10 | 11 | Before making your first contribution, make sure to review the [Contributing Guidelines](https://azure.github.io/secrets-store-csi-driver-provider-azure/contribution-guidelines/) in the docs. 12 | 13 | ## Overview 14 | 15 | The Azure Key Vault Provider for Secrets Store CSI Driver docs are built using [Hugo](https://gohugo.io/) with the [Docsy](https://docsy.dev) theme, hosted using [GitHub Pages](https://pages.github.com/). 16 | 17 | The [website](./website) directory contains the hugo project, markdown files, and theme configurations. 18 | 19 | ## Pre-requisites 20 | 21 | - [Hugo extended version](https://gohugo.io/getting-started/installing) 22 | - [Node.js](https://nodejs.org/en/) 23 | 24 | ## Environment setup 25 | 26 | 1. Ensure pre-requisites are installed 27 | 2. Clone this repository 28 | ```sh 29 | git clone https://github.com/Azure/secrets-store-csi-driver-provider-azure.git 30 | ``` 31 | 3. Change to website directory: 32 | ```sh 33 | cd website 34 | ``` 35 | 4. Add Docsy submodule: 36 | ```sh 37 | git submodule add https://github.com/google/docsy.git themes/docsy 38 | ``` 39 | 5. Update submodules: 40 | ```sh 41 | git submodule update --init --recursive 42 | ``` 43 | 6. Install npm packages: 44 | ```sh 45 | npm install 46 | ``` 47 | 48 | ## Run local server 49 | 1. Make sure you're still in the `website` directory 50 | 2. Run 51 | ```sh 52 | hugo server --disableFastRender 53 | ``` 54 | 3. Navigate to `http://localhost:1313/docs` 55 | 56 | ## Update docs 57 | 1. Create new branch 58 | 1. Commit and push changes to content 59 | 1. Submit pull request to `master` 60 | 1. Staging site will automatically get created and linked to PR to review and test 61 | -------------------------------------------------------------------------------- /website/assets/scss/_variables_project.scss: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Add styles or override variables from the theme here. 4 | 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /website/content/en/_index.md: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | type: docs 4 | title: "Azure Key Vault Provider for Secrets Store CSI Driver" 5 | linkTitle: "Documentation" 6 | weight: 20 7 | menu: 8 | main: 9 | weight: 20 10 | --- 11 | 12 | Azure Key Vault provider for [Secrets Store CSI Driver](https://github.com/kubernetes-sigs/secrets-store-csi-driver) allows you to get secret contents stored in an [Azure Key Vault](https://docs.microsoft.com/en-us/azure/key-vault/general/overview) instance and use the Secrets Store CSI driver interface to mount them into Kubernetes pods. 13 | 14 | ## Project Status 15 | 16 | | Azure Key Vault Provider | Compatible Kubernetes | `secrets-store.csi.x-k8s.io` Versions | 17 | | ---------------------------------------------------------------------------------------------- | --------------------- | ------------------------------------- | 18 | | [v1.5.2](https://github.com/Azure/secrets-store-csi-driver-provider-azure/releases/tag/v1.5.2) | 1.21+ | `v1`, `v1alpha1 [DEPRECATED]` | 19 | | [v1.4.1](https://github.com/Azure/secrets-store-csi-driver-provider-azure/releases/tag/v1.4.1) | 1.21+ | `v1`, `v1alpha1 [DEPRECATED]` | 20 | 21 | For Secrets Store CSI Driver project status and supported versions, check the doc [here](https://secrets-store-csi-driver.sigs.k8s.io/#project-status) 22 | 23 | ## Features 24 | 25 | - Mounts secrets/keys/certs to pod using a CSI Inline volume 26 | - Supports mounting multiple secrets store objects as a single volume 27 | - Supports multiple secrets stores as providers. Multiple providers can run in the same cluster simultaneously. 28 | - Supports pod portability with the SecretProviderClass CRD 29 | - Supports Linux and Windows containers 30 | - Supports sync with Kubernetes Secrets 31 | - Supports auto rotation of secrets 32 | 33 | ## Managed Add-ons 34 | Azure Key Vault provider for Secrets Store CSI Driver is available as a managed add-on in: 35 | - Azure Kubernetes Service (AKS). For more information, see [Use the Azure Key Vault Provider for Secrets Store CSI Driver in an AKS cluster](https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-driver). 36 | - Azure Arc enabled Kubernetes. For more information, see [Use the Azure Key Vault Secrets Provider extension to fetch secrets into Azure Arc-enabled Kubernetes clusters](https://learn.microsoft.com/en-us/azure/azure-arc/kubernetes/tutorial-akv-secrets-provider). 37 | -------------------------------------------------------------------------------- /website/content/en/configurations/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: docs 3 | title: "Configurations" 4 | linkTitle: "Configurations" 5 | weight: 4 6 | description: > 7 | An overview of all the configuration features of Azure KeyVault Provider for Secrets Store CSI Driver 8 | --- -------------------------------------------------------------------------------- /website/content/en/configurations/deploy-in-openshift.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: docs 3 | title: "Setup Secrets Store CSI Driver on Azure RedHat OpenShift (ARO)" 4 | linkTitle: "Setup Secrets Store CSI Driver on Azure RedHat OpenShift (ARO)" 5 | weight: 6 6 | description: > 7 | How to setup Azure Keyvault Provider for Secrets Store CSI Driver on Azure RedHat OpenShift (ARO) 8 | --- 9 | 10 | ### Installation 11 | 12 | 1. Install the Azure Keyvault provider for Secrets Store CSI Driver on Azure RedHat OpenShift run: 13 | 14 | ```bash 15 | helm repo add csi-secrets-store-provider-azure https://azure.github.io/secrets-store-csi-driver-provider-azure/charts 16 | helm install csi csi-secrets-store-provider-azure/csi-secrets-store-provider-azure --set linux.privileged=true 17 | ``` 18 | 19 | Setting `linux.privileged=true` with `helm install` will enable privileged mode for the Linux *daemonset* pods. 20 | 21 | ```yml 22 | securityContext: 23 | privileged: true 24 | ``` 25 | 26 | This is required for the AKV provider pods to successfully startup in ARO. 27 | 28 | 1. Bind SecurityContextConstraints (SCC) to the Secrets Store CSI Driver and Azure Keyvault Provider service accounts 29 | 30 | ```bash 31 | # Replace $target_namespace with the namespace used for helm install 32 | # Secrets Store CSI Driver service account 33 | oc adm policy add-scc-to-user privileged system:serviceaccount:$target_namespace:secrets-store-csi-driver 34 | # Azure Keyvault Provider service account 35 | oc adm policy add-scc-to-user privileged system:serviceaccount:$target_namespace:csi-secrets-store-provider-azure 36 | ``` 37 | 38 | ### Uninstall 39 | 40 | 1. Run the following command to uninstall 41 | 42 | ```bash 43 | helm delete 44 | ``` 45 | 46 | 1. Remove the SCC bindings 47 | 48 | ```bash 49 | # Replace $target_namespace with the namespace used for helm install 50 | oc adm policy remove-scc-from-user privileged system:serviceaccount:$target_namespace:secrets-store-csi-driver 51 | oc adm policy remove-scc-from-user privileged system:serviceaccount:$target_namespace:csi-secrets-store-provider-azure 52 | ``` 53 | -------------------------------------------------------------------------------- /website/content/en/configurations/enable-auto-rotation-secrets.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: docs 3 | title: "Enable Auto Rotation of Secrets" 4 | linkTitle: "Enable Auto Rotation of Secrets" 5 | weight: 2 6 | description: > 7 | Periodically update the pod mount and Kubernetes Secret with the latest content from external secrets store 8 | --- 9 | 10 | You can setup the Secrets Store CSI Driver to periodically update the pod volume mount and Kubernetes Secret with the latest content from external secrets-store. Refer to [doc](https://secrets-store-csi-driver.sigs.k8s.io/topics/secret-auto-rotation.html) for steps on enabling auto rotation. 11 | 12 | To enable this feature while installing azure provider helm chart, you can use the following helm `--set` flags: 13 | ```bash 14 | helm install csi csi-secrets-store-provider-azure/csi-secrets-store-provider-azure --namespace kube-system --set secrets-store-csi-driver.enableSecretRotation=true --set secrets-store-csi-driver.rotationPollInterval=2m 15 | ``` 16 | 17 | {{% alert title="NOTE" color="warning" %}} 18 | The CSI driver **does not restart** the application pods. It only handles updating the pod mount and Kubernetes secret similar to how Kubernetes handles updates to Kubernetes secret mounted as volumes. 19 | {{% /alert %}} 20 | -------------------------------------------------------------------------------- /website/content/en/configurations/feature-flags.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: docs 3 | title: "Feature Flags" 4 | linkTitle: "Feature Flags" 5 | weight: 1 6 | description: > 7 | Optional configuration feature flags 8 | --- 9 | 10 | ## Construct PEM Chain Feature Flag 11 | 12 | > Available in AKV Provider release `0.0.12+` 13 | 14 | > This feature is enabled by default in AKV Provider release `v0.2.0` 15 | 16 | The Azure Key Vault provider for Secrets Store CSI Driver by default fetches the chain of certificates from Keyvault and writes to the mount in the same order in which the certificate chain was uploaded. This is an experimental feature that supports reordering of the certificate chain in the following order: 17 | 18 | ```bash 19 | SERVER 20 | INTERMEDIATE 21 | ROOT 22 | KEY 23 | ``` 24 | 25 | To enable this feature, set `--construct-pem-chain=true` in the provider deployment YAMLs. If using helm to install the driver and provider, set `constructPEMChain: true`. 26 | 27 | Refer to [#156](https://github.com/Azure/secrets-store-csi-driver-provider-azure/issues/156) for more details. 28 | -------------------------------------------------------------------------------- /website/content/en/configurations/identity-access-modes/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: docs 3 | title: "Identity Access Modes" 4 | linkTitle: "Identity Access Modes" 5 | weight: 1 6 | description: > 7 | The Azure Key Vault Provider offers four modes for accessing a Key Vault instance 8 | --- 9 | 10 | ## Best Practices 11 | 12 | Following order of access modes is recommended for Secret Store CSI driver AKV provider: 13 | 14 | | Access Option | Comment | 15 | | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 16 | | Workload Identity [**RECOMMENDED**] | This is the most secure way to access Key Vault based on the [Workload Identity Federation](https://docs.microsoft.com/en-us/azure/active-directory/develop/workload-identity-federation). | 17 | | Pod Identity [**NOT RECOMMENDED**] | [AAD Pod Identity](https://github.com/Azure/aad-pod-identity) has been [DEPRECATED](https://github.com/Azure/aad-pod-identity#-announcement).
This provides a way to get access to Azure resources (AKV in this case) using the managed identity bound to the Pod.
| 18 | | Managed Identities (System-assigned and User-assigned) | Managed identities eliminate the need for developers to manage credentials. Managed identities provide an identity for applications to use when connecting to Azure Keyvault. | 19 | | Service Principal | This is the last option to consider while connecting to AKV as access credentials need to be created as Kubernetes Secret and stored in plain text in etcd. | 20 | -------------------------------------------------------------------------------- /website/content/en/contribution-guidelines/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: docs 3 | title: "Contribution Guidelines" 4 | linkTitle: "Contribution Guidelines" 5 | weight: 10 6 | description: > 7 | How to contribute to the project 8 | --- 9 | 10 | # Contributing 11 | 12 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 13 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 14 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 15 | 16 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 17 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 18 | provided by the bot. You will only need to do this once across all repos using our CLA. 19 | 20 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 21 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 22 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 23 | 24 | 25 | -------------------------------------------------------------------------------- /website/content/en/demos/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: docs 3 | title: "Demos" 4 | linkTitle: "Demos" 5 | weight: 2 6 | description: > 7 | We have created several demos for you to get familiar with the Azure KeyVault Provider for Secrets Store CSI Driver 8 | --- -------------------------------------------------------------------------------- /website/content/en/demos/community-demos-and-presentations/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: docs 3 | title: "Community Demos and Presentations" 4 | linkTitle: "Community Demos and Presentations" 5 | weight: 3 6 | description: > 7 | Demos and Presentations featured by our community contributors and users 8 | --- -------------------------------------------------------------------------------- /website/content/en/demos/community-demos-and-presentations/houssem-dellai-workload-identity.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: docs 3 | title: "Houssem Dellai - Retrieving database login info from Azure KeyVault using Workload Identity" 4 | linkTitle: "Houssem Dellai - Retrieving database login info from Azure KeyVault using Workload Identity" 5 | weight: 1 6 | description: > 7 | This demo created by [Houssem Dellai](https://twitter.com/HoussemDellai) is using Workload Identity and Secret Store CSI provider for Key Vault to retrieve database login and password from Azure Key Vault. 8 | --- 9 | 10 | {{< youtube paaFMgWpCjQ >}} 11 | -------------------------------------------------------------------------------- /website/content/en/demos/community-demos-and-presentations/houssem-dellai.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: docs 3 | title: "Houssem Dellai - Retrieving database login info from Azure KeyVault" 4 | linkTitle: "Houssem Dellai - Retrieving database login info from Azure KeyVault" 5 | weight: 1 6 | description: > 7 | This demo created by [Houssem Dellai](https://twitter.com/HoussemDellai) is using AAD Pod Identity and Secret Store CSI provider for Key Vault to retrieve database login and password from Azure Key Vault. 8 | --- 9 | 10 | {{< youtube dAFWrbeA6vQ >}} -------------------------------------------------------------------------------- /website/content/en/demos/community-demos-and-presentations/nilesh-gule.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: docs 3 | title: "Nilesh Gule - Retrieving RabbitMQ related secret from Azure KeyVault" 4 | linkTitle: "Nilesh Gule - Retrieving RabbitMQ related secret from Azure KeyVault" 5 | weight: 1 6 | description: > 7 | This demo created by [Nilesh Gule](https://twitter.com/NileshGule) is using VMSS Managed Identity and Secret Store CSI provider for Key Vault to retrieve RabbitMQ related secret from Azure Key Vault. 8 | --- 9 | 10 | These secrets are synced with Kubernetes Secret object and then injected into deployment as ENV variables. 11 | 12 | {{< youtube MHm4IVGVO1w >}} -------------------------------------------------------------------------------- /website/content/en/getting-started/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: docs 3 | title: "Getting Started" 4 | linkTitle: "Getting Started" 5 | weight: 1 6 | description: > 7 | This guide will walk you through the steps to configure and run the Azure Key Vault provider for Secrets Store CSI driver on Kubernetes. 8 | --- 9 | -------------------------------------------------------------------------------- /website/content/en/support/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: docs 3 | title: "Support" 4 | linkTitle: "Support" 5 | weight: 5 6 | description: > 7 | Azure Key Vault Provider for Secrets Store CSI Driver is an open source project that is not covered by the Microsoft Azure support policy 8 | --- 9 | 10 | Azure Key Vault Provider for Secrets Store CSI Driver is an open source project that is [**not** covered by the Microsoft Azure support policy](https://support.microsoft.com/en-us/help/2941892/support-for-linux-and-open-source-technology-in-azure). [Please search open issues here](https://github.com/Azure/secrets-store-csi-driver-provider-azure/issues), and if your issue isn't already represented please [open a new one](https://github.com/Azure/secrets-store-csi-driver-provider-azure/issues/new/choose). The project maintainers will respond to the best of their abilities. -------------------------------------------------------------------------------- /website/layouts/404.html: -------------------------------------------------------------------------------- 1 | {{ define "main"}} 2 |
3 |
4 |

Not found

5 |

Oops! This page doesn't exist. Try going back to our home page.

6 | 7 |

You can learn how to make a 404 page like this in Custom 404 Pages.

8 |
9 |
10 | {{ end }} 11 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tech-doc-hugo", 3 | "version": "0.0.1", 4 | "description": "Hugo theme for technical documentation.", 5 | "main": "none.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/google/docsy-example.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/google/docsy-example/issues" 17 | }, 18 | "homepage": "https://github.com/google/docsy-example#readme", 19 | "dependencies": {}, 20 | "devDependencies": { 21 | "autoprefixer": "^9.8.8", 22 | "postcss-cli": "^7.1.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /windows.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG OSVERSION 2 | FROM --platform=linux/amd64 gcr.io/k8s-staging-e2e-test-images/windows-servercore-cache:1.0-linux-amd64-${OSVERSION} as core 3 | 4 | FROM mcr.microsoft.com/windows/nanoserver:${OSVERSION} 5 | LABEL maintainers="aramase" 6 | LABEL description="Secrets Store CSI Driver Provider Azure" 7 | 8 | ARG TARGETARCH 9 | 10 | COPY ./_output/${TARGETARCH}/secrets-store-csi-driver-provider-azure.exe /secrets-store-csi-driver-provider-azure.exe 11 | COPY --from=core /Windows/System32/netapi32.dll /Windows/System32/netapi32.dll 12 | USER ContainerAdministrator 13 | 14 | ENTRYPOINT ["/secrets-store-csi-driver-provider-azure.exe"] 15 | --------------------------------------------------------------------------------