├── .all-contributorsrc ├── .aqua ├── aqua-policy.yaml ├── aqua.yaml └── registry.yaml ├── .changes ├── header.tpl.md ├── unreleased │ ├── .gitkeep │ └── new-product-feature-20250220-181256.yaml ├── v1.0.1.md ├── v1.1.0.md ├── v1.1.1.md ├── v1.1.2.md ├── v1.1.3.md ├── v1.1.4.md ├── v1.1.5.md ├── v1.1.6.md ├── v1.2.0.md ├── v1.2.1.md ├── v1.2.2.md ├── v1.2.3.md └── v1.2.4.md ├── .changie.yaml ├── .devcontainer ├── Dockerfile ├── devcontainer.json ├── files │ ├── .zshrc │ └── first-run-notice.txt ├── init └── init.bat ├── .editorconfig ├── .envrc ├── .gitattributes ├── .github ├── CODEOWNERS ├── auto-assign.yml └── workflows │ ├── assign.yml │ ├── changie-trigger-release.yml │ ├── conventional-pr.yml │ ├── lint.yml │ ├── release-composite.yml │ ├── release.yml │ ├── scan.yml │ ├── stale.yaml │ └── test.yml ├── .gitignore ├── .gitleaks.toml ├── .golangci.yml ├── .goreleaser.yaml ├── .hadolint.yaml ├── .idea ├── .gitignore ├── dsv-k8s.iml ├── inspectionProfiles │ └── Project_Default.xml ├── markdown.xml ├── modules.xml └── vcs.xml ├── .markdownlint.yaml ├── .npmrc ├── .pre-commit-config.yaml ├── .prettierrc.yaml ├── .shellcheckrc ├── .snyk ├── .trunk ├── .gitignore └── trunk.yaml ├── .vscode ├── settings.json └── tasks.json ├── .whitesource ├── .yamllint.yaml ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── Makefile ├── Makefile.helpers ├── README.md ├── Tiltfile ├── charts ├── dsv-injector │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── configmap.yaml │ │ ├── credentials-secret.yaml │ │ ├── deployment.yaml │ │ ├── service.yaml │ │ └── webhook.yaml │ └── values.yaml └── dsv-syncer │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── cluster-role-binding.yaml │ ├── cluster-role.yaml │ ├── configmap.yaml │ ├── serviceaccount.yaml │ └── syncer-cronjob.yaml │ └── values.yaml ├── cmd ├── injector │ └── main.go └── syncer │ └── main.go ├── codecov.yml ├── docker ├── Dockerfile.chainguard ├── Dockerfile.distroless └── Dockerfile.scratch ├── docs ├── assets │ ├── info-markup-default-creds.svg │ ├── random-dont-need-to-install.svg │ └── warning-app1-required-for-tests.svg ├── configure.md ├── devcontainer.md ├── developer-debugging.md ├── developer-reference.md ├── helm-install.md ├── local-cli-invoke.md ├── local-kubernetes.md ├── local-testing.md ├── release.md ├── setup-developer.md ├── setup-project.md ├── troubleshooting.md └── using.md ├── examples ├── add-to-secret.yml ├── set-secret.yml └── update-secret.yml ├── go.mod ├── go.sum ├── internal ├── k8s │ └── k8s.go ├── logger │ └── logger.go └── test │ └── testing.go ├── magefiles ├── constants │ ├── constants.mage.go │ └── variables.mage.go ├── dev-cli-tools.go ├── goreleaser.mage.go ├── helm │ └── helm.mage.go ├── install.mage.go ├── jobs.mage.go ├── k8s │ └── k8s.mage.go ├── kind-3-nodes.yaml ├── kind │ └── kind.mage.go ├── mage.go ├── magefile.go ├── minikube │ └── minikube.mage.go └── vault │ └── vault.mage.go ├── pkg ├── config │ ├── credentials.go │ └── credentials_test.go ├── injector │ └── inject.go ├── patch │ ├── patch.go │ └── patch_test.go └── syncer │ └── sync.go └── renovate.json /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "commitConvention": "angular", 8 | "contributors": [ 9 | { 10 | "login": "amigus", 11 | "name": "Adam C. Migus", 12 | "avatar_url": "https://avatars.githubusercontent.com/u/119477?v=4", 13 | "profile": "https://mig.us/adam", 14 | "contributions": [ 15 | "code", 16 | "doc", 17 | "test" 18 | ] 19 | }, 20 | { 21 | "login": "hansboder", 22 | "name": "Hans Boder", 23 | "avatar_url": "https://avatars.githubusercontent.com/u/36736535?v=4", 24 | "profile": "https://github.com/hansboder", 25 | "contributions": [ 26 | "bug" 27 | }, 28 | { 29 | "login": "sheldonhull", 30 | "name": "sheldonhull", 31 | "avatar_url": "https://avatars.githubusercontent.com/u/3526320?v=4", 32 | "profile": "https://www.sheldonhull.com", 33 | "contributions": [ 34 | "code", 35 | "doc", 36 | "test" 37 | ] 38 | }, 39 | { 40 | "login": "tylerezimmerman", 41 | "name": "tylerezimmerman", 42 | "avatar_url": "https://avatars.githubusercontent.com/u/100804646?v=4", 43 | "profile": "https://github.com/tylerezimmerman", 44 | "contributions": [ 45 | "maintenance" 46 | ] 47 | }, 48 | { 49 | "login": "delineaKrehl", 50 | "name": "Tim Krehl", 51 | "avatar_url": "https://avatars.githubusercontent.com/u/105234788?v=4", 52 | "profile": "https://github.com/delineaKrehl", 53 | "contributions": [ 54 | "maintenance" 55 | ] 56 | }, 57 | { 58 | "login": "EndlessTrax", 59 | "name": "Ricky White", 60 | "avatar_url": "https://avatars.githubusercontent.com/u/17141891?v=4", 61 | "profile": "http://endlesstrax.com", 62 | "contributions": [ 63 | "maintenance" 64 | ] 65 | }, 66 | { 67 | "login": "forced-request", 68 | "name": "John Poulin", 69 | "avatar_url": "https://avatars.githubusercontent.com/u/961246?v=4", 70 | "profile": "https://github.com/forced-request", 71 | "contributions": [ 72 | "maintenance" 73 | ] 74 | } 75 | ], 76 | "contributorsPerLine": 7, 77 | "skipCi": true, 78 | "repoType": "github", 79 | "repoHost": "https://github.com", 80 | "projectName": "dsv-k8s", 81 | "projectOwner": "DelineaXPM" 82 | } 83 | -------------------------------------------------------------------------------- /.aqua/aqua-policy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # aqua Policy 3 | # https://aquaproj.github.io/ 4 | registries: 5 | - type: standard 6 | ref: semver(">= 3.0.0") 7 | - name: local 8 | type: local 9 | path: registry.yaml 10 | packages: 11 | - registry: standard 12 | - registry: local 13 | -------------------------------------------------------------------------------- /.aqua/aqua.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # aqua - Declarative CLI Version Manager 3 | # https://aquaproj.github.io/ 4 | checksum: 5 | enabled: false 6 | require_checksum: false 7 | registries: 8 | - type: standard 9 | ref: v4.212.0 # renovate: depName=aquaproj/aqua-registry 10 | - name: local 11 | type: local 12 | path: registry.yaml 13 | packages: 14 | - name: miniscruff/changie@v1.19.1 15 | tags: ['release'] 16 | - name: golang/go@go1.22.6 17 | tags: ['first', 'release', 'test', 'scan', 'lint'] 18 | - name: direnv/direnv@v2.34.0 19 | - name: magefile/mage@v1.15.0 20 | tags: ['release', 'test', 'scan', 'lint'] 21 | - name: charmbracelet/glow@v1.5.1 22 | - name: goreleaser/goreleaser@v2.1.0 23 | tags: ['release'] 24 | - name: mvdan/gofumpt@v0.6.0 25 | - name: anchore/syft@v1.11.0 26 | tags: ['release'] 27 | - name: norwoodj/helm-docs@v1.14.2 28 | - name: gotestyourself/gotestsum@v1.12.0 29 | tags: ['test'] 30 | - name: c-bata/kube-prompt@v1.0.11 31 | - name: kubernetes-sigs/kind@v0.23.0 32 | - name: kubernetes/kubectl 33 | version: v1.25.2 34 | - name: helm/helm@v3.15.3 35 | - name: kubernetes/minikube@v1.33.1 36 | tags: ['ci'] 37 | - name: stern/stern@v1.30.0 38 | - name: tilt-dev/tilt@v0.33.19 39 | - name: golangci/golangci-lint@v1.59.1 40 | tags: ['lint'] 41 | - name: mage-select 42 | version: v1.4.2 43 | registry: local 44 | tags: ['goinstall'] 45 | - name: DelineaXPM/dsv-cli@v1.41.1 46 | - name: gitleaks/gitleaks@v8.18.4 47 | - name: charmbracelet/gum@v0.14.3 48 | - name: cli/cli@v2.58.0 49 | tags: 50 | - release 51 | - name: miniscruff/changie@v1.21.0 52 | - name: mikefarah/yq@v4.44.3 53 | tags: 54 | - release 55 | -------------------------------------------------------------------------------- /.aqua/registry.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | packages: 3 | - type: go_install 4 | name: mage-select 5 | path: github.com/iwittkau/mage-select 6 | link: github.com/iwittkau/mage-select 7 | description: CLI frontend for mage based on promptui. 8 | search_words: 9 | - mage 10 | - module 11 | - go 12 | - prompt 13 | -------------------------------------------------------------------------------- /.changes/header.tpl.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), 7 | and is generated by [Changie](https://github.com/miniscruff/changie). 8 | -------------------------------------------------------------------------------- /.changes/unreleased/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DelineaXPM/dsv-k8s/86bfd49799fe18bd5745d195a52c525926e5857a/.changes/unreleased/.gitkeep -------------------------------------------------------------------------------- /.changes/unreleased/new-product-feature-20250220-181256.yaml: -------------------------------------------------------------------------------- 1 | kind: new-product-feature 2 | body: Added documentation to utilize Azure Entra authentication 3 | time: 2025-02-20T18:12:56.436354904Z 4 | -------------------------------------------------------------------------------- /.changes/v1.1.0.md: -------------------------------------------------------------------------------- 1 | ## [v1.1.0] (2022-10-10) 2 | 3 | ### Added 4 | 5 | - feat: Configure Mend for GitHub.com (#26) (2022-08-26) 6 | 7 | ### Fixed 8 | 9 | - fix(mend): 🐛 remove trailing space in json (2022-08-26) 10 | - fix(tests): 🧪 resolve failing test cases and improve local testing automation (#7) (2022-07-27) 11 | 12 | ### Others 13 | 14 | - chore(deps): update trunk-io/trunk-action digest to 22e948f (#59) (2022-10-07) 15 | - chore(deps): update actions/stale digest to 5ebf00e (#58) (2022-10-07) 16 | - chore: add trunk config checks as linting tool (#40) (2022-10-07) 17 | - chore(deps): update amannn/action-semantic-pull-request digest to 505e44b (#46) (2022-09-28) 18 | - docs: add forced-request as a contributor for maintenance (#53) (2022-09-28) 19 | - docs: add EndlessTrax as a contributor for maintenance (#52) (2022-09-28) 20 | - docs: add delineaKrehl as a contributor for maintenance (#51) (2022-09-27) 21 | - docs: add tylerezimmerman as a contributor for maintenance (#50) (2022-09-27) 22 | - docs: add sheldonhull as a contributor for code, doc, and test (#48) (2022-09-27) 23 | - docs: add hansboder as a contributor for bug (#54) (2022-09-27) 24 | - docs: add amigus as a contributor for code, doc, and test (#49) (2022-09-27) 25 | - chore(deps): update actions/stale action to v6 (#43) (2022-09-27) 26 | - chore(deps): update actions/stale digest to 9c1b1c6 (#38) (2022-09-01) 27 | - chore(vscode): add missing linting settings (2022-09-01) 28 | - chore: align github workflows and tooling (#37) (2022-09-01) 29 | - ci(action/docker): 🔨 eliminate github event name push from skipping workflow dispatch (2022-08-31) 30 | - chore(deps): update kentaro-m/auto-assign-action action to v1.2.3 (#35) (2022-08-31) 31 | - chore(deps): update kentaro-m/auto-assign-action action to v1.2.2 (#34) (2022-08-29) 32 | - chore(deps): update kubernetes packages to v0.25.0 (#25) (2022-08-26) 33 | - ci(mend): disable issues for mend but still allow pull and renovate execution (2022-08-26) 34 | - chore(deps): update github.com/mattbaird/jsonpatch digest to 098863c (#13) (2022-08-23) 35 | - chore: update maintainers (#24) (2022-08-23) 36 | - chore(devcontainer): 🧰 improvements to experience with setup directions, prebuilt tooling, and reliability (#23) (2022-08-20) 37 | - chore(deps): update kubernetes packages to v0.24.4 (#22) (2022-08-18) 38 | - ci(tests): fix name of tasks (2022-08-05) 39 | - chore(deps): update ⬆️ golang module go to 1.19 (#17) (2022-08-05) 40 | - ci(tests): remove push filter condition (2022-08-05) 41 | - ci(tests): use mage to run tests (2022-08-05) 42 | - ci(tests): 🔨 adjust test to use gotestsum (2022-08-05) 43 | - ci(tests): ➕ require tests to be run as part of pull request (2022-08-05) 44 | - ci: 🤖 add workflow dispatch to docker building actions (#21) (2022-08-03) 45 | - ci(docker): set registry as org secret (#20) (2022-08-01) 46 | - chore: use new env vars for docker hub publishing (#19) (2022-08-01) 47 | - chore(deps): update ⬆️ golang module github.com/pterm/pterm to v0.12.45 (#16) [skip ci] (2022-07-29) 48 | - chore(deps): update ⬆️ golang module github.com/mittwald/go-helm-client to v0.11.3 (#14) [skip ci] (2022-07-28) 49 | - Configure Renovate AB#449139 (#11) (2022-07-28) 50 | - chore(codeowner): assign default codeowners (#12) (2022-07-27) 51 | - chore(helm): ⬆️ bump chart patch version due to minor changes (#8) (2022-07-27) 52 | - Merge pull request #6 from The-Migus-Group/fix-meta (2022-07-12) 53 | - Merge pull request #5 from DelineaXPM/fix-docker (2022-06-28) 54 | - Merge pull request #4 from DelineaXPM/fix-3 (2022-06-28) 55 | - Merge pull request #1 from DelineaXPM/delineaKrehl-DeepRebrand (2022-06-02) 56 | - Fix for #13 that improves injector error handling. (#14) (2022-05-20) 57 | - Add Testing 📛 (2022-05-06) 58 | - Run go test ./... (2022-05-06) 59 | - Shorten the name of the github action (2022-05-06) 60 | - Fix build badges. 📛 (2022-05-06) 61 | - Secret Synchronization Mechanism #11 (#12) (2022-05-06) 62 | - Merge pull request #10 from thycotic/chart-upgrade-fixes (2022-03-22) 63 | - Eval role logic after patchMode test; Fixes #8 (#9) (2022-03-10) 64 | - Comments + all = install-image (2022-03-01) 65 | - Publish to Minikube by default. 🪄 (2022-03-01) 66 | -------------------------------------------------------------------------------- /.changes/v1.1.1.md: -------------------------------------------------------------------------------- 1 | ## [v1.1.1] (2022-10-10) 2 | 3 | ### Added 4 | 5 | - feat: Configure Mend for GitHub.com (#26) (2022-08-26) 6 | 7 | ### Fixed 8 | 9 | - fix(mend): 🐛 remove trailing space in json (2022-08-26) 10 | - fix(tests): 🧪 resolve failing test cases and improve local testing automation (#7) (2022-07-27) 11 | 12 | ### Others 13 | 14 | - chore(deps): update trunk-io/trunk-action digest to 22e948f (#59) (2022-10-07) 15 | - chore(deps): update actions/stale digest to 5ebf00e (#58) (2022-10-07) 16 | - chore: add trunk config checks as linting tool (#40) (2022-10-07) 17 | - chore(deps): update amannn/action-semantic-pull-request digest to 505e44b (#46) (2022-09-28) 18 | - docs: add forced-request as a contributor for maintenance (#53) (2022-09-28) 19 | - docs: add EndlessTrax as a contributor for maintenance (#52) (2022-09-28) 20 | - docs: add delineaKrehl as a contributor for maintenance (#51) (2022-09-27) 21 | - docs: add tylerezimmerman as a contributor for maintenance (#50) (2022-09-27) 22 | - docs: add sheldonhull as a contributor for code, doc, and test (#48) (2022-09-27) 23 | - docs: add hansboder as a contributor for bug (#54) (2022-09-27) 24 | - docs: add amigus as a contributor for code, doc, and test (#49) (2022-09-27) 25 | - chore(deps): update actions/stale action to v6 (#43) (2022-09-27) 26 | - chore(deps): update actions/stale digest to 9c1b1c6 (#38) (2022-09-01) 27 | - chore(vscode): add missing linting settings (2022-09-01) 28 | - chore: align github workflows and tooling (#37) (2022-09-01) 29 | - ci(action/docker): 🔨 eliminate github event name push from skipping workflow dispatch (2022-08-31) 30 | - chore(deps): update kentaro-m/auto-assign-action action to v1.2.3 (#35) (2022-08-31) 31 | - chore(deps): update kentaro-m/auto-assign-action action to v1.2.2 (#34) (2022-08-29) 32 | - chore(deps): update kubernetes packages to v0.25.0 (#25) (2022-08-26) 33 | - ci(mend): disable issues for mend but still allow pull and renovate execution (2022-08-26) 34 | - chore(deps): update github.com/mattbaird/jsonpatch digest to 098863c (#13) (2022-08-23) 35 | - chore: update maintainers (#24) (2022-08-23) 36 | - chore(devcontainer): 🧰 improvements to experience with setup directions, prebuilt tooling, and reliability (#23) (2022-08-20) 37 | - chore(deps): update kubernetes packages to v0.24.4 (#22) (2022-08-18) 38 | - ci(tests): fix name of tasks (2022-08-05) 39 | - chore(deps): update ⬆️ golang module go to 1.19 (#17) (2022-08-05) 40 | - ci(tests): remove push filter condition (2022-08-05) 41 | - ci(tests): use mage to run tests (2022-08-05) 42 | - ci(tests): 🔨 adjust test to use gotestsum (2022-08-05) 43 | - ci(tests): ➕ require tests to be run as part of pull request (2022-08-05) 44 | - ci: 🤖 add workflow dispatch to docker building actions (#21) (2022-08-03) 45 | - ci(docker): set registry as org secret (#20) (2022-08-01) 46 | - chore: use new env vars for docker hub publishing (#19) (2022-08-01) 47 | - chore(deps): update ⬆️ golang module github.com/pterm/pterm to v0.12.45 (#16) [skip ci] (2022-07-29) 48 | - chore(deps): update ⬆️ golang module github.com/mittwald/go-helm-client to v0.11.3 (#14) [skip ci] (2022-07-28) 49 | - Configure Renovate AB#449139 (#11) (2022-07-28) 50 | - chore(codeowner): assign default codeowners (#12) (2022-07-27) 51 | - chore(helm): ⬆️ bump chart patch version due to minor changes (#8) (2022-07-27) 52 | - Merge pull request #6 from The-Migus-Group/fix-meta (2022-07-12) 53 | - Merge pull request #5 from DelineaXPM/fix-docker (2022-06-28) 54 | - Merge pull request #4 from DelineaXPM/fix-3 (2022-06-28) 55 | - Merge pull request #1 from DelineaXPM/delineaKrehl-DeepRebrand (2022-06-02) 56 | - Fix for #13 that improves injector error handling. (#14) (2022-05-20) 57 | - Add Testing 📛 (2022-05-06) 58 | - Run go test ./... (2022-05-06) 59 | - Shorten the name of the github action (2022-05-06) 60 | - Fix build badges. 📛 (2022-05-06) 61 | - Secret Synchronization Mechanism #11 (#12) (2022-05-06) 62 | - Merge pull request #10 from thycotic/chart-upgrade-fixes (2022-03-22) 63 | - Eval role logic after patchMode test; Fixes #8 (#9) (2022-03-10) 64 | -------------------------------------------------------------------------------- /.changes/v1.1.2.md: -------------------------------------------------------------------------------- 1 | ## [v1.1.2] (2022-10-10) 2 | 3 | ### Added 4 | 5 | - feat: Configure Mend for GitHub.com (#26) (2022-08-26) 6 | 7 | ### Fixed 8 | 9 | - fix(mend): 🐛 remove trailing space in json (2022-08-26) 10 | - fix(tests): 🧪 resolve failing test cases and improve local testing automation (#7) (2022-07-27) 11 | 12 | ### Others 13 | 14 | - chore(deps): update trunk-io/trunk-action digest to 22e948f (#59) (2022-10-07) 15 | - chore(deps): update actions/stale digest to 5ebf00e (#58) (2022-10-07) 16 | - chore: add trunk config checks as linting tool (#40) (2022-10-07) 17 | - chore(deps): update amannn/action-semantic-pull-request digest to 505e44b (#46) (2022-09-28) 18 | - docs: add forced-request as a contributor for maintenance (#53) (2022-09-28) 19 | - docs: add EndlessTrax as a contributor for maintenance (#52) (2022-09-28) 20 | - docs: add delineaKrehl as a contributor for maintenance (#51) (2022-09-27) 21 | - docs: add tylerezimmerman as a contributor for maintenance (#50) (2022-09-27) 22 | - docs: add sheldonhull as a contributor for code, doc, and test (#48) (2022-09-27) 23 | - docs: add hansboder as a contributor for bug (#54) (2022-09-27) 24 | - docs: add amigus as a contributor for code, doc, and test (#49) (2022-09-27) 25 | - chore(deps): update actions/stale action to v6 (#43) (2022-09-27) 26 | - chore(deps): update actions/stale digest to 9c1b1c6 (#38) (2022-09-01) 27 | - chore(vscode): add missing linting settings (2022-09-01) 28 | - chore: align github workflows and tooling (#37) (2022-09-01) 29 | - ci(action/docker): 🔨 eliminate github event name push from skipping workflow dispatch (2022-08-31) 30 | - chore(deps): update kentaro-m/auto-assign-action action to v1.2.3 (#35) (2022-08-31) 31 | - chore(deps): update kentaro-m/auto-assign-action action to v1.2.2 (#34) (2022-08-29) 32 | - chore(deps): update kubernetes packages to v0.25.0 (#25) (2022-08-26) 33 | - ci(mend): disable issues for mend but still allow pull and renovate execution (2022-08-26) 34 | - chore(deps): update github.com/mattbaird/jsonpatch digest to 098863c (#13) (2022-08-23) 35 | - chore: update maintainers (#24) (2022-08-23) 36 | - chore(devcontainer): 🧰 improvements to experience with setup directions, prebuilt tooling, and reliability (#23) (2022-08-20) 37 | - chore(deps): update kubernetes packages to v0.24.4 (#22) (2022-08-18) 38 | - ci(tests): fix name of tasks (2022-08-05) 39 | - chore(deps): update ⬆️ golang module go to 1.19 (#17) (2022-08-05) 40 | - ci(tests): remove push filter condition (2022-08-05) 41 | - ci(tests): use mage to run tests (2022-08-05) 42 | - ci(tests): 🔨 adjust test to use gotestsum (2022-08-05) 43 | - ci(tests): ➕ require tests to be run as part of pull request (2022-08-05) 44 | - ci: 🤖 add workflow dispatch to docker building actions (#21) (2022-08-03) 45 | - ci(docker): set registry as org secret (#20) (2022-08-01) 46 | - chore: use new env vars for docker hub publishing (#19) (2022-08-01) 47 | - chore(deps): update ⬆️ golang module github.com/pterm/pterm to v0.12.45 (#16) [skip ci] (2022-07-29) 48 | - chore(deps): update ⬆️ golang module github.com/mittwald/go-helm-client to v0.11.3 (#14) [skip ci] (2022-07-28) 49 | - Configure Renovate AB#449139 (#11) (2022-07-28) 50 | - chore(codeowner): assign default codeowners (#12) (2022-07-27) 51 | - chore(helm): ⬆️ bump chart patch version due to minor changes (#8) (2022-07-27) 52 | - Merge pull request #6 from The-Migus-Group/fix-meta (2022-07-12) 53 | - Merge pull request #5 from DelineaXPM/fix-docker (2022-06-28) 54 | - Merge pull request #4 from DelineaXPM/fix-3 (2022-06-28) 55 | - Merge pull request #1 from DelineaXPM/delineaKrehl-DeepRebrand (2022-06-02) 56 | - Fix for #13 that improves injector error handling. (#14) (2022-05-20) 57 | ] 58 | -------------------------------------------------------------------------------- /.changes/v1.1.3.md: -------------------------------------------------------------------------------- 1 | ## v1.1.3 - 2022-10-10 2 | 3 | ### Added 4 | 5 | - Changelog generation triggers docker release instead of every commit 6 | 7 | ### Fixed 8 | 9 | - Resolve dockerhub publishing. 10 | -------------------------------------------------------------------------------- /.changes/v1.1.4.md: -------------------------------------------------------------------------------- 1 | ## v1.1.4 - 2022-10-11 2 | 3 | ### Security 4 | 5 | Update kubernetes package dependencies. 6 | -------------------------------------------------------------------------------- /.changes/v1.1.5.md: -------------------------------------------------------------------------------- 1 | ## v1.1.5 - 2023-01-24 2 | 3 | ### 🔨 Refactor 4 | 5 | - Point the helm charts towards docker hub based images, instead of quay, as these are now iterated on with changelog driven release instead of each commit. 6 | This should reduce frequency of needless version updates. 7 | 8 | ### 🐛 Bug Fix 9 | 10 | - Docker Hub published images did not have the correct path to the injector and syncer, resulting in an invalid entrypoint. 11 | This is fixed and should now correctly resolve when using the updated helm charts that provide a qualified path. 12 | For example: `/app/dsv-injector` instead of just saying `dsv-injector` now. 13 | This is due to using a minimal distroless image and not copying binaries into a path that is assumed to be resolved automatically by `PATH`, such as `/usr/local/bin`. 14 | Now the path to the binary is explicitly set and should resolve any path resolution issues. 15 | 16 | ### 🤖 Development 17 | 18 | - Bump aqua tooling and include dsv-cli in the project setup. 19 | - Included `CGO_ENABLED=0` to avoid issues with running commands in devcontainers & codespaces. 20 | - Improved mage tasks to support minikube as default to see if this helps with Codespace timeouts being experienced. 21 | - Bumped the docker feature kit to 2.0 as well to attempt to resolve timeouts in devcontainer/codespaces. 22 | 23 | ### Related 24 | 25 | - fixes AB#483421 26 | - [Issue 83 Fixed](https://github.com/DelineaXPM/dsv-k8s/issues/83). 27 | Thank you @JulianPedro for helping identify this and opening the descriptive issue. 👍 28 | 29 | ### Contributors 30 | 31 | - [sheldonhull](https://github.com/sheldonhull) 32 | -------------------------------------------------------------------------------- /.changes/v1.1.6.md: -------------------------------------------------------------------------------- 1 | ## v1.1.6 - 2023-02-06 2 | 3 | ### 🤖 CI 4 | 5 | - Adjust Quay registry to be a target publishing platform instead of building on every single commit to main. 6 | This aligns Quay registry with the more controlled semver versioning in docker hub instead of publishing under latest only. 7 | 8 | ### Related 9 | 10 | - fixed AB#485565 11 | 12 | ### Contributors 13 | 14 | - [sheldonhull](https://github.com/sheldonhull) 15 | -------------------------------------------------------------------------------- /.changes/v1.2.0.md: -------------------------------------------------------------------------------- 1 | ## v1.2.0 - 2023-04-27 2 | 3 | ### 🤖 CI & Build 4 | 5 | - Improve mage tasks for minikube, including list images, remove/load images. 6 | - Update aqua to the latest version. 7 | - Include aqua configuration tags for Github actions. 8 | - Pin the version examples in the chart install and yaml values file as a better practice for production chart usage. 9 | - Chart versions no longer are independently versioned. 10 | Instead the docker image, syncing chart, and injector chart are all aligned with the same version number. 11 | This is automatically kept to the correct version when running `changie merge`. 12 | 13 | ### 🔨 Refactor 14 | 15 | - Improve output to structured json logs. 16 | - Change invocation to use environment variable configuration. 17 | - Allow users to provide config maps dynamically in Values.yaml. 18 | - Note the current version, commit sha, and date of the currently used binary at startup. 19 | 20 | ### 🔒 Security 21 | 22 | - Improve the base image to use Chainguard's static nonroot image. 23 | - Ensure default timeout is set for `ReadHeaderTimeout`. 24 | 25 | ### 🤖 Development 26 | 27 | - Improve the local development experience with Tilt. 28 | - Improve documentation and include stern/kubectl log to file snippets. 29 | - Add local development checks for values files. 30 | This will catch incorrectly configured values that are difficult to troubleshoot in local development workflows. 31 | - Add missing `--overwrite` tag to `mage minikube:loadimages` task. -------------------------------------------------------------------------------- /.changes/v1.2.1.md: -------------------------------------------------------------------------------- 1 | ## v1.2.1 - 2023-09-05 2 | 3 | ### 📘 Documentation 4 | 5 | - Include detail on providing `tld` in the configuration, allowing `eu` and other TLDs to be used. 6 | - Mention `tilt up` in the initial setup config as viable option. 7 | 8 | ### 🤖 CI & Build 9 | 10 | - Improve mage tasks with secret setup and tear down for better development support and troubleshooting. 11 | - Bump go version in release pipeline to use `1.21` as can include standard library security improvements. 12 | - Remove failing error condition on `mage job:rebuild` to better allow default setup without running local builds, such as just using the published docker image. 13 | This supports easier demo/test usage by support. 14 | 15 | ### 🔨 Refactor 16 | 17 | - Improve logging with error wrapping and remove deprecated Go `ioutil` usage. 18 | 19 | ### ⬆️ Dependencies 20 | 21 | - Bump tooling such as changie, release, trunk, more security scanners. 22 | - Other dependency bumps such as `golang.org/x/net`. 23 | -------------------------------------------------------------------------------- /.changes/v1.2.2.md: -------------------------------------------------------------------------------- 1 | ## v1.2.2 - 2024-01-15 2 | 3 | 4 | ### ⬆️ Dependencies 5 | 6 | - Update dependent libraries and go version. No user facing changes, just continued maintenance for improved security & stability. -------------------------------------------------------------------------------- /.changes/v1.2.3.md: -------------------------------------------------------------------------------- 1 | ## v1.2.3 - 2024-08-12 2 | 3 | 4 | ### 🤖 CI & Build 5 | 6 | - Add a buildName metadata to binary so easy to see if caching issue with container loading. Handle `dev.local/dsv-k8s` as standard image name to better reflect standard approach I've been using. Improve validation checks. Goreleaser upgrade schema and more. Lots of quality of life improvements for dev, and aqua updates. 7 | 8 | ### 🔨 Refactor 9 | 10 | - Improve `values.yml` for the dsv-injector to expose the days till expiration of the self signed cert. 11 | Include minor doc improvements to this as well to better handle. -------------------------------------------------------------------------------- /.changes/v1.2.4.md: -------------------------------------------------------------------------------- 1 | ## v1.2.4 - 2024-10-02 2 | 3 | 4 | ### 🤖 CI & Build 5 | 6 | - Improve mage tasks and linting with fixes. Less issues with err output on cleanup now, and also load the docker version of images into minikube setup proactively. 7 | 8 | ### ⬆️ Dependencies 9 | 10 | - Maintenance release due to updated dependencies. -------------------------------------------------------------------------------- /.changie.yaml: -------------------------------------------------------------------------------- 1 | changesDir: .changes 2 | unreleasedDir: unreleased 3 | headerPath: header.tpl.md 4 | changelogPath: CHANGELOG.md 5 | versionExt: md 6 | versionFormat: '## {{.Version}} - {{.Time.Format "2006-01-02"}}' 7 | kindFormat: '' 8 | changeFormat: '- _{{ .KindLabel }}_: {{ .Body -}}' 9 | body: 10 | block: true 11 | kinds: 12 | - label: 🤖 CI & Build 13 | auto: patch 14 | key: ci-build 15 | - label: 🎉 New Product Feature 16 | auto: minor 17 | key: new-product-feature 18 | - label: ⬇️ Deprecated 19 | auto: minor 20 | key: deprecated 21 | - label: 🧪 Tests 22 | auto: patch 23 | key: tests 24 | - label: 🔨 Refactor 25 | auto: patch 26 | key: refactor 27 | - label: 🐛 Bug Fix 28 | auto: patch 29 | key: bug-fix 30 | - label: 🔥 Breaking Change 31 | auto: minor 32 | key: breaking-change 33 | - label: 🔒 Security 34 | auto: patch 35 | key: security 36 | - label: ⬆️ Dependencies 37 | auto: patch 38 | key: dependencies 39 | - label: 🔥 Major Version Change (Breaking Changes) 40 | auto: major 41 | key: major-version-change 42 | newlines: 43 | afterChange: 0 44 | afterChangelogHeader: 1 45 | afterChangelogVersion: 1 46 | afterComponent: 1 47 | afterFooterFile: 1 48 | afterFooter: 1 49 | afterHeaderFile: 1 50 | afterHeaderTemplate: 1 51 | afterKind: 1 52 | afterVersion: 1 53 | 54 | beforeChange: 0 55 | beforeChangelogVersion: 0 56 | beforeComponent: 0 57 | beforeFooterFile: 0 58 | beforeFooterTemplate: 0 59 | beforeHeaderFile: 0 60 | beforeHeaderTemplate: 0 61 | beforeKind: 1 62 | beforeVersion: 0 63 | endOfVersion: 0 64 | 65 | replacements: 66 | # chart versions align with the release and get bumped by changie for us on `changie merge`. 67 | - path: 'charts/dsv-injector/Chart.yaml' 68 | find: 'version: .*' 69 | replace: 'version: {{.Version}}' 70 | - path: 'charts/dsv-syncer/Chart.yaml' 71 | find: 'version: .*' 72 | replace: 'version: {{.Version}}' 73 | # use pinned versions in the chart install examples as a best practice 74 | - path: 'charts/dsv-injector/Chart.yaml' 75 | find: "IMAGE_TAG='.*'" 76 | replace: "IMAGE_TAG='{{.Version}}'" 77 | - path: 'charts/dsv-syncer/Chart.yaml' 78 | find: "IMAGE_TAG='.*'" 79 | replace: "IMAGE_TAG='{{.Version}}'" 80 | # use pinned versions in the chart default values as a best practice 81 | - path: 'charts/dsv-injector/values.yaml' 82 | find: 'tag: .*' 83 | replace: 'tag: {{.Version}}' 84 | - path: 'charts/dsv-syncer/values.yaml' 85 | find: 'tag: .*' 86 | replace: 'tag: {{.Version}}' 87 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/base:bullseye 2 | # BUILD ARGS FROM DEVCONTAINER JSON 3 | ARG DEVCONTAINER_USER 4 | ARG GOPATH 5 | 6 | USER root 7 | ENV DOCKER_BUILDKIT=1 8 | ENV GOPATH=$GOPATH 9 | # to avoid gcc compile issues as don't need gcc except for race conditions testing 10 | ENV CGO_ENABLED=0 11 | ENV MAGEFILE_ENABLE_COLOR=1 12 | ENV TRUNK_LAUNCHER_QUIET=true 13 | ENV PATH="$GOPATH/bin:/home/$DEVCONTAINER_USER/.local/share/aquaproj-aqua/bin:/home/$DEVCONTAINER_USER/go/bin:$PATH" 14 | # ENV PATH="${GOPATH}/bin:${PATH}" --> this uses HOST path , use the $PATH variable instead 15 | USER $DEVCONTAINER_USER 16 | COPY files/.zshrc /home/$DEVCONTAINER_USER/.zshrc 17 | COPY files/first-run-notice.txt /home/$DEVCONTAINER_USER/first-run-notice.txt 18 | 19 | RUN mkdir -p /home/$DEVCONTAINER_USER/.minikube \ 20 | && sudo chown $DEVCONTAINER_USER /home/$DEVCONTAINER_USER/.minikube \ 21 | && mkdir -p /home/$DEVCONTAINER_USER/go \ 22 | && sudo chown $DEVCONTAINER_USER /home/$DEVCONTAINER_USER/go \ 23 | && /bin/bash -c 'set -euo pipefail && curl https://get.trunk.io -fsSL | bash -s -- -y' \ 24 | && /bin/bash -c 'set -euo pipefail && curl -sSfL https://raw.githubusercontent.com/aquaproj/aqua-installer/v3.0.1/aqua-installer | bash' 25 | 26 | 27 | VOLUME [ "/var/lib/docker" ] 28 | CMD ["sleep", "infinity"] 29 | # ENTRYPOINT ["/usr/local/share/docker-init.sh"] 30 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.241.1/containers/kubernetes-helm-minikube 3 | { 4 | "name": "devcontainer", 5 | "dockerFile": "Dockerfile", 6 | "build": { 7 | "args": { 8 | "DEVCONTAINER_USER": "vscode", 9 | "GOPATH": "/home/vscode/go" 10 | } 11 | }, 12 | "runArgs": [ 13 | "--init", 14 | "--privileged" 15 | ], 16 | // "runArgs": [ 17 | // "--cap-add=SYS_PTRACE", 18 | // "--security-opt", 19 | // "seccomp=unconfined", 20 | // "--privileged", 21 | // "--init" 22 | // ], 23 | "mounts": [ 24 | "source=minikube-config,target=/home/vscode/.minikube,type=volume", 25 | "source=${localEnv:HOME}${localEnv:USERPROFILE}/.kube,target=/home/vscode/.kube/,type=bind,consistency=cached", // support for SSH keys 26 | "source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/vscode/.ssh/,type=bind,consistency=cached", // support for SSH keys 27 | "source=${localEnv:HOME}${localEnv:USERPROFILE}/.envrc,target=/home/vscode/.envrc,type=bind,consistency=cached", // envrc from home to allow direnv to mount credentials 28 | "source=${localEnv:HOME}${localEnv:USERPROFILE}/.thy,target=/home/vscode/.thy/,type=bind,consistency=cached", // support for dsv-cli filestore based store 29 | "source=${localEnv:HOME}${localEnv:USERPROFILE}/.dsv.yml,target=/home/vscode/.dsv.yml/,type=bind,consistency=cached", // mounting for dsv-config 30 | // cache gopath directory 31 | "source=go-path,target=/home/vscode/go/,type=volume" 32 | ], 33 | "overrideCommand": false, 34 | // Configure tool-specific properties. 35 | "customizations": { 36 | // Configure properties specific to VS Code. 37 | "vscode": { 38 | // Add the IDs of extensions you want installed when the container is created. 39 | "extensions": [ 40 | "trunk.io", 41 | "GitHub.vscode-pull-request-github", 42 | "yzhang.markdown-all-in-one", 43 | "sheldon-hull.extension-pack-go", 44 | "ms-azuretools.vscode-docker", 45 | "ms-kubernetes-tools.vscode-kubernetes-tools" 46 | ] 47 | } 48 | }, 49 | "settings": { 50 | "terminal.integrated.profiles.linux": { 51 | "zsh-login": { 52 | "args": [ 53 | "-l" 54 | ], 55 | "icon": "terminal-bash", 56 | "path": "zsh" 57 | } 58 | }, 59 | "terminal.integrated.defaultProfile.linux": "zsh-login" 60 | }, 61 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 62 | // "forwardPorts": [], 63 | // Use 'postCreateCommand' to run commands after the container is created. 64 | // "postCreateCommand": "", 65 | // Use 'postStartCommand' to run commands after the container is created like starting minikube. 66 | "postStartCommand": "sudo chown -R vscode /home/vscode/go/ && export GOPATH=/home/vscode/go/ && echo '🔨 aqua tooling download' && aqua install && echo '✅ aqua install complete' && echo 67 | '🔨 running go mod download' && /home/vscode/.local/share/aquaproj-aqua/bin/go mod download && echo '✅ go mod download finished' && echo '🔨 downloading build tooling dependencies' && 68 | /home/vscode/.local/share/aquaproj-aqua/bin/mage -compile ./magec && echo '✅ build tooling dependencies complete'", 69 | // Minikube does not like running as root, so use a non-root user. 70 | "remoteUser": "vscode", 71 | "containerEnv": { 72 | "ENABLE_NONROOT_DOCKER": "true", 73 | "GITHUB_OATH_TOKEN": "${localEnv:GITHUB_OATH_TOKEN}", 74 | "CGO_ENABLED": "false" 75 | }, 76 | "remoteEnv": { 77 | "ENABLE_NONROOT_DOCKER": "true" 78 | }, 79 | "initializeCommand": [ 80 | ".devcontainer/init" 81 | ], 82 | "forwardPorts": [ 83 | 10350, 84 | 2222 85 | ], 86 | "portsAttributes": { 87 | "10350": { 88 | "label": "tilt", 89 | "onAutoForward": "openBrowserOnce", 90 | "protocol": "http", 91 | "requireLocalPort": false 92 | }, 93 | "2222": { 94 | "label": "remote-ssh-support", 95 | "onAutoForward": "silent" 96 | } 97 | }, 98 | "hostRequirements": { 99 | "cpus": 8, // running local kind cluster and toolchain tends to run into issues with 4, let's recommend 8 (though this requires an org membership by default). Local devcontainers should be fine. 100 | "memory": "8gb", 101 | "storage": "32gb" 102 | }, 103 | "features": { 104 | "ghcr.io/devcontainers/features/common-utils:1": { 105 | "version": "latest", 106 | "username": "vscode", 107 | "installOhMyZsh": true 108 | }, 109 | "ghcr.io/devcontainers/features/docker-in-docker:2": { 110 | "version": "latest", 111 | "dockerDashComposeVersion": "v2", 112 | "username": "vscode", 113 | "enableNonRootDocker": "false", 114 | "moby": "true" 115 | }, 116 | "ghcr.io/devcontainers/features/sshd:1": { 117 | "version": "latest" 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /.devcontainer/files/.zshrc: -------------------------------------------------------------------------------- 1 | #eval "$(direnv hook zsh)" 2 | export PATH="${AQUA_ROOT_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/aquaproj-aqua}/bin:$PATH" 3 | export PATH="$(go env GOPATH)/bin:${PATH}" 4 | export ZSH="$HOME/.oh-my-zsh" 5 | ZSH_THEME=avit 6 | plugins=(z direnv zsh-interactive-cd docker golang gh zsh-navigation-tools) 7 | source "$ZSH/oh-my-zsh.sh" 8 | 9 | if ! command -v direnv &>/dev/null; then 10 | echo "running aqua install since missing some tools like direnv" 11 | aqua install 12 | fi 13 | 14 | # Display optional first run image specific notice if configured and terminal is interactive 15 | # if [ -t 1 ] && [[ "${TERM_PROGRAM}" = "vscode" || "${TERM_PROGRAM}" = "vscodes" ]] && [ ! -f "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed" ]; then 16 | if [ -t 1 ] && [[ "${TERM_PROGRAM}" = "vscode" || "${TERM_PROGRAM}" = "vscodes" ]]; then 17 | if [ -f "$HOME/first-run-notice.txt" ]; then 18 | if command -v glow &>/dev/null; then 19 | glow "$HOME/first-run-notice.txt" 20 | else 21 | cat "$HOME/first-run-notice.txt" 22 | fi 23 | fi 24 | mkdir -p "$HOME/.config/vscode-dev-containers" || echo 'not able to create "$HOME/.config/vscode-dev-containers"' 25 | # Mark first run notice as displayed after 10s to avoid problems with fast terminal refreshes hiding it 26 | ((sleep 10s; touch "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed") &) 27 | fi 28 | -------------------------------------------------------------------------------- /.devcontainer/files/first-run-notice.txt: -------------------------------------------------------------------------------- 1 | # Welcome to this devcontainer 2 | 3 | > This bakes in Go, Kind, Docker, and more into a self-contained dev environment. 4 | 5 | > TIP: run `glow README.md` to get a nice formatted setup guide in your terminal. 6 | 7 | Common Starting Tasks: 8 | 9 | - `glow README.md` 10 | - `mage -l` 11 | - `mage -h init` 12 | - `mage job:setup` 13 | 14 | > WARNING: You'll want to make sure to run: `direnv allow` if you haven't to load all the correct environment variables. 15 | -------------------------------------------------------------------------------- /.devcontainer/init: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # This runs on the the host context 3 | touch "$HOME/.envrc" && echo "✅ $HOME/.envrc initialized" 4 | mkdir "$HOME/.ssh" || echo "✅ .ssh dir already exists" 5 | mkdir "$HOME/.kube" || echo "✅ .kube dir already exists" 6 | echo '✅ prep.sh setup complete' 7 | -------------------------------------------------------------------------------- /.devcontainer/init.bat: -------------------------------------------------------------------------------- 1 | powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "New-Item (Join-Path $ENV:USERPROFILE '.ssh') -ItemType Directory -EA 0; New-Item (Join-Path $ENV:USERPROFILE '.envrc') -ItemType File -EA 0; New-Item (Join-Path $ENV:USERPROFILE '.kube') -ItemType Directory -EA 0;" 2 | echo '✅ prep setup complete' -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | # Use 4 spaces for the Python files 13 | [*.py] 14 | indent_size = 4 15 | max_line_length = 80 16 | 17 | # The JSON files contain newlines inconsistently 18 | [*.json] 19 | insert_final_newline = ignore 20 | 21 | # Minified JavaScript files shouldn't be changed 22 | [**.min.js] 23 | indent_style = ignore 24 | insert_final_newline = ignore 25 | 26 | # Makefiles always use tabs for indentation 27 | [{Makefile, makefile, GNUmakefile, Makefile.*}] 28 | indent_style = tab 29 | 30 | # Batch files use tabs for indentation 31 | [*.bat] 32 | indent_style = tab 33 | 34 | [*.md] 35 | trim_trailing_whitespace = true 36 | 37 | [*.sh] 38 | indent_style = space 39 | indent_size = 4 40 | 41 | [*.{tf,tfvars,tpl}] 42 | indent_size = 2 43 | indent_style = space 44 | 45 | [*.{go,mod,sum}] 46 | indent_style = tab 47 | indent_size = 4 48 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | # export PATH="${AQUA_ROOT_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/aquaproj-aqua}/bin:$PATH" # for those using aqua this will ensure it's in the path with all tools if loading from home 2 | export DIRENV_WARN_TIMEOUT='10s' 3 | export DIRENV_LOG_FORMAT="" 4 | 5 | INFO_COLOR="\033[1;30;40m" 6 | RESET_COLOR="\033[0m" 7 | WARNING_COLOR="\033[33m" 8 | END_WARNING_COLOR="\033[0m" 9 | IMPORTANT_COLOR="\033[104;30m" 10 | WORKING_COLOR="\033[94m" 11 | BACKGROUND_GREEN="\033[94m" 12 | RESET_BACKGROUND="\033[0;49m" 13 | 14 | # variable for setting terminal output with blue background with black text 15 | BACKGROUND_BLUE="\033[44;30m" 16 | 17 | # variable for setting terminal output with light green background with black text and bold 18 | BACKGROUND_LIGHT_GREEN="\033[1;102;30m" 19 | 20 | # variable for setting terminal output with light yellow background and black text that is bold 21 | BACKGROUND_LIGHT_YELLOW="\033[1;103;30m" 22 | 23 | source_env "$HOME" 24 | source_env_if_exists ./env/.envrc 25 | export PATH="${GOPATH}/bin:${PATH}" 26 | 27 | # This is set for local kind/minikube config to avoid touching the main kubeconfig 28 | # Loaded by direnv, this should be pulled automatically by Mage tasks, if direnv is correctly setup. 29 | export KUBECONFIG=.cache/config 30 | 31 | # Default Testing configuration for local dev work 32 | export DSV_CREDENTIALS_ANNOTATION_VALUE='app1' 33 | export DSV_K8S_TEST_SECRET_PATH='secrets:ci:tests:dsv-k8s:sync-test' 34 | # Without this codespaces might have an issues with loading with mage initially without gcc being installed 35 | export CGO_ENABLED=0 36 | export MAGEFILE_HASHFAST=1 # use mage -f to force recompile, this should make it faster if you aren't editing magefiles often 37 | export GOTEST_DISABLE_RACE=1 # this requires CGO and not sure this app is compatible with race conditions checks, run this seperately manually if needed 38 | 39 | # for tilt lsp 40 | alias tilt="$(aqua which tilt)" 41 | # easier for new folks to filter this way 42 | alias mages="$(aqua which mage-select)" 43 | 44 | eval $(minikube docker-env) 45 | 46 | if [[ -f ".env" ]]; then 47 | dotenv_if_exists ".env" 48 | else 49 | printf "${BACKGROUND_LIGHT_YELLOW}LOCAL ENVIRONMENT CONFIGURATION${RESET_COLOR}\n" 50 | printf "${WARNING_COLOR}👉 .env not found\n" 51 | printf "${WARNING_COLOR}\tSuggested fixes:\n" 52 | printf "${WARNING_COLOR}\t\t1. create ${BACKGROUND_LIGHT_GREEN}.env${RESET_COLOR}\n" 53 | printf "${WARNING_COLOR}\t\t2. ensure ${RESET_COLOR}${BACKGROUND_LIGHT_GREEN}.env${RESET_COLOR}${WARNING_COLOR} contains the following variables: \n\n" 54 | fi 55 | # Check if DSV_PROFILE_NAME is set 56 | if [[ -z "${DSV_PROFILE_NAME}" ]]; then 57 | printf "\t\t\t- ${WARNING_COLOR}👉 DSV_PROFILE_NAME not set${RESET_COLOR}\n" 58 | else 59 | printf "✔️ ${INFO_COLOR}DSV_PROFILE_NAME set${RESET_COLOR}\n" 60 | fi 61 | # Check if DSV_TENANT_NAME is set 62 | if [[ -z "${DSV_TENANT_NAME}" ]]; then 63 | printf "\t\t\t- ${WARNING_COLOR}👉 DSV_TENANT_NAME not set${RESET_COLOR}\n" 64 | else 65 | printf "✔️ ${INFO_COLOR}DSV_TENANT_NAME set${RESET_COLOR}\n" 66 | fi 67 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # all others 2 | * @DelineaXPM/dsv-contributors 3 | 4 | # require admin review 5 | .github @DelineaXPM/dsv-admins 6 | CODEOWNERS @DelineaXPM/dsv-admins 7 | 8 | # renovate bot and no review required 9 | aqua.yaml 10 | 11 | # no extra review required for devcontainer updates 12 | .devcontainer/ 13 | -------------------------------------------------------------------------------- /.github/auto-assign.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Schema: https://github.com/kentaro-m/auto-assign-action 3 | runOnDraft: true 4 | addReviewers: true 5 | addAssignees: author 6 | numberOfReviewers: 0 7 | reviewers: 8 | - dsv-contributors 9 | reviewGroups: 10 | useReviewGroups: false 11 | filterLabels: 12 | exclude: 13 | - wontmerge 14 | - wip 15 | - incomplete pr 16 | -------------------------------------------------------------------------------- /.github/workflows/assign.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: assign 3 | on: 4 | pull_request_target: 5 | types: [opened, ready_for_review] 6 | env: 7 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 8 | jobs: 9 | assign: 10 | uses: delineaxpm/github-workflows/.github/workflows/assign.yml@main 11 | secrets: inherit 12 | -------------------------------------------------------------------------------- /.github/workflows/changie-trigger-release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: changie-trigger-release 3 | on: 4 | workflow_dispatch: 5 | permissions: 6 | contents: write 7 | pull-requests: write 8 | jobs: 9 | changelog: 10 | uses: delineaxpm/github-workflows/.github/workflows/changie-trigger-release.yml@main 11 | secrets: inherit 12 | with: 13 | additional_git_adds: "'charts/*'" 14 | -------------------------------------------------------------------------------- /.github/workflows/conventional-pr.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: conventional-commit-pr-title 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | permissions: 10 | pull-requests: read 11 | env: 12 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 13 | jobs: 14 | conventional-commit-pr-title: 15 | uses: delineaxpm/github-workflows/.github/workflows/conventional-pr.yml@main 16 | secrets: inherit 17 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: lint 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | workflow_call: 9 | 10 | permissions: 11 | pull-requests: write 12 | checks: write 13 | contents: read 14 | jobs: 15 | lint: 16 | uses: delineaxpm/github-workflows/.github/workflows/lint.yml@main 17 | secrets: inherit 18 | -------------------------------------------------------------------------------- /.github/workflows/release-composite.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: release-composite 3 | on: 4 | push: 5 | branches: [main] 6 | paths: [CHANGELOG.md] # your changelog file if different 7 | workflow_dispatch: 8 | 9 | # concurrency: 10 | # group: ${{ github.workflow }}-${{ github.ref }}-${{ github.action }} 11 | # cancel-in-progress: true 12 | 13 | permissions: 14 | pull-requests: write 15 | checks: write 16 | # NOTE: individual jobs define more narrowly scoped permissions. 17 | # Release requires so must be defined here 18 | contents: write 19 | 20 | jobs: 21 | lint: 22 | uses: ./.github/workflows/lint.yml 23 | secrets: inherit 24 | test: 25 | uses: ./.github/workflows/test.yml 26 | secrets: inherit 27 | release: 28 | needs: [test] 29 | uses: ./.github/workflows/release.yml 30 | secrets: inherit 31 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: release 3 | on: 4 | # push: 5 | # tags: 6 | # - 'v*' 7 | workflow_dispatch: 8 | workflow_call: 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }}-${{ github.action }} 11 | cancel-in-progress: true 12 | jobs: 13 | goreleaser: 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 15 16 | steps: 17 | - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 18 | 19 | # https://github.com/magnetikonline/action-golang-cache 20 | - name: Setup Golang with cache 21 | uses: magnetikonline/action-golang-cache@797f193169d3c8ba6f60d90f50ecdadd2583fbd8 # tag=v3 22 | with: 23 | go-version: ~1.21 24 | # with: 25 | # go-version-file: go.mod 26 | 27 | - uses: aquaproj/aqua-installer@fd2089d1f56724d6456f24d58605e6964deae124 # v2.3.2 28 | with: 29 | aqua_version: v2.28.0 30 | enable_aqua_install: true 31 | aqua_opts: '--tags release' 32 | policy_allow: true 33 | env: 34 | AQUA_LOG_LEVEL: debug 35 | AQUA_OPTS: '' 36 | - name: mage-tidy 37 | uses: magefile/mage-action@a3d5bb52942181c125118a2be4b4664c3337aef6 # v2 38 | with: 39 | version: latest 40 | args: init 41 | - name: docker-login 42 | uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2 43 | with: 44 | username: ${{ secrets.DSV_DOCKER_USERNAME }} 45 | password: ${{ secrets.DSV_DOCKER_PASSWORD }} 46 | - name: quay-login 47 | uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2 48 | with: 49 | registry: quay.io 50 | username: ${{ secrets.DOCKER_QUAY_LOGIN_NAME }} 51 | password: ${{ secrets.DOCKER_QUAY_PASSWORD }} 52 | - name: mage-release 53 | uses: magefile/mage-action@a3d5bb52942181c125118a2be4b4664c3337aef6 # v2 54 | with: 55 | version: latest 56 | args: release 57 | env: 58 | # GitHub sets this automatically 59 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 60 | SLACK_CHANNEL: ${{ secrets.DSV_SLACK_CHANNEL }} 61 | SLACK_WEBHOOK: ${{ secrets.DSV_SLACK_WEBHOOK }} 62 | DOCKER_ORG: ${{ secrets.DSV_DOCKER_REGISTRY }} 63 | 64 | # - name: Run GoReleaser 65 | # uses: goreleaser/goreleaser-action@ff11ca24a9b39f2d36796d1fbd7a4e39c182630a # renovate: tag=v3.1.0 66 | # with: 67 | # version: latest 68 | # args: release --rm-dist 69 | # env: 70 | # # GitHub sets this automatically 71 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 72 | # SLACK_CHANNEL: ${{ secrets.DSV_SLACK_CHANNEL }} 73 | # SLACK_WEBHOOK: ${{ secrets.DSV_SLACK_WEBHOOK }} 74 | # DOCKER_ORG: ${{ secrets.DSV_DOCKER_REGISTRY }} 75 | -------------------------------------------------------------------------------- /.github/workflows/scan.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: scan 3 | on: 4 | workflow_call: 5 | workflow_dispatch: 6 | schedule: 7 | - cron: '35 18 * * 0' 8 | push: 9 | branches: ['main'] 10 | pull_request: 11 | # The branches below must be a subset of the branches above 12 | branches: ['main'] 13 | permissions: 14 | actions: read 15 | contents: read 16 | security-events: write 17 | concurrency: 18 | group: ${{ github.workflow }}-${{ github.ref }}-${{ github.action }} 19 | cancel-in-progress: true 20 | jobs: 21 | scan: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 25 | - uses: aquaproj/aqua-installer@fd2089d1f56724d6456f24d58605e6964deae124 # v2.3.2 26 | with: 27 | aqua_version: v2.28.0 28 | enable_aqua_install: true 29 | aqua_opts: '--tags scan' 30 | env: 31 | AQUA_LOG_LEVEL: debug 32 | AQUA_OPTS: '' 33 | - name: env-config-go 34 | run: | 35 | echo "$(go env GOPATH)/bin" >> $GITHUB_PATH 36 | # no pinning of github managed action 37 | - name: Setup Golang caches 38 | uses: actions/cache@2f8e54208210a422b2efd51efaa6bd6d7ca8920f # v3 39 | with: 40 | path: | 41 | ${HOME}/.cache/go-build 42 | ${HOME}/go/pkg/mod 43 | key: ${{ runner.os }}-golang-${{ hashFiles('**/go.sum') }} 44 | restore-keys: | 45 | ${{ runner.os }}-golang- 46 | # This should be informational, and not block as it's experimental and no exclusion logic at this time that I've found. 47 | # https://go.dev/security/vuln/#feedback 48 | - name: govuln-scan 49 | uses: elgohr/go-vulncheck-action@04c514965656cb0768b37118284fb7c9c40477db # renovate tag=v1 50 | continue-on-error: true 51 | # - name: mage-vulcheck 52 | # run: | 53 | # export PATH="${AQUA_ROOT_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/aquaproj-aqua}/bin/aqua:${PATH}" 54 | # export PATH="$(go env GOPATH)/bin:${PATH}" 55 | # mage vulncheck 56 | codeql: 57 | name: codeql-scan 58 | runs-on: ubuntu-latest 59 | strategy: 60 | fail-fast: false 61 | matrix: 62 | language: ['go'] 63 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 64 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 65 | 66 | steps: 67 | - name: Checkout repository 68 | uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 69 | 70 | # Initializes the CodeQL tools for scanning. 71 | - name: Initialize CodeQL 72 | uses: github/codeql-action/init@b8d3b6e8af63cde30bdc382c0bc28114f4346c88 # v2 73 | with: 74 | languages: ${{ matrix.language }} 75 | # If you wish to specify custom queries, you can do so here or in a config file. 76 | # By default, queries listed here will override any specified in a config file. 77 | # Prefix the list here with "+" to use these queries and those in the config file. 78 | 79 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 80 | # queries: security-extended,security-and-quality 81 | 82 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 83 | # If this step fails, then you should remove it and run the build manually (see below) 84 | - name: Autobuild 85 | uses: github/codeql-action/autobuild@b8d3b6e8af63cde30bdc382c0bc28114f4346c88 # v2 86 | 87 | # ℹ️ Command-line programs to run using the OS shell. 88 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 89 | 90 | # If the Autobuild fails above, remove it and uncomment the following three lines. 91 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 92 | # - run: | 93 | # echo "Run, Build Application using script" 94 | # ./location_of_script_within_repo/buildscript.sh 95 | 96 | - name: Perform CodeQL Analysis 97 | uses: github/codeql-action/analyze@b8d3b6e8af63cde30bdc382c0bc28114f4346c88 # v2 98 | with: 99 | category: '/language:${{matrix.language}}' 100 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: stale 3 | on: 4 | schedule: 5 | - cron: 30 1 * * * 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | stale: 14 | uses: delineaxpm/github-workflows/.github/workflows/stale.yml@main 15 | secrets: inherit 16 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: test 3 | 4 | # secrets aren't inherited to tasks, so not able to use the reusable as of 2023-02. 5 | # Using workflow directly and will revisit in the future. 6 | # uses: delineaxpm/github-workflows/.github/workflows/test.yml@main 7 | # secrets: inherit 8 | env: 9 | DSV_K8S_TEST_CONFIG: ${{ secrets.DSV_TMG_CONFIG }} 10 | DSV_K8S_TEST_SECRET_PATH: ci:tests:dsv-k8s:sync-test 11 | 12 | on: 13 | pull_request: 14 | push: 15 | tags: 16 | - v* 17 | workflow_dispatch: 18 | inputs: 19 | gotestflags: 20 | type: string 21 | description: 'GOTEST_FLAGS to pass' 22 | required: false 23 | default: '--tags=integration' 24 | workflow_call: 25 | inputs: 26 | gotestflags: 27 | type: string 28 | description: 'GOTEST_FLAGS to pass' 29 | required: false 30 | default: '--tags=integration' 31 | defaults: 32 | run: 33 | shell: bash 34 | 35 | permissions: 36 | contents: read 37 | # concurrency: 38 | # group: ${{ github.workflow }}-${{ github.ref }}-${{ github.action }} 39 | # cancel-in-progress: true 40 | 41 | jobs: 42 | test: 43 | runs-on: ubuntu-latest 44 | timeout-minutes: 5 45 | steps: 46 | - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 47 | - uses: aquaproj/aqua-installer@fd2089d1f56724d6456f24d58605e6964deae124 # v2.3.2 48 | with: 49 | aqua_version: v2.28.0 50 | enable_aqua_install: true 51 | aqua_opts: '--tags test' 52 | env: 53 | AQUA_LOG_LEVEL: debug 54 | AQUA_OPTS: '' 55 | - name: env-config-go 56 | run: | 57 | echo "$(go env GOPATH)/bin" >> $GITHUB_PATH 58 | # no pinning of github managed action 59 | - name: Setup Golang caches 60 | uses: actions/cache@2f8e54208210a422b2efd51efaa6bd6d7ca8920f # v3 61 | with: 62 | path: | 63 | ${HOME}/.cache/go-build 64 | ${HOME}/go/pkg/mod 65 | key: ${{ runner.os }}-golang-${{ hashFiles('**/go.sum') }} 66 | restore-keys: | 67 | ${{ runner.os }}-golang- 68 | # - name: mage-init 69 | # run: | 70 | # export PATH="${AQUA_ROOT_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/aquaproj-aqua}/bin/aqua:${PATH}" 71 | # export PATH="$(go env GOPATH)/bin:${PATH}" 72 | # mage init 73 | - name: mage-testsum 74 | run: | 75 | export PATH="${AQUA_ROOT_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/aquaproj-aqua}/bin/aqua:${PATH}" 76 | export PATH="$(go env GOPATH)/bin:${PATH}" 77 | USER_GOTESTFLAGS="${{ github.event.inputs.name }}" 78 | # Use user input or fall back to --tags=integration if nothing provided 79 | GOTEST_FLAGS=${USER_GOTESTFLAGS:-"--tags=integration"} 80 | mage go:testsum ./... 81 | env: 82 | # Race conditions will be hit due to the cli driven tasks and binaries being called. 83 | GOTEST_DISABLE_RACE: 1 84 | # GOTEST_FLAGS: '${{ inputs.gotestflags }}' 85 | 86 | - uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457 # v3.1.6 87 | with: 88 | fail_ci_if_error: false 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/go 2 | # Edit at https://www.gitignore.io/?templates=go 3 | 4 | ### Go ### 5 | # Binaries for programs and plugins 6 | *.exe 7 | *.exe~ 8 | *.dll 9 | *.so 10 | *.dylib 11 | 12 | # Test binary, built with `go test -c` 13 | *.test 14 | 15 | # Output of the go coverage tool, specifically when used with LiteIDE 16 | *.out 17 | 18 | # Dependency directories (remove the comment below to include it) 19 | # vendor/ 20 | 21 | ### Go Patch ### 22 | # /vendor/ 23 | /Godeps/ 24 | 25 | # End of https://www.gitignore.io/api/go 26 | configs/ 27 | target 28 | 29 | /dsv-injector 30 | /dsv-syncer 31 | 32 | .artifacts/ 33 | .cache/ 34 | /aws/ 35 | /awscli.sig 36 | /awscli.zip 37 | adhoc* 38 | 39 | # Artifact File 40 | mage_output_file.go 41 | minikube-linux-amd64 42 | packages-* 43 | *.DS_Store 44 | /secret-*.yaml 45 | 46 | vendor/**/*.png 47 | 48 | Tiltfile.local 49 | .env 50 | -------------------------------------------------------------------------------- /.gitleaks.toml: -------------------------------------------------------------------------------- 1 | # Templates: https://github.com/zricethezav/gitleaks/tree/master/examples 2 | title = "gitleaks config" 3 | 4 | [[rules]] 5 | description = "AWS Access Key" 6 | regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' 7 | tags = ["key", "AWS"] 8 | 9 | [[rules]] 10 | description = "AWS cred file info" 11 | regex = '''(?i)(aws_access_key_id|aws_secret_access_key)(.{0,20})?=.[0-9a-zA-Z\/+]{20,40}''' 12 | tags = ["AWS"] 13 | 14 | [[rules]] 15 | description = "AWS Secret Key" 16 | regex = '''(?i)aws(.{0,20})?(?-i)['\"][0-9a-zA-Z\/+]{40}['\"]''' 17 | tags = ["key", "AWS"] 18 | 19 | [[rules]] 20 | description = "AWS MWS key" 21 | regex = '''amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}''' 22 | tags = ["key", "AWS", "MWS"] 23 | 24 | [[rules]] 25 | description = "Facebook Secret Key" 26 | regex = '''(?i)(facebook|fb)(.{0,20})?(?-i)['\"][0-9a-f]{32}['\"]''' 27 | tags = ["key", "Facebook"] 28 | 29 | [[rules]] 30 | description = "Facebook Client ID" 31 | regex = '''(?i)(facebook|fb)(.{0,20})?['\"][0-9]{13,17}['\"]''' 32 | tags = ["key", "Facebook"] 33 | 34 | [[rules]] 35 | description = "Twitter Secret Key" 36 | regex = '''(?i)twitter(.{0,20})?['\"][0-9a-z]{35,44}['\"]''' 37 | tags = ["key", "Twitter"] 38 | 39 | [[rules]] 40 | description = "Twitter Client ID" 41 | regex = '''(?i)twitter(.{0,20})?['\"][0-9a-z]{18,25}['\"]''' 42 | tags = ["client", "Twitter"] 43 | 44 | [[rules]] 45 | description = "Github" 46 | regex = '''(?i)github(.{0,20})?(?-i)['\"][0-9a-zA-Z]{35,40}['\"]''' 47 | tags = ["key", "Github"] 48 | 49 | [[rules]] 50 | description = "LinkedIn Client ID" 51 | regex = '''(?i)linkedin(.{0,20})?(?-i)['\"][0-9a-z]{12}['\"]''' 52 | tags = ["client", "LinkedIn"] 53 | 54 | [[rules]] 55 | description = "LinkedIn Secret Key" 56 | regex = '''(?i)linkedin(.{0,20})?['\"][0-9a-z]{16}['\"]''' 57 | tags = ["secret", "LinkedIn"] 58 | 59 | [[rules]] 60 | description = "Slack" 61 | regex = '''xox[baprs]-([0-9a-zA-Z]{10,48})?''' 62 | tags = ["key", "Slack"] 63 | 64 | [[rules]] 65 | description = "EC" 66 | regex = '''-----BEGIN EC PRIVATE KEY-----''' 67 | tags = ["key", "EC"] 68 | 69 | 70 | [[rules]] 71 | description = "Google API key" 72 | regex = '''AIza[0-9A-Za-z\\-_]{35}''' 73 | tags = ["key", "Google"] 74 | 75 | 76 | [[rules]] 77 | description = "Heroku API key" 78 | regex = '''(?i)heroku(.{0,20})?['"][0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}['"]''' 79 | tags = ["key", "Heroku"] 80 | 81 | [[rules]] 82 | description = "MailChimp API key" 83 | regex = '''(?i)(mailchimp|mc)(.{0,20})?['"][0-9a-f]{32}-us[0-9]{1,2}['"]''' 84 | tags = ["key", "Mailchimp"] 85 | 86 | [[rules]] 87 | description = "Mailgun API key" 88 | regex = '''(?i)(mailgun|mg)(.{0,20})?['"][0-9a-z]{32}['"]''' 89 | tags = ["key", "Mailgun"] 90 | 91 | [[rules]] 92 | description = "PayPal Braintree access token" 93 | regex = '''access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}''' 94 | tags = ["key", "Paypal"] 95 | 96 | [[rules]] 97 | description = "Picatic API key" 98 | regex = '''sk_live_[0-9a-z]{32}''' 99 | tags = ["key", "Picatic"] 100 | 101 | [[rules]] 102 | description = "Slack Webhook" 103 | regex = '''https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}''' 104 | tags = ["key", "slack"] 105 | 106 | [[rules]] 107 | description = "Stripe API key" 108 | regex = '''(?i)stripe(.{0,20})?['\"][sk|rk]_live_[0-9a-zA-Z]{24}''' 109 | tags = ["key", "Stripe"] 110 | 111 | [[rules]] 112 | description = "Square access token" 113 | regex = '''sq0atp-[0-9A-Za-z\-_]{22}''' 114 | tags = ["key", "square"] 115 | 116 | [[rules]] 117 | description = "Square OAuth secret" 118 | regex = '''sq0csp-[0-9A-Za-z\\-_]{43}''' 119 | tags = ["key", "square"] 120 | 121 | [[rules]] 122 | description = "Twilio API key" 123 | regex = '''(?i)twilio(.{0,20})?['\"][0-9a-f]{32}['\"]''' 124 | tags = ["key", "twilio"] 125 | 126 | [[rules]] 127 | description = "Env Var" 128 | regex = '''(?i)(apikey|secret|key|api|password|pass|pw|host)=[0-9a-zA-Z-_.{}]{4,120}''' 129 | 130 | [[rules]] 131 | description = "Port" 132 | regex = '''(?i)port(.{0,4})?[0-9]{1,10}''' 133 | [rules.allowlist] 134 | regexes = ['''(?i)port '''] 135 | description = "ignore export " 136 | 137 | 138 | [[rules]] 139 | description = "Email" 140 | regex = '''[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}''' 141 | tags = ["email"] 142 | [rules.allowlist] 143 | files = ['''(?i)bashrc'''] 144 | regexes = ['''(semverbot@github.com)'''] 145 | description = "ignore bashrc emails" 146 | 147 | 148 | [[rules]] 149 | description = "Generic Credential" 150 | regex = '''(?i)(dbpasswd|dbuser|dbname|dbhost|api_key|apikey|secret|key|api|password|user|guid|hostname|pw|auth)(.{0,20})?['|"]([0-9a-zA-Z-_\/+!{}/=]{4,120})['|"]''' 151 | tags = ["key", "API", "generic"] 152 | # ignore leaks with specific identifiers like slack and aws 153 | [rules.allowlist] 154 | description = "ignore slack, mailchimp, aws" 155 | regexes = [ 156 | '''xox[baprs]-([0-9a-zA-Z]{10,48})''', 157 | '''(?i)(.{0,20})?['"][0-9a-f]{32}-us[0-9]{1,2}['"]''', 158 | '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''', 159 | ] 160 | # Generates of noise 161 | # [[rules]] 162 | # description = "High Entropy" 163 | # regex = '''[0-9a-zA-Z-_!{}/=]{4,120}''' 164 | # file = '''(?i)(dump.sql|high-entropy-misc.txt)$''' 165 | # tags = ["entropy"] 166 | # [[rules.Entropies]] 167 | # Min = "4.3" 168 | # Max = "7.0" 169 | # [rules.allowlist] 170 | # description = "ignore ssh key and pems" 171 | # files = ['''(pem|ppk|env)$'''] 172 | # paths = ['''(.*)?ssh'''] 173 | 174 | [[rules]] 175 | description = "Potential bash var" 176 | regex = '''(?i)(=)([0-9a-zA-Z-_!{}=]{4,120})''' 177 | tags = ["key", "bash", "API", "generic"] 178 | [[rules.Entropies]] 179 | Min = "3.5" 180 | Max = "4.5" 181 | Group = "1" 182 | 183 | [[rules]] 184 | description = "WP-Config" 185 | regex = '''define(.{0,20})?(DB_CHARSET|NONCE_SALT|LOGGED_IN_SALT|AUTH_SALT|NONCE_KEY|DB_HOST|DB_PASSWORD|AUTH_KEY|SECURE_AUTH_KEY|LOGGED_IN_KEY|DB_NAME|DB_USER)(.{0,20})?['|"].{10,120}['|"]''' 186 | tags = ["key", "API", "generic"] 187 | 188 | # [[rules]] 189 | # description = "Files with keys and credentials" 190 | # files = '''(?i)(id_rsa|passwd|id_rsa.pub|pgpass|pem|key|shadow)''' 191 | 192 | # Global allowlist 193 | [allowlist] 194 | description = "Allowlisted files" 195 | files = ['''(.*?)(jpg|gif|doc|pdf|bin)$'''] 196 | paths = [ 197 | '''gitleaks\.toml''', 198 | '''.devcontainer/''', # setup scripts for devcontainer/codespace 199 | '''.pre-commit-config.yaml$''', 200 | '''(go.mod|go.sum)$''', 201 | ] 202 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | output: 3 | formats: 4 | - format: tab 5 | linters-settings: 6 | misspell: 7 | locale: US 8 | 9 | nolintlint: 10 | allow-unused: false 11 | allow-no-explanation: [] 12 | require-explanation: true 13 | require-specific: true 14 | gofumpt: 15 | extra-rules: true 16 | godox: 17 | keywords: 18 | - OPTIMIZE 19 | - HACK 20 | - TODO 21 | - BUG 22 | - FIXME 23 | godot: 24 | scope: all 25 | exclude: 26 | - //nolint 27 | - (API) 28 | - ^[ ]*@ 29 | capital: true 30 | depguard: 31 | rules: 32 | main: 33 | deny: 34 | - pkg: 'github.com/sirupsen/logrus' 35 | desc: use zerolog 36 | - pkg: log 37 | desc: use zerolog 38 | - pkg: 'github.com/pkg/errors' 39 | desc: Should be replaced by standard lib errors package 40 | 41 | # gomodguard: 42 | # blocked: 43 | # modules: 44 | # - github.com/sirupsen/logrus: 45 | # recommendations: 46 | # - internal/logging 47 | # reason: logging is allowed only by zerolog. Please use zerolog 48 | # local_replace_directives: false 49 | tagliatelle: 50 | case: 51 | use-field-name: true 52 | rules: 53 | json: snake 54 | yaml: kebab 55 | xml: camel 56 | bson: camel 57 | avro: snake 58 | mapstructure: kebab 59 | errcheck: 60 | check-type-assertions: true 61 | check-blank: false 62 | exclude-functions: 63 | - io/ioutil.ReadFile 64 | - io.Copy(*bytes.Buffer) 65 | - io.Copy(os.Stdout) 66 | - io.Closer.Close 67 | - io.Closer.Body.Close 68 | govet: 69 | enable-all: true 70 | disable: 71 | - fieldalignment 72 | settings: 73 | printf: 74 | funcs: 75 | - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof 76 | - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf 77 | - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf 78 | - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf 79 | varnamelen: 80 | max-distance: 15 81 | min-name-length: 3 82 | check-receiver: false 83 | check-return: false 84 | ignore-type-assert-ok: true 85 | ignore-map-index-ok: true 86 | ignore-chan-recv-ok: true 87 | ignore-names: 88 | - err 89 | - tt 90 | - i 91 | - x 92 | - id 93 | - b 94 | - ok 95 | - zl 96 | - fs 97 | 98 | revive: 99 | ignore-generated-header: true 100 | enable-all-rules: true 101 | rules: 102 | - name: var-naming 103 | severity: error 104 | - name: line-length-limit 105 | severity: warning 106 | arguments: 107 | - 400 108 | - name: function-length 109 | severity: warning 110 | arguments: [20, 5000] 111 | 112 | linters: 113 | enable-all: true 114 | disable: 115 | - scopelint 116 | - paralleltest 117 | - noctx 118 | - wsl 119 | - lll 120 | - interfacer 121 | - golint 122 | - maligned 123 | - goimports 124 | - gci 125 | - gofmt 126 | - nlreturn 127 | - gofumpt 128 | - exhaustivestruct 129 | - exhaustruct 130 | - wrapcheck 131 | - godox 132 | - execinquery 133 | - nonamedreturns 134 | - forbidigo 135 | - structcheck 136 | - varcheck 137 | - deadcode 138 | - ifshort 139 | - godox 140 | - godot 141 | - nosnakecase 142 | - rowserrcheck # disabled due to generics, can enable in future if needed 143 | - sqlclosecheck # disabled due to generics, can enable in future if needed 144 | - wastedassign # disabled due to generics, can enable in future if needed 145 | - funlen #OVERRIDE: ok using for bot, lots of quick long commands i worked on 146 | - cyclop #OVERRIDE: ok using for bot, lots of quick long commands i worked on 147 | - gocognit #OVERRIDE: ok using for bot, lots of quick long commands i worked on 148 | - perfsprint 149 | run: 150 | timeout: 5m 151 | build-tags: 152 | - mage 153 | - tools 154 | - integration 155 | - codeanalysis 156 | issues: 157 | exclude-rules: 158 | - path: _test\.go 159 | linters: 160 | - goerr113 161 | - wrapcheck 162 | - funlen 163 | - cyclop 164 | - gocognit 165 | - unparam 166 | - varnamelen 167 | - revive 168 | - linters: 169 | - goerr113 170 | text: do not define dynamic errors 171 | - path: magefiles 172 | linters: 173 | - goerr113 174 | - wrapcheck 175 | - funlen 176 | - gocyclo 177 | - cyclop 178 | - gocognit 179 | - maintidx 180 | - deadcode 181 | - gochecknoglobals 182 | - path: magefile.go 183 | linters: 184 | - goerr113 185 | - wrapcheck 186 | - funlen 187 | - gocyclo 188 | - cyclop 189 | - gocognit 190 | - maintidx 191 | - deadcode 192 | - gochecknoglobals 193 | - linters: 194 | - goerr113 195 | text: magefiles don't need to worry about wrapping in the same way 196 | - linters: 197 | - govet 198 | - revive 199 | text: 'shadow: declaration of .err. shadows declaration' 200 | - path: mocks 201 | linters: 202 | - godot 203 | text: mocked files do not need to be checked 204 | whole-files: false 205 | exclude-dirs: 206 | - build 207 | - .artifacts 208 | - .cache 209 | - artifacts 210 | - .trunk 211 | - _tools 212 | - vendor 213 | - vendor$ 214 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | project_name: dsv-k8s 4 | dist: .artifacts/goreleaser 5 | env: 6 | - GITHUB_TOKEN='' 7 | - GITLAB_TOKEN='' 8 | - GITEA_TOKEN='' 9 | - LOCAL_DEBUGGING=false # set to make this quick and bypass extra effort builds like archiving zip 10 | - CGO_ENABLED=0 11 | - BUILD_NAME='{{ if index .Env "BUILD_NAME" }}{{ .Env.BUILD_NAME }}{{else}}""{{end}}' 12 | before: 13 | hooks: 14 | - go mod download 15 | 16 | builds: 17 | - id: dsv-injector 18 | binary: dsv-injector 19 | main: ./cmd/injector 20 | mod_timestamp: '{{ .CommitTimestamp }}' 21 | ldflags: 22 | - -s -w 23 | - -X main.version={{ .Tag }} 24 | - -X main.commit={{ .FullCommit }} 25 | - -X main.date={{ .Now.Format "2006-01-02T15:04:05Z07:00" }} #RFC3339 26 | - -X main.builtBy=goreleaser 27 | - -X main.buildName={{ .Env.BUILD_NAME }} 28 | goos: [linux] 29 | goarch: 30 | - amd64 31 | - id: dsv-syncer 32 | binary: dsv-syncer 33 | main: ./cmd/syncer 34 | mod_timestamp: '{{ .CommitTimestamp }}' 35 | ldflags: 36 | - -s -w 37 | - -X main.version={{ .Tag }} 38 | - -X main.commit={{ .FullCommit }} 39 | - -X main.date={{ .Now.Format "2006-01-02T15:04:05Z07:00" }} #RFC3339 40 | - -X main.builtBy=goreleaser 41 | goos: [linux] 42 | goarch: 43 | - amd64 44 | archives: 45 | - id: binaries 46 | format: binary 47 | 48 | checksum: 49 | name_template: checksums.txt 50 | algorithm: sha256 51 | disable: false 52 | snapshot: 53 | name_template: '{{ incpatch .Version }}-next' 54 | changelog: 55 | disable: true 56 | sort: asc 57 | use: github 58 | groups: 59 | - title: Features 60 | regexp: "^.*feat[(\\w)]*:+.*$" 61 | order: 0 62 | - title: 'Fixes' 63 | regexp: "^.*fix[(\\w)]*:+.*$" 64 | order: 1 65 | - title: 'CI & Chore' 66 | regexp: "^.*(fix|chore|build)[(\\w)]*:+.*$" 67 | order: 2 68 | - title: Others 69 | order: 999 70 | filters: 71 | exclude: 72 | - '^docs:' 73 | - '^test:' 74 | - '^style:' 75 | dockers: 76 | - id: docker-publish 77 | goos: linux 78 | # goarch: amd64 79 | # goarm: '' 80 | # goamd64: v2 81 | image_templates: 82 | - '{{ if index .Env "DOCKER_ORG" }}{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:{{ .Tag }}{{ end }}' 83 | - '{{ if index .Env "DOCKER_ORG" }}{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:latest{{ end }}' 84 | 85 | # tag for quay publishing both with semver and latest tag 86 | - '{{ if index .Env "QUAY_ORG" }}{{ .Env.QUAY_ORG }}/{{ .ProjectName }}:{{ .Tag }}{{ end }}' 87 | - '{{ if index .Env "QUAY_ORG" }}{{ .Env.QUAY_ORG }}/{{ .ProjectName }}:latest{{ end }}' 88 | 89 | skip_push: false 90 | dockerfile: ./docker/Dockerfile.distroless 91 | use: buildx 92 | build_flag_templates: 93 | - --platform=linux/amd64 94 | - --label=org.opencontainers.image.created={{ .Now.Format "2006-01-02T15:04:05Z07:00" }} 95 | - --label=org.opencontainers.image.title={{ .ProjectName }} 96 | - --label=org.opencontainers.image.revision={{ .FullCommit }} 97 | - --label=org.opencontainers.image.version={{.Version}} 98 | # - --label=org.opencontainers.image.version="{{ .Tag }}" 99 | # local builds 100 | - id: local-docker-images 101 | goos: linux 102 | image_templates: 103 | - 'dev.local/{{ .ProjectName }}:{{ .Tag }}' 104 | - 'dev.local/{{ .ProjectName }}:latest' # This one is for dev usage so latest version, no tagged semver required in docker compose or local testing 105 | skip_push: true 106 | dockerfile: ./docker/Dockerfile.distroless 107 | use: buildx 108 | build_flag_templates: 109 | - --platform=linux/amd64 110 | - --label=org.opencontainers.image.created={{ .Now.Format "2006-01-02T15:04:05Z07:00" }} 111 | - --label=org.opencontainers.image.title={{ .ProjectName }} 112 | - --label=org.opencontainers.image.revision={{ .FullCommit }} 113 | - --label=org.opencontainers.image.version={{.Version}} 114 | announce: 115 | slack: 116 | enabled: true 117 | # The name of the channel that the user selected as a destination for webhook messages. 118 | channel: '{{ .Env.SLACK_CHANNEL }}' 119 | message_template: ':github: {{ .ProjectName }} {{ .Tag }} is out. {{ .ReleaseURL }}' 120 | # Emoji to use as the icon for this message. Overrides icon_url. 121 | icon_emoji: ':rocket:' 122 | sboms: 123 | - artifacts: binary 124 | documents: 125 | - '${artifact}.spdx.sbom' 126 | -------------------------------------------------------------------------------- /.hadolint.yaml: -------------------------------------------------------------------------------- 1 | # Following source doesn't work in most setups 2 | ignored: 3 | - SC1090 4 | - SC1091 5 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/dsv-k8s.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/markdown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | #https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md002 3 | 4 | # enable all default tagged rules 5 | default: true 6 | 7 | # enable indentation rules by default 8 | indentation: true 9 | 10 | #MD004 - Unordered list style 11 | # Parameters: style ("consistent", "asterisk", "plus", "dash", "sublist"; default "consistent") 12 | # Defaults to dash 13 | MD004: 14 | style: dash 15 | 16 | # MD041 - First line in a file should be a top-level heading: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md041---first-line-in-a-file-should-be-a-top-level-heading 17 | MD041: false 18 | # MD007 - Unordered list indentation: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md007---unordered-list-indentation 19 | # This rule is triggered when list items are not indented by the configured number of spaces (default: 2). 20 | MD007: true 21 | no-hard-tabs: true 22 | 23 | # enable all whitespace rules, such as: 24 | # - eliminate trailing whitespace 25 | # - no tabs, use only spaces 26 | # - remove double spaces 27 | # - clean spacing up 28 | whitespace: true 29 | 30 | # require urls to have []() format, rather than just pasting raw links. This improves readability. 31 | no-bare-urls: true 32 | 33 | # Don't allow anonymous code blocks, make sure a language is specificed, so that formatting can be applied. 34 | fenced-code-language: true 35 | # Don't allow nested html to be directly used without exception being allowed. 36 | no-inline-html: true 37 | # MD025 - Multiple top-level headings in the same document -> https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md025---multiple-top-level-headings-in-the-same-document 38 | # if using front matter, we don't want a title property + a # Title to conflict 39 | MD025: true 40 | 41 | # MD013 - Line length 42 | # This is for maintainability and code diffs. 43 | # Try applying semantic line break concept for breaking up longer phrases 44 | # https://sembr.org/ 45 | MD013: false 46 | 47 | # MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content 48 | # This tweaks to allow nested items to have duplicate headers. 49 | MD024: 50 | # Only check sibling headings 51 | siblings_only: true 52 | MD034: false 53 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | v20.15.0 2 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Setup: pre-commit install 3 | # Upgrade: pre-commit autoupdate 4 | # Run: pre-commit run --all-files 5 | # https://pre-commit.com/hooks.html 6 | default_language_version: 7 | # force all unspecified python hooks to run python3 8 | python: python3 9 | node: 16.15.0 10 | exclude: | 11 | (?x)( 12 | ^.devcontainer/| 13 | ^.cache/| 14 | ^.artifacts/| 15 | ^vendor/| 16 | ^vendir.lock.yml$| 17 | vendor/ 18 | ) 19 | default_stages: [commit, push] 20 | repos: 21 | - repo: https://github.com/jumanjihouse/pre-commit-hook-yamlfmt 22 | # https://github.com/jumanjihouse/pre-commit-hook-yamlfmt#override-defaults 23 | # https://yaml.readthedocs.io/en/latest/example.html 24 | rev: 0.2.2 # or specific tag 25 | hooks: 26 | - id: yamlfmt 27 | types_or: [yaml] 28 | args: [--mapping, '2', --sequence, '4', --offset, '2', --width, '999'] 29 | - repo: https://github.com/Yelp/detect-secrets 30 | rev: v1.3.0 31 | hooks: 32 | - name: detect-secrets 33 | id: detect-secrets 34 | stages: [commit] 35 | args: 36 | [ 37 | --disable-plugin, 38 | KeywordDetector, 39 | --exclude-files, 40 | .gitleaks.toml, 41 | --exclude-files, 42 | .trunk/trunk.yaml, 43 | ] 44 | - repo: https://github.com/adrienverge/yamllint.git 45 | rev: v1.27.1 # or higher tag 46 | hooks: 47 | - id: yamllint 48 | types_or: [yaml] 49 | args: [--format, parsable, --strict, --config-file, .yamllint.yaml] 50 | - repo: https://github.com/pre-commit/pre-commit-hooks 51 | rev: v4.3.0 # Use the ref you want to point at 52 | hooks: 53 | - id: trailing-whitespace 54 | args: [--markdown-linebreak-ext=md] 55 | - id: check-case-conflict 56 | name: Prevent case conflicts 57 | - id: detect-aws-credentials 58 | args: [--allow-missing-credentials] 59 | - id: mixed-line-ending 60 | name: Normalize line endings to lf 61 | args: [--fix=lf] 62 | - id: fix-byte-order-marker 63 | - id: check-added-large-files 64 | name: No large files, use artifacts for that 65 | - id: check-merge-conflict 66 | name: Prevent merge markers being committed 67 | - id: forbid-new-submodules 68 | name: Don't allow git submodules 69 | pass_filenames: false 70 | - id: no-commit-to-branch 71 | name: Don't commit to main 72 | args: [--branch, master, --branch, main] 73 | pass_filenames: false 74 | - repo: https://gitlab.com/bmares/check-json5 75 | rev: v1.0.0 76 | hooks: 77 | - id: 78 | check-json5 79 | # stages: ['commit'] 80 | - repo: local 81 | hooks: 82 | - id: golines 83 | name: go-fmt 84 | description: Run formatter against changed files 85 | entry: golines --base-formatter="gofumpt" -w --max-len=120 --no-reformat-tags 86 | types: [go] 87 | language: system 88 | pass_filenames: true 89 | - id: golangci-lint 90 | name: golangci-lint 91 | description: Fast linters runner for Go. 92 | entry: golangci-lint run --fix --timeout 15s --new-from-rev=HEAD~ 93 | types: [go] 94 | language: system 95 | pass_filenames: false 96 | - id: go-test-all 97 | name: go-test-all 98 | description: Run integration tests for go 99 | entry: gotestsum --format pkgname -- -shuffle=on -race -tags integration ./... 100 | stages: [commit] 101 | types: [go] 102 | language: system 103 | pass_filenames: false 104 | verbose: true 105 | - id: go-mod-tidy 106 | name: go-mod-tidy 107 | description: Run go mod tidy 108 | entry: go mod tidy 109 | stages: [commit] 110 | types: [go] 111 | language: golang 112 | pass_filenames: false 113 | verbose: true 114 | # - id: go-mod-vendor 115 | # name: go-mod-vendor 116 | # description: Run go mod vendor 117 | # # entry: #env GOTEST_FLAGS='-tags integration' mage -v go:test 118 | # entry: mod vendor 119 | # stages: [commit] 120 | # types: [go] 121 | # language: golang 122 | # pass_filenames: false 123 | # verbose: true 124 | # TODO: not certain why, but go work sync or go version etc doesn't work here 125 | 126 | - repo: https://github.com/DavidAnson/markdownlint-cli2 127 | rev: v0.5.1 128 | hooks: 129 | - id: markdownlint-cli2 130 | - repo: https://github.com/zricethezav/gitleaks 131 | rev: v8.11.0 132 | hooks: 133 | - id: gitleaks 134 | name: gitleaks-scan 135 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | singleQuote: true 3 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- 1 | enable=all 2 | source-path=SCRIPTDIR 3 | disable=SC2154 4 | 5 | # If you're having issues with shellcheck following source, disable the errors via: 6 | # disable=SC1090 7 | # disable=SC1091 8 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.25.0 3 | ignore: {} 4 | patch: {} 5 | exclude: 6 | global: 7 | - vendor/ 8 | - .devcontainer/ 9 | -------------------------------------------------------------------------------- /.trunk/.gitignore: -------------------------------------------------------------------------------- 1 | *out 2 | *logs 3 | *actions 4 | *notifications 5 | *tools 6 | plugins 7 | user_trunk.yaml 8 | user.yaml 9 | tmp 10 | -------------------------------------------------------------------------------- /.trunk/trunk.yaml: -------------------------------------------------------------------------------- 1 | version: 0.1 2 | plugins: 3 | sources: 4 | - id: trunk 5 | ref: v1.5.0 6 | uri: https://github.com/trunk-io/plugins 7 | actions: 8 | enabled: 9 | - helm-docs-syncer 10 | - helm-docs-injector 11 | - trunk-announce 12 | - trunk-cache-prune 13 | - trunk-upgrade-available 14 | - trunk-fmt-pre-commit 15 | - trunk-check-pre-push 16 | - helm-lint 17 | # - gotestsum 18 | # - go-mod-vendor 19 | definitions: 20 | - id: gotestsum 21 | runtime: go 22 | description: run go tests on pre-push 23 | run: gotestsum --format pkgname -- -shuffle=on -tags integration ./... 24 | triggers: 25 | - git_hooks: [pre-push] 26 | - id: go-mod-vendor 27 | description: When go mod or sum is updated, go vendoring should be run to ensure it's consistent. 28 | runtime: go 29 | run: mod vendor 30 | triggers: 31 | - files: [go.mod] 32 | - id: helm-lint 33 | # runtime: go 34 | description: run helm lint on pre-push 35 | run: 'helm lint --quiet charts/*' 36 | triggers: 37 | - git_hooks: [pre-push] 38 | # AQUA installs helm-docs, so run as if already exists as these actions will be only for the refresh of charts/release process 39 | - id: helm-docs-injector 40 | description: run helm-docs on pre-commit when the helm charts are updated 41 | run: helm-docs --chart-search-root charts/dsv-injector --output-file README.md && trunk fmt 42 | triggers: 43 | - files: 44 | [ 45 | 'charts/dsv-injector/values.yaml', 46 | 'charts/dsv-injector/Chart.yaml', 47 | ] 48 | - git_hooks: [pre-commit] 49 | - id: helm-docs-syncer 50 | description: run helm-docs on pre-commit when the helm charts are updated 51 | run: helm-docs --chart-search-root charts/dsv-syncer --output-file README.md && trunk fmt 52 | triggers: 53 | - files: 54 | ['charts/dsv-syncer/values.yaml', 'charts/dsv-syncer/Chart.yaml'] 55 | - git_hooks: [pre-commit] 56 | runtimes: 57 | enabled: 58 | - go@1.21.0 59 | - node@18.12.1 60 | - python@3.10.8 61 | cli: 62 | version: 1.22.1 63 | lint: 64 | threshold: 65 | - linters: [gitleaks] 66 | level: high 67 | disabled: 68 | - gokart 69 | - cspell 70 | - gofmt 71 | enabled: 72 | - checkov@3.2.128 73 | - osv-scanner@1.7.4 74 | - terrascan@1.19.1 75 | - trivy@0.52.0 76 | - trufflehog@3.78.0 77 | - gofumpt@0.5.0 78 | - renovate@37.396.1 79 | - golangci-lint@SYSTEM 80 | - git-diff-check 81 | - taplo@0.8.1 82 | - markdownlint@0.41.0 83 | - prettier@3.3.1 84 | - actionlint@1.7.1 85 | - hadolint@2.12.0 86 | - gitleaks@8.18.3 87 | - shellcheck@0.10.0 88 | - shfmt@3.6.0 89 | - yamllint@1.35.1 90 | - svgo@3.3.2 91 | - prettier@2.8.3 92 | - git-diff-check 93 | - taplo@0.8.1 94 | - yamllint@1.29.0 95 | - actionlint@1.7.1 96 | - gitleaks@8.15.3 97 | - hadolint@2.12.0 98 | - markdownlint@0.41.0 99 | - shellcheck@0.10.0 100 | - shfmt@3.6.0 101 | 102 | ignore: 103 | - linters: [ALL] 104 | paths: 105 | # Generated files 106 | - .cache/* 107 | - .artifacts/* 108 | - .devcontainer/* 109 | - configs/* 110 | # Test data 111 | - b/test_data/** 112 | - vendor/* 113 | - .changes/* 114 | - 'charts/*/templates/*' 115 | # - linters: [prettier, yamllint] 116 | # paths: 117 | - linters: [checkov] 118 | paths: 119 | - '**' 120 | - '!charts/**' 121 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[go.mod]": { 3 | "editor.formatOnSave": true 4 | }, 5 | "[go]": { 6 | "debug.saveBeforeStart": "allEditorsInActiveGroup", 7 | "editor.codeActionsOnSave": { 8 | "source.fixAll": "explicit", 9 | "source.organizeImports": "never" 10 | }, 11 | "editor.defaultFormatter": "go", 12 | "editor.formatOnSave": true 13 | }, 14 | "[markdown]": { 15 | "editor.defaultFormatter": "trunk.io" 16 | }, 17 | "[yaml]": { 18 | "editor.defaultFormatter": "redhat.vscode-yaml" 19 | }, 20 | "[shell]": { 21 | "editor.defaultFormatter": "foxundermoon.shell-format" 22 | }, 23 | "go.toolsManagement.autoUpdate": true, 24 | "go.useLanguageServer": true, 25 | "gopls": { 26 | "buildFlags": [ 27 | "-tags=mage tools integration" // required for gopls to autocomplete mage files with mage tag 28 | ], 29 | "build.expandWorkspaceToModule": true 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "tilt-up", 8 | "type": "shell", 9 | "command": "echo \"⚙️ loading direnv\" && direnv allow .envrc && echo \"🟦 KO_DOCKER_REPO: ${KO_DOCKER_REPO}\" && echo \"⚙️ running tilt up, this may take a minute\" && tilt up", 10 | "runOptions": { 11 | "instanceLimit": 1 12 | }, 13 | "promptOnClose": true, 14 | "options": { 15 | "cwd": "${workspaceFolder}", 16 | "env": { 17 | "KO_DOCKER_REPO": "ko.local" 18 | }, 19 | "shell": { 20 | "executable": "zsh", 21 | "args": ["-l", "-c"] 22 | } 23 | }, 24 | "isBackground": true, 25 | "group": { 26 | "kind": "build", 27 | "isDefault": true 28 | }, 29 | "presentation": { 30 | "echo": true, 31 | "reveal": "always", 32 | "focus": false, 33 | "panel": "dedicated", 34 | "showReuseMessage": true, 35 | "clear": true 36 | }, 37 | "icon": { 38 | "color": "terminal.ansiGreen", 39 | "id": "vm-running" 40 | } 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "scanSettings": { 3 | "configMode": "AUTO", 4 | "configExternalURL": "", 5 | "projectToken": "", 6 | "baseBranches": [], 7 | "enableLicenseViolations": true, 8 | "displayLicenseViolations": false 9 | }, 10 | "checkRunSettings": { 11 | "vulnerableCheckRunConclusionLevel": "failure", 12 | "displayMode": "diff", 13 | "useMendCheckNames": true 14 | }, 15 | "issueSettings": { 16 | "minSeverityLevel": "NONE", 17 | "issueType": "DEPENDENCY" 18 | }, 19 | "remediateSettings": { 20 | "enableRenovate": true, 21 | "workflowRules": { 22 | "enabled": true 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.yamllint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | rules: 4 | line-length: disable 5 | document-start: disable # standard only requires this when directives are used, which is rarely done 6 | comments: 7 | require-starting-space: false 8 | ignore-shebangs: true 9 | min-spaces-from-content: 1 10 | comments-indentation: disable 11 | 12 | truthy: 13 | allowed-values: ['true', 'false'] 14 | check-keys: false # this eliminates on: in github actions from being a failure 15 | 16 | # For all rules 17 | ignore: | 18 | .markdownlint-cli2.yaml 19 | .licenses/ 20 | docs/godocs/ 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest AS build 2 | RUN apk update && apk upgrade 3 | RUN apk add go 4 | 5 | WORKDIR /build 6 | COPY cmd/ ./cmd 7 | COPY internal/ ./internal 8 | COPY pkg/ ./pkg 9 | COPY go.mod go.sum ./ 10 | RUN go build -o dsv-injector ./cmd/injector 11 | RUN go build -o dsv-syncer ./cmd/syncer 12 | 13 | FROM alpine:latest 14 | RUN apk update && apk upgrade 15 | RUN addgroup dsv && adduser -S -G dsv dsv 16 | 17 | COPY --from=build /build/dsv-injector /build/dsv-syncer /usr/bin/ 18 | 19 | USER dsv 20 | WORKDIR /home/dsv 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Delinea Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Makefile.helpers: -------------------------------------------------------------------------------- 1 | # 2 | # Helpers - stuff that's shared between make files 3 | # Written Originally by https://github.com/cloudposse/build-harness/blob/master/Makefile.helpers, Apache License 2.0 4 | # https://github.com/cloudposse/build-harness/blob/master/LICENSE 5 | # 6 | 7 | EDITOR ?= vim 8 | 9 | SHELL = /bin/bash 10 | 11 | DEFAULT_HELP_TARGET ?= help/short 12 | HELP_FILTER ?= .* 13 | 14 | green = $(shell echo -e '\x1b[32;01m$1\x1b[0m') 15 | yellow = $(shell echo -e '\x1b[33;01m$1\x1b[0m') 16 | red = $(shell echo -e '\x1b[33;31m$1\x1b[0m') 17 | 18 | # Ensures that a variable is defined and non-empty 19 | define assert-set 20 | @$(if $($(1)),,$(error $(1) not defined in $(@))) 21 | endef 22 | 23 | # Ensures that a variable is undefined 24 | define assert-unset 25 | @$(if $($1),$(error $(1) should not be defined in $(@)),) 26 | endef 27 | 28 | warn: 29 | @printf "\e[33m🚨 make is deprecated, all automation excepting install-host for debugging have been moved to mage. Future changes should be made to mage.\e[0m\n" 30 | 31 | test/assert-set: 32 | $(call assert-set,PATH) 33 | @echo assert-set PASS 34 | 35 | test/assert-unset: 36 | $(call assert-unset,JKAHSDKJAHSDJKHASKD) 37 | @echo assert-unset PASS 38 | 39 | test/assert: test/assert-set test/assert-unset 40 | @exit 0 41 | 42 | default:: warn $(DEFAULT_HELP_TARGET) 43 | @exit 0 44 | 45 | ## Help screen 46 | help: warn 47 | @printf "Available targets:\n\n" 48 | @$(SELF) -s help/generate | grep -E "\w($(HELP_FILTER))" 49 | 50 | ## Display help for all targets 51 | help/all: warn 52 | @printf "Available targets:\n\n" 53 | @$(SELF) -s help/generate 54 | 55 | ## This help short screen 56 | help/short: warn 57 | @printf "Available targets:\n\n" 58 | @$(SELF) -s help/generate MAKEFILE_LIST="Makefile $(BUILD_HARNESS_PATH)/Makefile.helpers" 59 | 60 | # Generate help output from MAKEFILE_LIST 61 | help/generate: 62 | @awk '/^[-a-zA-Z_0-9%:\\\.\/]+:/ { \ 63 | helpMessage = match(lastLine, /^## (.*)/); \ 64 | if (helpMessage) { \ 65 | helpCommand = $$1; \ 66 | helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \ 67 | gsub("\\\\", "", helpCommand); \ 68 | gsub(":+$$", "", helpCommand); \ 69 | printf " \x1b[32;01m%-35s\x1b[0m %s\n", helpCommand, helpMessage; \ 70 | } \ 71 | } \ 72 | { lastLine = $$0 }' $(MAKEFILE_LIST) | sort -u 73 | @printf "\n" -------------------------------------------------------------------------------- /charts/dsv-injector/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/dsv-injector/Chart.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v2 3 | name: dsv-injector 4 | description: | 5 | A Helm chart for the Delinea DevOps Secrets Vault (DSV) Injector Mutating Webhook. 6 | 7 | ```shell 8 | NAMESPACE='dsv' 9 | CREDENTIALS_JSON_FILE='.cache/credentials.json' # replace with your credentials file 10 | IMAGE_REPOSITORY='docker.io/delineaxpm/dsv-k8s' 11 | IMAGE_TAG='v1.2.4' # use latest for test, but pin to a specific version for production 12 | 13 | helm install \ 14 | --namespace $NAMESPACE \ 15 | --create-namespace \ 16 | --set-file credentialsJson=${CREDENTIALS_JSON_FILE} \ 17 | --set image.repository=${IMAGE_REPOSITORY} \ 18 | --set image.tag=${IMAGE_TAG} \ 19 | --atomic \ 20 | --timeout "5m" \ 21 | --debug -v4 \ # optional for local test iteration --values .cache/charts/dsv-injector/values.yaml \ 22 | dsv-injector ./charts/dsv-injector 23 | ``` 24 | 25 | An upgrade to the existing deployment can be done with: 26 | 27 | ```shell 28 | helm upgrade \ 29 | --namespace $NAMESPACE \ 30 | --atomic \ 31 | --timeout "5m" \ 32 | --debug -v4 \ 33 | dsv-injector ./charts/dsv-injector 34 | ``` 35 | 36 | To upgrade, ensuring that the current self signed cert that is deployed by default (if not using your own CA), run the commands below to ensure the prior hook and secret are removed so they can be recreated with the newly generated cert. 37 | 38 | ```shell 39 | kubectl --namespace $NAMESPACE delete mutatingwebhookconfiguration dsv-injector \ 40 | && kubectl --namespace $NAMESPACE delete secret --timeout "5m" dsv-injector-tls \ 41 | && helm upgrade \ 42 | --namespace $NAMESPACE \ 43 | --atomic \ 44 | --timeout "5m" \ 45 | --debug -v4 \ 46 | dsv-injector ./charts/dsv-injector 47 | ``` 48 | 49 | To uninstall the deployment, you can run: 50 | 51 | ```shell 52 | helm uninstall --namespace $NAMESPACE dsv-injector 53 | ``` 54 | 55 | keywords: 56 | - Delinea 57 | - DevOps 58 | - DSV 59 | - secrets 60 | - vault 61 | type: application 62 | version: v1.2.4 63 | appVersion: latest 64 | maintainers: 65 | - name: Sheldon Hull 66 | - name: Delinea DSV Team 67 | -------------------------------------------------------------------------------- /charts/dsv-injector/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | {{- $name := (include "dsv.dnsname" .) -}} 2 | {{- $port := int .Values.webhookPort -}} 3 | {{- $uri := trimAll "/" .Values.webhookUri -}} 4 | ✨ Deployed: {{ include "dsv.fullname" . }} 5 | 6 | 📂 Namespace: {{ .Release.Namespace }} 7 | {{ if eq .Values.service.type "ExternalName" }} 8 | 🔗 The cluster will call the webhook at {{ printf "https://%s:%d/%s" .Values.externalName $port $uri }} 9 | 10 | ❗ IMPORTANT: the endpoint certificate must have Subject Alternative Name '{{ $name }}' 11 | 12 | 🔐 The certificate chain that the cluster will use to verify the webhook is: 13 | {{- $cert := .Values.caBundle | b64dec -}} 14 | {{- $certs := split "\\n" $cert -}} 15 | {{ range $cert := $certs }} 16 | {{ $cert }} 17 | {{ end }} 18 | {{ else }} 19 | 🔗 The cluster will call the webhook at {{ printf "https://%s:%d/%s" $name $port $uri }} 20 | {{ end }} 21 | 22 | {{- if .Values.configmap }} 23 | ⚙️ ConfigMap created: {{ include "dsv.fullname" . }}-configmap 24 | {{ else }} 25 | ➖ no configmap detected, defaults used for logging level and any other configmap values 26 | {{ end }} 27 | 28 | {{- if eq .Values.service.type "ExternalName" }} 29 | ℹ️ ExternalName provided for service type, so no self signed cert will be used. 30 | {{- else -}} 31 | ➕ Will generate a self signed cert with expiration of [{{ (default 365 .Values.webhookCertExpireDays | int) }}] days. 32 | {{- end -}} 33 | -------------------------------------------------------------------------------- /charts/dsv-injector/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "dsv.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "dsv.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create a DNS name i.e. a fully-qualified domain name (FQDN) for the webhook. 28 | */}} 29 | {{- define "dsv.dnsname" -}} 30 | {{- print (include "dsv.name" .) "." .Release.Namespace ".svc" -}} 31 | {{- end }} 32 | 33 | {{/* 34 | Create chart name and version as used by the chart label. 35 | */}} 36 | {{- define "dsv.chart" -}} 37 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 38 | {{- end }} 39 | 40 | {{/* 41 | Common labels 42 | */}} 43 | {{- define "dsv.labels" -}} 44 | helm.sh/chart: {{ include "dsv.chart" . }} 45 | {{ include "dsv.selectorLabels" . }} 46 | {{- if .Chart.AppVersion }} 47 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 48 | {{- end }} 49 | app.kubernetes.io/managed-by: {{ .Release.Service }} 50 | dsv-filter-name: {{ .Chart.Name }} 51 | {{- end }} 52 | 53 | {{/* 54 | Selector labels 55 | */}} 56 | {{- define "dsv.selectorLabels" -}} 57 | app.kubernetes.io/name: {{ include "dsv.name" . }} 58 | app.kubernetes.io/instance: {{ .Release.Name }} 59 | dsv-filter-name: {{ .Chart.Name }} 60 | {{- end }} 61 | -------------------------------------------------------------------------------- /charts/dsv-injector/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | {{- if .Values.configmap }} 3 | apiVersion: v1 4 | kind: ConfigMap 5 | metadata: 6 | name: {{ include "dsv.fullname" . }}-configmap 7 | data: 8 | {{- toYaml .Values.configmap | nindent 2 -}} 9 | {{- end }} -------------------------------------------------------------------------------- /charts/dsv-injector/templates/credentials-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: {{ include "dsv.fullname" . }}-credentials 5 | data: 6 | config.json: {{ .Values.credentialsJson | b64enc }} 7 | type: Opaque 8 | -------------------------------------------------------------------------------- /charts/dsv-injector/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | {{ $fullname := include "dsv.fullname" . }} 2 | {{- if ne .Values.service.type "ExternalName" -}} 3 | --- 4 | apiVersion: apps/v1 5 | kind: Deployment 6 | metadata: 7 | name: {{ .Chart.Name }} 8 | labels: 9 | {{- include "dsv.labels" . | nindent 4 }} 10 | spec: 11 | replicas: {{ .Values.replicaCount }} 12 | selector: 13 | matchLabels: 14 | {{- include "dsv.selectorLabels" . | nindent 6 }} 15 | template: 16 | metadata: 17 | {{- with .Values.podAnnotations }} 18 | annotations: 19 | checksum/last-updated: {{ now | date "2006-01-02T15:04:05Z07:00" | quote }} 20 | {{- toYaml . | nindent 8 }} 21 | {{- end }} 22 | labels: 23 | {{- include "dsv.selectorLabels" . | nindent 8 }} 24 | spec: 25 | {{- with .Values.imagePullSecrets }} 26 | imagePullSecrets: 27 | {{- toYaml . | nindent 8 }} 28 | {{- end }} 29 | containers: 30 | - name: {{ .Chart.Name }} 31 | command: [{{ .Values.image.entrypoint }}] 32 | securityContext: 33 | {{- toYaml .Values.securityContext | nindent 12 }} 34 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 35 | imagePullPolicy: {{ .Values.image.pullPolicy }} 36 | ports: 37 | - name: http 38 | containerPort: {{ .Values.containerPort }} 39 | protocol: TCP 40 | resources: 41 | {{- toYaml .Values.resources | nindent 12 }} 42 | volumeMounts: 43 | - name: cert 44 | readOnly: true 45 | mountPath: /home/nonroot/tls 46 | - name: credentials 47 | readOnly: true 48 | mountPath: /home/nonroot/credentials 49 | {{- with .Values.configmap }} 50 | envFrom: 51 | - configMapRef: 52 | name: {{ $fullname }}-configmap 53 | {{- end }} 54 | securityContext: 55 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 56 | volumes: 57 | - name: credentials 58 | secret: 59 | secretName: {{ include "dsv.name" . }}-credentials 60 | - name: cert 61 | secret: 62 | secretName: {{ include "dsv.name" . }}-tls 63 | {{- end -}} 64 | -------------------------------------------------------------------------------- /charts/dsv-injector/templates/service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ include "dsv.fullname" . }} 6 | labels: 7 | {{- include "dsv.labels" . | nindent 4 }} 8 | spec: 9 | type: {{ .Values.service.type }} 10 | {{ if eq .Values.service.type "ExternalName" }} 11 | externalName: {{ lower .Values.externalName }} 12 | {{ else }} 13 | ports: 14 | - port: {{ .Values.webhookPort }} 15 | targetPort: {{ .Values.containerPort }} 16 | selector: 17 | {{- include "dsv.selectorLabels" . | nindent 4 }} 18 | {{ end }} 19 | -------------------------------------------------------------------------------- /charts/dsv-injector/templates/webhook.yaml: -------------------------------------------------------------------------------- 1 | {{- $tlsCert := genSelfSignedCert (include "dsv.dnsname" .) nil (list (include "dsv.dnsname" .) (include "dsv.name" .)) (default 365 .Values.webhookCertExpireDays | int) -}} 2 | {{- $existingTlsSecret := lookup "v1" "Secret" .Release.Namespace (printf "%s-tls" (include "dsv.name" .)) -}} 3 | 4 | --- 5 | apiVersion: admissionregistration.k8s.io/v1 6 | kind: MutatingWebhookConfiguration 7 | metadata: 8 | name: {{ include "dsv.fullname" . }} 9 | labels: 10 | {{- include "dsv.labels" . | nindent 4 }} 11 | annotations: 12 | checksum/last-updated: {{ now | date "2006-01-02T15:04:05Z07:00" | quote }} 13 | webhooks: 14 | - name: {{ include "dsv.fullname" . }}.{{ .Release.Namespace }}.svc 15 | failurePolicy: Fail 16 | rules: 17 | - apiGroups: [""] 18 | apiVersions: ["v1"] 19 | operations: ["CREATE"] 20 | resources: ["secrets"] 21 | scope: {{ default "*" .Values.webhookScope }} 22 | clientConfig: # Secret Reference: Not supported directly by the MutatingWebhookConfiguration API. 23 | {{- if eq .Values.service.type "ExternalName" }} 24 | caBundle: {{ .Values.caBundle }} 25 | {{ else if $existingTlsSecret }} 26 | caBundle: {{ $existingTlsSecret.data.cert }} 27 | {{- else }} 28 | caBundle: {{ $tlsCert.Cert | b64enc }} 29 | {{- end }} 30 | service: 31 | namespace: {{ .Release.Namespace }} 32 | name: {{ include "dsv.name" . }} 33 | path: {{ .Values.webhookUri }} 34 | port: {{ .Values.webhookPort }} 35 | admissionReviewVersions: ["v1"] 36 | sideEffects: {{ default "None" .Values.sideEffects }} 37 | {{- if ne .Values.service.type "ExternalName" }} 38 | --- 39 | apiVersion: v1 40 | kind: Secret 41 | metadata: 42 | name: {{ include "dsv.name" . }}-tls 43 | annotations: 44 | checksum/last-updated: {{ now | date "2006-01-02T15:04:05Z07:00" | quote }} 45 | data: 46 | {{- if $existingTlsSecret }} 47 | tls.crt: {{ $existingTlsSecret.data.cert }} 48 | tls.key: {{ $existingTlsSecret.data.key }} 49 | {{- else }} 50 | tls.crt: {{ $tlsCert.Cert | b64enc }} 51 | tls.key: {{ $tlsCert.Key | b64enc }} 52 | {{- end }} 53 | type: kubernetes.io/tls 54 | {{- end }} 55 | -------------------------------------------------------------------------------- /charts/dsv-injector/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Default values for the Delina DevOps Secrets Vault (DSV) Secrets Injector 3 | # Kubernetes API Server Mutating Webhook. 4 | 5 | # -- credentialsJson contains the JSON-formatted credentials file (see README.md) 6 | # @default - placeholder. *REQUIRED FIELD* 7 | credentialsJson: >- 8 | { 9 | "default": { 10 | "credentials": { 11 | "clientId": "", 12 | "clientSecret": "" 13 | }, 14 | "tenant": "example" 15 | } 16 | } 17 | 18 | # -- replicate count 19 | # @default - 1 20 | replicaCount: 1 21 | 22 | image: 23 | repository: docker.io/delineaxpm/dsv-k8s 24 | # -- pullPolicy is the image pull policy. 25 | # If running locally built images, you'll want to set to Never to ensure local loaded images are used. 26 | # Local testing use: `Never`. 27 | # @default -- IfNotPresent 28 | pullPolicy: IfNotPresent 29 | # -- Overrides the image tag whose default is the chart appVersion. 30 | # Local Testing: Use `latest`. 31 | # @default -- the current app version/chart version 32 | tag: v1.2.4 33 | # -- Entrypoint is the path to the binary. Since the container image could contain multiple binaries, this makes sure it's correctly mapped to the binary. 34 | entrypoint: /app/dsv-injector 35 | 36 | imagePullSecrets: [] 37 | nameOverride: '' 38 | fullnameOverride: '' 39 | 40 | # -- podAnnotations 41 | # @default - Includes `dsv-filter-name` for easier log selector filter. 42 | podAnnotations: {} 43 | 44 | podSecurityContext: 45 | {} 46 | # fsGroup: 2000 47 | 48 | # -- securityContext is the security context for the controller. 49 | # This uses chainguard static nonroot based image. 50 | # Reference: https://edu.chainguard.dev/chainguard/chainguard-images/reference/static/overview/ 51 | securityContext: 52 | # -- readOnlyRootFilesystem is the read only root file system flag. 53 | # @default -- true 54 | readOnlyRootFilesystem: true 55 | # -- runAsNonRoot is the run as non root flag. 56 | # @default -- true 57 | runAsNonRoot: true 58 | # -- runAsUser is the run as user. 59 | # @default -- 65532 (from chainguard static image) 60 | runAsUser: 65532 61 | # -- runAsGroup is the run as group. 62 | # @default -- 65532 (from chainguard static image) 63 | runAsGroup: 65532 64 | # capabilities: 65 | # drop: 66 | # - ALL 67 | # readOnlyRootFilesystem: true 68 | # runAsNonRoot: true 69 | # runAsUser: 1000 70 | 71 | service: 72 | # -- Default port for the injector webhook service. 73 | # @default -- port 8543 74 | port: 8543 75 | 76 | # -- ClusterIP is typical when the webhook is running as a POD 77 | # However, it can also be hosted externally, which is useful for debugging, by providing the following instead: 78 | # type: ExternalName 79 | # externalName: my.fqdn 80 | # So long as: 81 | # - my.fqdn hosts an HTTPS endpoint on port {webhookPort} that answers URI {webhookUri} 82 | # - the certificate must have a Subject Alternative Name for {name}.{namespace}.{svc}, e.g., dsv-injector.dsv.svc 83 | # - the caBundle must be a base64 string containing a PEM-encoded certificate chain that validates the certifcate 84 | # caBundle: ... 85 | type: ClusterIP 86 | 87 | # -- We usually recommend not to specify default resources and to leave this as a conscious 88 | # choice for the user. This also increases chances charts run on environments with little 89 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 90 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 91 | # @default -- No default values, user must specify to set resource limits. 92 | resources: {} 93 | # limits: 94 | # cpu: 100m 95 | # memory: 128Mi 96 | # requests: 97 | # cpu: 100m 98 | # memory: 128Mi 99 | 100 | # -- webhookUri is path portion of the URL of the webhook endpoint 101 | webhookUri: /inject 102 | # -- webhookPort is the port that the webhook endpoint is listening on 103 | # @default -- 8543 104 | webhookPort: 8543 105 | # -- webhookScope specifies which resources are in scope, "Cluster", "Namespaced" or "*" 106 | # @default -- "Namespaced" 107 | webhookScope: Namespaced 108 | # -- containerPort is the port that the container itself listens on 109 | containerPort: 18543 110 | 111 | # -- configmap are configuration values for the app to load. 112 | # All of these are defaulted in the template itself and only need be set if adjusting. 113 | # Since the user for the container is nonroot, only edit if you know what you are doing. 114 | # Boolean values should be passed quoted to avoid issues. 115 | # @default -- {} empty. 116 | configmap: 117 | {} 118 | # DSV_CERT: 119 | # DSV_KEY: 120 | # DSV_CREDENTIALS_JSON: 121 | # DSV_SERVER_ADDRESS: 122 | # DSV_DEBUG: 'true' # Warning: if passing boolean, use quoted string to avoid issues 123 | 124 | # -- webhookCertExpireDays specifies the number of days before the webhook certificate expires 125 | # @default -- 365 126 | webhookCertExpireDays: 365 127 | -------------------------------------------------------------------------------- /charts/dsv-syncer/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/dsv-syncer/Chart.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v2 3 | name: dsv-syncer 4 | description: | 5 | A Helm chart for the Delinea DevOps Secrets Vault (DSV) Kubernetes Synchronizer Job. 6 | 7 | ```shell 8 | NAMESPACE='dsv' 9 | IMAGE_REPOSITORY='docker.io/delineaxpm/dsv-k8s' 10 | IMAGE_TAG='v1.2.4' # use latest for test, but pin to a specific version for production 11 | 12 | helm install \ 13 | --namespace $NAMESPACE \ 14 | --create-namespace \ 15 | --set image.repository=${IMAGE_REPOSITORY} \ 16 | --set image.tag=${IMAGE_TAG} \ 17 | --atomic \ 18 | --timeout "5m" \ 19 | --debug \ # optional --values --values .cache/charts/dsv-injector/values.yaml \ 20 | dsv-syncer ./charts/dsv-syncer 21 | ``` 22 | keywords: 23 | - Delinea 24 | - DevOps 25 | - DSV 26 | - secrets 27 | - vault 28 | type: application 29 | version: v1.2.4 30 | appVersion: latest 31 | maintainers: 32 | - name: Sheldon Hull 33 | - name: Delinea DSV Team 34 | -------------------------------------------------------------------------------- /charts/dsv-syncer/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | ✨ Deployed: {{ include "dsv.fullname" . }} 2 | 3 | 📂 Namespace: {{ .Release.Namespace }} 4 | 5 | {{- if .Values.configmap }} 6 | ⚙️ ConfigMap created: {{ include "dsv.fullname" . }}-configmap 7 | {{ else }} 8 | ➖ no configmap detected, defaults used for logging level and any other configmap values 9 | {{ end }} 10 | -------------------------------------------------------------------------------- /charts/dsv-syncer/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "dsv.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "dsv.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "dsv.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "dsv.labels" -}} 37 | helm.sh/chart: {{ include "dsv.chart" . }} 38 | {{- if .Chart.AppVersion }} 39 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 40 | {{- end }} 41 | app.kubernetes.io/managed-by: {{ .Release.Service }} 42 | dsv-filter-name: {{ .Chart.Name }} 43 | {{- end }} 44 | 45 | {{/* 46 | Create the name of the service account to use 47 | */}} 48 | {{- define "dsv.serviceAccountName" -}} 49 | {{- if .Values.serviceAccount.create }} 50 | {{- default (include "dsv.fullname" .) .Values.serviceAccount.name }} 51 | {{- else }} 52 | {{- default "default" .Values.serviceAccount.name }} 53 | {{- end }} 54 | {{- end }} 55 | 56 | {{/* 57 | DSV Syncing Labels for cronjob 58 | - Required to be able to easily follow with cron job continuing to have random suffix 59 | */}} 60 | {{- define "cronjob.labels" -}} 61 | dsv-filter-name: {{ .Chart.Name }} 62 | {{- end }} 63 | -------------------------------------------------------------------------------- /charts/dsv-syncer/templates/cluster-role-binding.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRoleBinding 4 | metadata: 5 | name: {{ include "dsv.fullname" . }} 6 | labels: 7 | {{- include "dsv.labels" . | nindent 4 }} 8 | subjects: 9 | - kind: ServiceAccount 10 | name: {{ include "dsv.serviceAccountName" . }} 11 | namespace: {{ .Release.Namespace }} 12 | roleRef: 13 | kind: ClusterRole 14 | name: {{ include "dsv.fullname" . }} 15 | apiGroup: rbac.authorization.k8s.io 16 | -------------------------------------------------------------------------------- /charts/dsv-syncer/templates/cluster-role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: {{ include "dsv.fullname" . }} 6 | labels: 7 | {{- include "dsv.labels" . | nindent 4 }} 8 | rules: 9 | - apiGroups: [""] 10 | resources: ["secrets"] 11 | verbs: ["list", "patch"] 12 | -------------------------------------------------------------------------------- /charts/dsv-syncer/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | {{- if .Values.configmap }} 3 | apiVersion: v1 4 | kind: ConfigMap 5 | metadata: 6 | name: {{ include "dsv.fullname" . }}-configmap 7 | data: 8 | {{- toYaml .Values.configmap | nindent 2 -}} 9 | {{- end }} -------------------------------------------------------------------------------- /charts/dsv-syncer/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "dsv.serviceAccountName" . }} 6 | labels: 7 | {{- include "dsv.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /charts/dsv-syncer/templates/syncer-cronjob.yaml: -------------------------------------------------------------------------------- 1 | {{ $fullname := include "dsv.fullname" . }} 2 | {{- if .Capabilities.APIVersions.Has "batch/v1/CronJob" -}} 3 | apiVersion: batch/v1 4 | {{- else -}} 5 | apiVersion: batch/v1beta1 6 | {{- end }} 7 | kind: CronJob 8 | metadata: 9 | name: {{ .Chart.Name }} 10 | labels: 11 | {{- include "dsv.labels" . | nindent 4 }} 12 | spec: 13 | schedule: {{ quote .Values.cronJobSchedule }} 14 | jobTemplate: 15 | spec: 16 | template: 17 | metadata: 18 | labels: 19 | {{- include "cronjob.labels" . | nindent 14 }} 20 | spec: 21 | containers: 22 | - name: {{ .Chart.Name }} 23 | command: [{{ .Values.image.entrypoint }}] 24 | resources: 25 | {{- toYaml .Values.resources | nindent 14 }} 26 | securityContext: 27 | {{- toYaml .Values.securityContext | nindent 14 }} 28 | image: {{ quote (printf "%s:%s" .Values.image.repository (.Values.image.tag | default .Chart.AppVersion)) }} 29 | imagePullPolicy: {{ .Values.image.pullPolicy }} 30 | volumeMounts: 31 | - name: credentials 32 | readOnly: true 33 | mountPath: /home/nonroot/credentials 34 | {{- with .Values.configmap }} 35 | envFrom: 36 | - configMapRef: 37 | name: {{ $fullname }}-configmap 38 | {{- end }} 39 | restartPolicy: OnFailure 40 | serviceAccountName: {{ include "dsv.serviceAccountName" . }} 41 | securityContext: 42 | {{- toYaml .Values.podSecurityContext | nindent 12 }} 43 | volumes: 44 | - name: credentials 45 | secret: 46 | secretName: {{ .Values.dsvInjectorCredentialsSecretName }} 47 | -------------------------------------------------------------------------------- /charts/dsv-syncer/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Default values for the Delina DevOps Secrets Vault (DSV) Secrets Injector 3 | # Kubernetes API Server Mutating Webhook. 4 | # This is a YAML-formatted file. 5 | # Declare variables to be passed into your templates. 6 | 7 | # -- replicaCount 8 | # @default - 1 9 | replicaCount: 1 10 | 11 | image: 12 | repository: docker.io/delineaxpm/dsv-k8s 13 | # -- pullPolicy is the image pull policy. 14 | # If running locally built images, you'll want to set to Never to ensure local loaded images are used. 15 | # Local testing use: `Never`. 16 | # @default -- IfNotPresent 17 | pullPolicy: IfNotPresent 18 | # -- Overrides the image tag whose default is the chart appVersion. 19 | # Local Testing: Use `latest`. 20 | # @default -- the current app version/chart version 21 | tag: v1.2.4 22 | # -- Entrypoint is the path to the binary. Since the container image could contain multiple binaries, this makes sure it's correctly mapped to the binary. 23 | entrypoint: /app/dsv-syncer 24 | args: ['-environment', 'foo'] 25 | 26 | imagePullSecrets: [] 27 | nameOverride: '' 28 | fullnameOverride: '' 29 | 30 | serviceAccount: 31 | # -- Specifies whether a service account should be created 32 | # @default - true 33 | create: true 34 | # -- Annotations to add to the service account 35 | # @default - Adds `dsv-filter-name` to simplify log selector streaming 36 | annotations: {} 37 | # -- The name of the service account to use. 38 | # -- If not set and create is true, a name is generated using the fullname template 39 | name: '' 40 | 41 | # -- default annotations to add 42 | # @default - Adds `dsv-filter-name` to simplify log selector streaming 43 | podAnnotations: {} 44 | 45 | podSecurityContext: 46 | {} 47 | # fsGroup: 2000 48 | 49 | # -- securityContext is the security context for the controller. 50 | # This uses chainguard static nonroot based image. 51 | # Reference: https://edu.chainguard.dev/chainguard/chainguard-images/reference/static/overview/ 52 | securityContext: 53 | # -- readOnlyRootFilesystem is the read only root file system flag. 54 | # @default -- true 55 | readOnlyRootFilesystem: true 56 | # -- runAsNonRoot is the run as non root flag. 57 | # @default -- true 58 | runAsNonRoot: true 59 | # -- runAsUser is the run as user. 60 | # @default -- 65532 (from chainguard static image) 61 | runAsUser: 65532 62 | # -- runAsGroup is the run as group. 63 | # @default -- 65532 (from chainguard static image) 64 | runAsGroup: 65532 65 | # capabilities: 66 | # drop: 67 | # - ALL 68 | # readOnlyRootFilesystem: true 69 | # runAsNonRoot: true 70 | # runAsUser: 1000 71 | 72 | # -- We usually recommend not to specify default resources and to leave this as a conscious 73 | # choice for the user. This also increases chances charts run on environments with little 74 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 75 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 76 | # @default -- No default values, user must specify to set resource limits. 77 | resources: 78 | {} 79 | # limits: 80 | # cpu: 100m 81 | # memory: 128Mi 82 | # requests: 83 | # cpu: 100m 84 | # memory: 128Mi 85 | 86 | # -- dsvInjectorCredentialsSecretName is the name of thecredentialsJson secret from the dsv-injector 87 | dsvInjectorCredentialsSecretName: dsv-injector-credentials #checkov:skip=CKV_SECRET_6: this is a secret name and not an embedded secret 88 | 89 | # -- cronJobSchedule controls when the syncer runs; five asterisks means "every minute". 90 | # See [cronjob](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#cron-schedule-syntax) 91 | # @default - every minute, ie '* * * * *' 92 | cronJobSchedule: '* * * * *' 93 | 94 | # -- configmap are configuration values for the app to load. 95 | # All of these are defaulted in the template itself and only need be set if adjusting. 96 | # Since the user for the container is nonroot, only edit if you know what you are doing. 97 | # Boolean values should be passed quoted to avoid issues. 98 | # @default -- {} empty. 99 | configmap: 100 | {} 101 | # DSV_DEBUG: true # Warning: if passing boolean, use quoted string to avoid issues 102 | -------------------------------------------------------------------------------- /cmd/syncer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "github.com/DelineaXPM/dsv-k8s/v2/internal/logger" 9 | "github.com/DelineaXPM/dsv-k8s/v2/pkg/config" 10 | "github.com/DelineaXPM/dsv-k8s/v2/pkg/syncer" 11 | "github.com/rs/zerolog" 12 | 13 | "k8s.io/client-go/rest" 14 | "k8s.io/client-go/tools/clientcmd" 15 | 16 | env "github.com/caarlos0/env/v6" 17 | ) 18 | 19 | const ( 20 | // ExitFailure is exit code sent for failed task. 21 | exitFailure = 1 22 | // ExitSuccess is exit code sent for running without any error. 23 | exitSuccess = 0 24 | ) 25 | 26 | // main is the entrypoint for the syncer; parses the credentials file and calls syncer.Sync 27 | func main() { 28 | if err := Run(os.Args); err != nil { 29 | fmt.Fprintf(os.Stderr, "%s\n", err) 30 | os.Exit(exitFailure) 31 | } 32 | os.Exit(exitSuccess) // shouldn't hit this if run is invoked correctly 33 | } 34 | 35 | //nolint:gochecknoglobals // ok for providing as version output 36 | var ( 37 | // Version is the descriptive version, normally the tag from which the app was built. 38 | // Since git tags can be changed, use Commit instead as the most accurate version. 39 | version = "dev" 40 | // Commit is the git commit hash that the build was generated from. 41 | commit = "none" 42 | // Date is the date the binary was produced. 43 | date = "unknown" 44 | // buildName is the build name for easier confirmation on local builds that a build has changed. 45 | buildName = "unknown" 46 | ) 47 | 48 | // Run contains the actual invocation code for the syncer and is public to allow running integration tests with it. 49 | func Run(args []string) error { //nolint:funlen // ok for Run 50 | log := logger.New() 51 | log.Info(). 52 | Str("version", version). 53 | Str("commit", commit). 54 | Str("date", date). 55 | Str("buildName", buildName). 56 | Msg("syncer version information") 57 | 58 | // Config is the configuration for the syncer. 59 | // This is provided by environment variables. 60 | type Config struct { 61 | Namespace string `env:"DSV_NAMESPACE"` // DSV_NAMESPACE is the namespace for secrets to sync. "" (the default) by default includes all namespaces. 62 | Debug bool `env:"DSV_DEBUG" envDefault:"false"` // Debug enables debug logging. 63 | KubeConfig string `env:"KUBECONFIG" envDefault:"${HOME}/.kube/config" envExpand:"true"` // KubeConfig is the path to the kubeconfig file and only required if running locally. By default the connection will be built as "incluster". 64 | CredentialsJSONFile string `env:"DSV_CREDENTIALS_JSON" envDefault:"${HOME}/credentials/config.json" envExpand:"true"` // CredentialsJSONFile is the path to the JSON formatted credentials file that is mounted as a secret. 65 | } 66 | 67 | cfg := Config{} 68 | err := env.Parse(&cfg) 69 | if err != nil { 70 | log.Error().Err(err).Msg("unable to parse environment variables") 71 | } 72 | log.Info().Strs("args", args).Msg("starting syncer, args passed, but not used, as environment variables are used instead") 73 | if cfg.Debug { 74 | zerolog.SetGlobalLevel(zerolog.DebugLevel) 75 | log.Info().Msg("debug logging enabled") 76 | } 77 | 78 | credentials, err := config.GetCredentials(cfg.CredentialsJSONFile) 79 | if err != nil { 80 | log.Fatal().Msgf("[ERROR] unable to process configuration file '%s': %s", cfg.CredentialsJSONFile, err) 81 | return fmt.Errorf("unable to process configuration file %q: %w", cfg.CredentialsJSONFile, err) 82 | } 83 | log.Info(). 84 | Strs("credential_list", credentials.Names()). 85 | Int("credential_count", len(*credentials)). 86 | Msg("success loading credential sets") 87 | 88 | start := time.Now() 89 | if err := syncer.Sync( 90 | func(namespace string) (rconfig *rest.Config, err error) { 91 | rconfig, err = rest.InClusterConfig() 92 | if err != nil { 93 | log.Debug().Msg("unable to get InClusterConfig, falling back to KubeConfig") 94 | 95 | rconfig, err = clientcmd.BuildConfigFromFlags("", cfg.KubeConfig) 96 | if err != nil { 97 | log.Error().Err(err).Msg("error getting Kubernetes Client rest.Config") 98 | return nil, fmt.Errorf("error getting Kubernetes Client rest.Config: %w", err) 99 | } 100 | return rconfig, nil 101 | } 102 | return rconfig, nil 103 | }, 104 | cfg.Namespace, 105 | *credentials, 106 | log, 107 | ); err != nil { 108 | log.Fatal().Msgf("[ERROR] unable to sync Secrets: %s", err) 109 | } 110 | log.Info().Dur("duration", time.Since(start)).Msg("syncer processing complete") 111 | return nil 112 | } 113 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | --- 2 | codecov: 3 | require_ci_to_pass: true 4 | comment: 5 | require_changes: true # if true: only post the comment if coverage changes 6 | coverage: 7 | status: 8 | project: 9 | default: 10 | informational: true 11 | target: auto # auto compares coverage to the previous base commit 12 | patch: 13 | default: 14 | informational: true 15 | target: auto # auto compares coverage to the previous base commit 16 | # sample regex patterns 17 | ignore: 18 | - 'magefiles' 19 | - 'examples' 20 | - '.trunk' 21 | - '.vscode' 22 | - '.devcontainer' 23 | - 'vendor' 24 | -------------------------------------------------------------------------------- /docker/Dockerfile.chainguard: -------------------------------------------------------------------------------- 1 | 2 | 3 | # https://goreleaser.com/customization/docker/?h=scrat#how-it-works 4 | # trunk-ignore(hadolint/DL3007) 5 | FROM cgr.dev/chainguard/static:latest 6 | COPY dsv-syncer /app/dsv-syncer 7 | COPY dsv-injector /app/dsv-injector 8 | -------------------------------------------------------------------------------- /docker/Dockerfile.distroless: -------------------------------------------------------------------------------- 1 | # Why this base image? 2 | # FROM: https://github.com/GoogleContainerTools/distroless/blob/main/base/README.md 3 | # "Distroless" images contain only your application and its runtime dependencies. 4 | # They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution. 5 | 6 | # Restricting what's in your runtime container to precisely what's necessary for your app is a best practice employed by Google and other tech giants that have used containers in production for many years. 7 | # It improves the signal to noise of scanners (e.g. CVE) and reduces the burden of establishing provenance to just what you need. 8 | 9 | # Distroless images are very small. The smallest distroless image, gcr.io/distroless/static-debian11, is around 2 MiB. 10 | # That's about 50% of the size of alpine (~5 MiB), and less than 2% of the size of debian (124 MiB). 11 | 12 | # This image contains a minimal Linux, glibc-based system. It is intended for use directly by "mostly-statically compiled" languages like Go, Rust or D. 13 | # Statically compiled applications (Go) that do not require libc can use the gcr.io/distroless/static image, which contains: 14 | 15 | # ca-certificates 16 | # A /etc/passwd entry for a root user 17 | # A /tmp directory 18 | # tzdata 19 | # Most other applications (and Go apps that require libc/cgo) should start with gcr.io/distroless/base, which contains all of the packages in gcr.io/distroless/static, and 20 | # glibc 21 | # libssl 22 | # openssl 23 | 24 | # To debug: change tag to debug instead of nonroot, then 25 | # run `docker run --rm -it --entrypoint=sh dev.local/dsv-repo-template` and take a look in the image. 26 | # move back to nonroot when done. 27 | # nonroot won't work for github actions 28 | # https://docs.github.com/en/actions/creating-actions/dockerfile-support-for-github-actions#user this was for dockerfile approach, not docker image but it worked well this way. 29 | 30 | 31 | # If you change the copy location, you'll want to make sure to change the path in the helm charts and publish a new version. 32 | FROM gcr.io/distroless/static:nonroot 33 | COPY dsv-syncer /app/dsv-syncer 34 | COPY dsv-injector /app/dsv-injector 35 | -------------------------------------------------------------------------------- /docker/Dockerfile.scratch: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | ENTRYPOINT ["/dsv-repo-template"] 3 | COPY dsv-repo-template / 4 | -------------------------------------------------------------------------------- /docs/assets/info-markup-default-creds.svg: -------------------------------------------------------------------------------- 1 |
The injector uses the default credentials when mutating a Kubernetes Secret without a credentialAnnotation.
-------------------------------------------------------------------------------- /docs/assets/random-dont-need-to-install.svg: -------------------------------------------------------------------------------- 1 |
Save Yourself Some Work
The image is on Docker Hub. You don't have to build and run this locally to use the helm charts.
-------------------------------------------------------------------------------- /docs/assets/warning-app1-required-for-tests.svg: -------------------------------------------------------------------------------- 1 |
The tests are hardcoded to expect app1, so ensure this is configured for the local tests.
-------------------------------------------------------------------------------- /docs/configure.md: -------------------------------------------------------------------------------- 1 | # Configure 2 | 3 | This focuses on the DSV configuration required to use with Kubernetes. 4 | This applies to both local testing Kubernetes and your own seperate cluster. 5 | 6 | ## Help Getting Started 7 | 8 | Run `mage dsv:setupdsv` to create the required DSV configuration for testing. 9 | This requires you to have already run `dsv init` in the project and runs against the profile you specified in `.env`. 10 | You should ensure `direnv allow` has been run and the `.env` file is loaded. 11 | Your `zsh` terminal should warn you if you didn't create the `.env` file. 12 | 13 | The order: 14 | 15 | - `mage dsv:setupdsv` 16 | - `mage dsv:createsecret` 17 | - `mage dsv:convertClientToCredentials` 18 | 19 | To tear down and recreate with new secret, just run `mage dsv:destroy` 20 | 21 | ## Manually Creating (Prior Method Before Automation) 22 | 23 | ### JSON Credentials for Helm Install 24 | 25 | The configuration requires a JSON formatted list of Client Credential and Tenant mappings. 26 | 27 | The name of the credential (such as `app1` or `default`) is used for matching the annontated credential to the right credentials file to use to connect to the connect tenant. 28 | 29 | > [!WARNING] 30 | > If you have a different top level domain, for example `eu` instead of `.com` then look at the [section on supporting alternative tld for guidance](troubleshooting.md#supporting-alternative-tld). 31 | 32 | You can place your temporary config in `.cache/credentials.json` as this is ignored by git, so that you can run the helm install command manually if you aren't doing local development. 33 | 34 | 35 | 36 | ```json 37 | { 38 | "app1": { 39 | "credentials": { 40 | "clientId": "", 41 | "clientSecret": "xxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxx-xxxxx" 42 | }, 43 | "tenant": "mytenant" 44 | }, 45 | "default": { 46 | "credentials": { 47 | "clientId": "", 48 | "clientSecret": "xxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxx-xxxxx" 49 | }, 50 | "tenant": "mytenant" 51 | } 52 | } 53 | ``` 54 | 55 | ### AZURE Entra authentication 56 | 57 | To use Azure Entra authentication add the "tenantId" and "Provider" parameters. 58 | For more information on how to set up and configure DSV for use with Azure Entra authentication 59 | [dsv-sdk-go documentation](https://github.com/DelineaXPM/dsv-sdk-go/tree/main/example/azure) 60 | 61 | ```json 62 | { 63 | "default": { 64 | "credentials": { 65 | "clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", 66 | "clientSecret": "zzzz~zzzzzz.zzzzzzzzzzzzz~zzzzzzzzzzz" 67 | }, 68 | "tenant": "youTenantName", 69 | "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", 70 | "Provider": 3 71 | } 72 | } 73 | ``` 74 | 75 | ### Update Manifests 76 | 77 | This would be referenced by a Kubernetes secret with annotations like: 78 | 79 | ```yaml 80 | --- 81 | apiVersion: v1 82 | kind: Secret 83 | metadata: 84 | name: user-domain-pass 85 | annotations: 86 | dsv.delinea.com/credentials: app1 87 | dsv.delinea.com/set-secret: 'tests:dsv-k8s' 88 | ``` 89 | 90 | If using the provided examples, you can edit: `.cache/manifests` and adjust the secrets to map. 91 | You can use all of the provided manifests to test the different behavior, or just deploy one if desired. 92 | 93 | ## Configuring Credentials in Kubernetes To Talk to DSV 94 | 95 | ## Configuring DSV 96 | 97 | The following is an example of the steps to setup for testing, but can be modified to support your use case. 98 | 99 | Create the role that will allow creating a client for programmatic access 100 | 101 | ```shell 102 | dsv role create --name 'k8s' --desc 'test profile for k8s' 103 | dsv secret create --path 'tests:dsv-k8s' --data '{"password": "admin","username": "admin"}' 104 | ``` 105 | 106 | Create a policy that allows the local user to read the secret, modify this to the correct user/group mapping: 107 | 108 | ```shell 109 | dsv policy create -- actions 'read' --path 'secrets:k8s' --desc 'test access to secret' --resources 'secrets:k8s:<.*>' --subjects 'roles:k8s' 110 | dsv client create --role k8s 111 | ``` 112 | -------------------------------------------------------------------------------- /docs/devcontainer.md: -------------------------------------------------------------------------------- 1 | # Devcontainer 2 | 3 | ## Codespaces 4 | 5 | Use the GitHub cli to create the codespace or do this from the browser. 6 | Ideally, use minimum of 8gb ram configuration or you'll run into issues with nested Kubernetes setup. 7 | 8 | Example invocation: 9 | 10 | ```shell 11 | gh codespace create \ 12 | --display-name dsv-k8s \ 13 | --machine 'standardLinux32gb' \ 14 | --repo DelineaXPM/dsv-k8s \ 15 | --status \ 16 | --retention-period "5d" 17 | ``` 18 | 19 | ## Prerequisites 20 | 21 | - Docker 22 | - Visual Studio Code 23 | 24 | ## I'm starting from scratch 25 | 26 | > **_NOTE_** 27 | > Docker is left out of these directions, just install that from [Docker Desktop](https://www.docker.com/products/docker-desktop/) site. 28 | 29 | ### Windows 30 | 31 | - [Install chocolatey (package manager for Windows)](https://chocolatey.org/install#individual) (provides single line command to run). 32 | - Run `choco install vscode -y` 33 | 34 | ### MacOS 35 | 36 | - [Homebrew](https://brew.sh/) 37 | 38 | - Run `brew install visual-studio-code` 39 | 40 | ### Linux 41 | 42 | - You'll have to install the apps manually. 43 | 44 | ### After You've Setup VSCode 45 | 46 | Run `code --install-extension ms-vscode-remote.remote-containers` 47 | 48 | - For supporting Codespaces: `code --install-extension GitHub.codespaces` 49 | 50 | ## I already use devcontainers 51 | 52 | - Ensure you've got Remote Containers or Codespace extension installed as mentioned in directions above and you'll be good to start. 53 | 54 | ## Spin It Up 55 | 56 | > **_NOTE_** 57 | > 58 | > 🐎 PERFORMANCE TIP: Using the directions provided for named container volume will optimize performance over trying to just "open in container" as there is no mounting files to your local filesystem. 59 | 60 | Use command pallet with vscode (Control+Shift+P or F1) and type to find the command `Remote Containers: Clone Repository in Named Container`. 61 | 62 | - Put the git clone url in, for example: `https://github.com/DelineaXPM/dsv-k8s.git` 63 | - Name the volume and directory both dsv-k8s or whatever you prefer. 64 | 65 | > **Note** 66 | > This is a large development image (10GB). The first time you run this it will take a while. 67 | > However, after this first run, rebuilding the container to start over should be minimal time, as you'll have the majority of Docker image cached locally. 68 | 69 | This includes (for updated info just look at dockerfile): 70 | 71 | - Embedded docker 72 | - Embedded Kind/Minikube (kubernetes) 73 | - Go 74 | - Dotnet 75 | - Python 76 | - Node 77 | - Go tools for linting, formatting, and testing. 78 | - Extensions for VSCode defined in `.devcontainers`, such as Go, Kubernetes & Docker, and some others. 79 | - Initial placeholder `.zshrc` file included to help initialize usage of `direnv` for automatically loading default `.envrc` which contains local developement default environment variables. 80 | 81 | ### After Devcontainer Loads 82 | 83 | 1. Accept "Install Recommended Extensions" from popup, to automatically get all the preset tools, such as Kubernetes, Go and others setup. 84 | 1. Open a new `zsh-login` terminal and allow the automatic setup to finish, as this will ensure all other required tools are setup. 85 | - Make sure to run `direnv allow` as it prompts you, to ensure all project and your personal environment variables (optional). 86 | 1. Make sure Go 1.19 is the correct version running with `go version`. 87 | 1. If it's not, run `sudo .devcontainer/library-scripts/go-debian.sh "1.19"` 88 | 1. Run setup task: 89 | - Using CLI: Run `mage init` 90 | -------------------------------------------------------------------------------- /docs/developer-debugging.md: -------------------------------------------------------------------------------- 1 | # Developer Debugging 2 | 3 | This documentation comes from the original `make` driven build process. 4 | It hasn't been migrated to `mage` and will be noted here unless deprecated. 5 | 6 | ## Host (for debugging) 7 | 8 | Per above, typically, the injector runs as a POD in the cluster but running it on the host makes debugging easier. 9 | 10 | ```sh 11 | make install-host EXTERNAL_NAME=laptop.mywifi.net CA_BUNDLE=$(cat /path/to/ca.crt | base64 -w0 -) 12 | ``` 13 | 14 | For it to work: 15 | 16 | - The certificate that the injector presents must validate against the `$(CA_BUNDLE)`. 17 | - The certificate must also have a Subject Alternative Name for `$(INJECTOR_NAME).$(NAMESPACE).svc`. 18 | By default that's `dsv-injector.dsv.svc`. 19 | 20 | - The `$(EXTERNAL_NAME)` is a required argument, and the name itself must be resolvable _inside_ the cluster. 21 | **localhost will not work**. 22 | 23 | If the `$(CA_BUNDLE)` is argument is omitted, `make` will attempt to extract it from `kubectl config`: 24 | 25 | ```make 26 | install-host: CA_BUNDLE_KUBE_CONFIG_INDEX = 0 27 | install-host: CA_BUNDLE_JSON_PATH = {.clusters[$(CA_BUNDLE_KUBE_CONFIG_INDEX)].cluster.certificate-authority-data} 28 | install-host: CA_BUNDLE=$(shell $(KUBECTL) config view --raw -o jsonpath='$(CA_BUNDLE_JSON_PATH)' | tr -d '"') 29 | 30 | ``` 31 | 32 | which will make: 33 | 34 | ```sh 35 | kubectl config view --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | tr -d '"' 36 | ``` 37 | 38 | Optionally set `$(CA_BUNDLE_KUBE_CONFIG_INDEX)` to use `1`, to use the second cluster in your configuration, 39 | `2` for the third and so on. 40 | ℹ️ All this assumes that the injector uses a certificate signed by the cluster CA. 41 | There are several options like [cert-manager](https://cert-manager.io/) 42 | for getting cluster-signed certs, however, 43 | this simple [bash script](https://gist.github.com/amigus/b4e6e642f88e756be1996e44a1c35349) 44 | will request and grant a suitable certificate from the cluster using cURL and OpenSSL. 45 | To use it: 46 | 47 | ```sh 48 | get_k8s_cert.sh -n dsv-injector -N dsv 49 | ``` 50 | 51 | Now run it: 52 | 53 | ```sh 54 | ./dsv-injector -cert ./dsv-injector.pem -key ./dsv-injector.key -credentials ./configs/credentials.json -address :8543 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/developer-reference.md: -------------------------------------------------------------------------------- 1 | # Devcontainer Based Setup 2 | 3 | - [Devcontainer Based Setup](#devcontainer-based-setup) 4 | - [Troubleshooting](#troubleshooting) 5 | - [Error With Permissions On Go Directories](#error-with-permissions-on-go-directories) 6 | - [Mismatch With Checksum for Go Modules](#mismatch-with-checksum-for-go-modules) 7 | - [Connecting to Services Outside of devcontainer](#connecting-to-services-outside-of-devcontainer) 8 | 9 | ## Troubleshooting 10 | 11 | ## Error With Permissions On Go Directories 12 | 13 | Clear the directories with `rm -rf /home/vscode/go` and then try `mage init` to redownload packages. 14 | 15 | > Known issue: Haven't figured out why this is being set incorrectly yet 16 | 17 | ### Mismatch With Checksum for Go Modules 18 | 19 | - Run `go clean -modcache && go mod tidy`. 20 | 21 | ### Connecting to Services Outside of devcontainer 22 | 23 | You are in an isolated, self-contained Docker setup. 24 | The ports internally aren't the same as externally in your host OS. 25 | If the port forward isn't discovered automatically, enable it yourself, by using the port forward tab (next to the terminal tab). 26 | 27 | 1. You should see a port forward once the services are up (next to the terminal button in the bottom pane). 28 | 1. If the click to open url doesn't work, try accessing the path manually, and ensure it is `https`. 29 | Example: `https://127.0.0.1:9999` 30 | 31 | You can choose the external port to access, or even click on it in the tab and it will open in your host for you. 32 | -------------------------------------------------------------------------------- /docs/helm-install.md: -------------------------------------------------------------------------------- 1 | ## Helm Install 2 | 3 | Installation of charts into the a cluster requires [Helm](https://helm.sh). 4 | 5 | There are two separate charts for the `dsv-injector` and the `dsv-syncer`. 6 | 7 | - The `dsv-injector` chart imports `credentials.json` from the filesystem and stores it in a Kubernetes Secret. 8 | - The `dsv-syncer` chart refers to that Secret _instead of creating its own_. 9 | 10 | See [configure](configure.md#json-credentials-for-helm-install) 11 | 12 | ```shell 13 | NAMESPACE='testing' 14 | CREDENTIALS_JSON_FILE='.cache/credentials.json' 15 | IMAGE_REPOSITORY='docker.io/delineaxpm/dsv-k8s' 16 | 17 | helm install 18 | --namespace $NAMESPACE 19 | --create-namespace \ 20 | --set-file credentialsJson=${CREDENTIALS_JSON_FILE} \ 21 | --set image.repository=${IMAGE_REPOSITORY} \ 22 | dsv-injector ./charts/dsv-injector 23 | 24 | helm install 25 | --namespace $NAMESPACE 26 | --create-namespace \ 27 | --set-file credentialsJson=${CREDENTIALS_JSON_FILE} \ 28 | --set image.repository=${IMAGE_REPOSITORY} \ 29 | dsv-syncer ./charts/dsv-syncer 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/local-cli-invoke.md: -------------------------------------------------------------------------------- 1 | # Local CLI Invoke 2 | 3 | The cli can be invoked locally. 4 | 5 | The injector uses the HTTPS server built-in to the Golang [http](https://pkg.go.dev/net/http) 6 | package to host the Kubernetes Mutating Webhook Webservice. 7 | 8 | ```bash 9 | $ ./dsv-injector -h 10 | Usage of ./dsv-injector: 11 | -address string 12 | the address to listen on, e.g., 'localhost:8080' or ':8443' (default ":18543") 13 | -cert string 14 | the path of the public certificate file in PEM format (default "tls/cert.pem") 15 | -credentials string 16 | the path of JSON formatted credentials file (default "credentials/config.json") 17 | -key string 18 | the path of the private key file in PEM format (default "tls/key.pem") 19 | ``` 20 | 21 | Thus the injector can run "anywhere," but, typically, 22 | the injector runs as a POD in the Kubernetes cluster that uses it. 23 | The syncer is a simple Golang executable. 24 | It typically runs as a Kubernetes CronJob, but it will run outside the cluster. 25 | 26 | ```bash 27 | $ ./dsv-syncer -h 28 | Usage of ./dsv-syncer: 29 | -credentials string 30 | the path of JSON formatted credentials file (default "credentials/config.json") 31 | -kubeConfig string 32 | the Kubernetes Client API configuration file; ignored when running in-cluster (default "/home/user/.kube/config") 33 | -namespace string 34 | the Kubernetes namespace containing the Secrets to sync; "" (the default) for all namespaces 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/local-kubernetes.md: -------------------------------------------------------------------------------- 1 | # Local Kubernetes 2 | 3 | For easier development workflow, the project has prebuilt tasks for both minikube and kind kubernetes tools. 4 | 5 | As of 2023-02, the default behavior is to use minikube since it handles updating the kubeconfig locally a little more consistently in the tests run. 6 | 7 | ## Working With Kubernetes & Stack Locally 8 | 9 | > **_NOTE_** 10 | > For any tasks get more help with `-h`, for example, run `mage -h k8s:init` 11 | 12 | For local development, Mage tasks have been created to automate most of the setup and usage for local testing. 13 | 14 | - Ensure your local `configs/credentials.json` exists. 15 | - run `mage job:init` to setup a local k8s cluster, initial local copies of the helm chart and kubernetes manifest files. 16 | - Modify the `.cache/dsv-injector/values.yaml` with the embedded credentials.json contents matching your `configs/credentials.json`. 17 | - Modify the `.cache/manifests/*.yaml` files to match the credentials you want to test against. 18 | - To deploy (or redeploy after changes) all the helm charts and kuberenetes manifests run `mage job:redeploy`. 19 | 20 | ## Using Minikube With VM Driver 21 | 22 |
23 | ℹ️ Using Minikube With VM Driver 24 | 25 | To deploy to Minikube set-up with the VM driver, e.g., Linux [kvm2](https://minikube.sigs.k8s.io/docs/drivers/kvm2/) 26 | or Microsoft [Hyper-V](https://minikube.sigs.k8s.io/docs/drivers/hyperv/), 27 | enable the Minikube built-in registry and use it to make the image available to the Minikube VM: 28 | 29 | ```shell 30 | minikube addons enable registry 31 | ``` 32 | 33 | ❗NOTE: run Minikube [tunnel](https://minikube.sigs.k8s.io/docs/commands/tunnel/) 34 | in a separate terminal to make the registry service available to the host. 35 | 36 | ```shell 37 | minikube tunnel 38 | ``` 39 | 40 | _It will run continuously, and stopping it will render the registry inaccessible._ 41 | 42 | Next, get the _host:port_ of the registry: 43 | 44 | ```shell 45 | kubectl get -n kube-system service registry -o jsonpath="{.spec.clusterIP}{':'}{.spec.ports[0].port}" 46 | ``` 47 | 48 | Finally, follow the [Remote Cluster](#remote-cluster) 49 | instructions using it as `$(REGISTRY)` 50 | 51 |
52 | -------------------------------------------------------------------------------- /docs/local-testing.md: -------------------------------------------------------------------------------- 1 | # Local Testing 2 | 3 | ## First Time Setup 4 | 5 | - A valid DSV tenant. 6 | - Creation of a dsv secret and the configured client credentials to setup this. 7 | See [configuring-dsv](configure.md#configuring-dsv) 8 | - The test helm chart values file to be updated with the credentials. 9 | It's located at: `.cache/dsv-injector/values.yaml`. 10 | 11 | 12 | 13 | ## PENDING Contributor Improvements 14 | 15 | For dsv-team members, the goal is to load all this directly from a team vault. Right now this project has not been migrated to this, so you have to setup manually the first time. 16 | 17 | ## Test Environment Configuration 18 | 19 | | Environment Variable | Default | Explanation | 20 | | -------------------------------------- | -------------------------------- | ----------------------------------------------------------- | 21 | | `DSV_K8S_TEST_CONFIG` | _none_ | Contain a JSON string containing a valid `credentials.json` | 22 | | `DSV_K8S_TEST_SECRET_PATH` | `/test/secret` | The path to the secret to test against in the vault | 23 | | DEPRECATED: `DSV_K8S_TEST_CONFIG_FILE` | `../../configs/credentials.json` | The path to a valid `credentials.json` | 24 | 25 | ℹ️ NOTE: `DSV_K8S_TEST_CONFIG` takes precedence over `DSV_K8S_TEST_CONFIG_FILE` 26 | 27 | For example: 28 | 29 | ```shell 30 | DSV_K8S_TEST_CONFIG='{"app1":{"credentials":{"clientId":"","clientSecret":""},"tenant":"mytenant"}}' \ 31 | DSV_K8S_TEST_SECRET_PATH=my:test:secret \ 32 | mage go:testsum ./... 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/release.md: -------------------------------------------------------------------------------- 1 | # Release 2 | 3 | ## Release Notes 4 | 5 | This project uses an different approach to release, driving it from changelog and versioned changelog notes instead of tagging. 6 | 7 | > Use [changie](https://changie.dev/guide/quick-start/) quick start for basic review. 8 | 9 | ### Creating New Notes 10 | 11 | - During development, new changes of note get tracked via `changie new`. This can span many pull requests, whatever makes sense as version to ship as changes to users. 12 | - To release the changes into a version, `changie batch ` (unless breaking changes occur, you'll want to stick with minor for feature additions, and patch for fixes or non app work. 13 | 14 | Keep your summary of changes that users would care about in the `.changes/` files it will create. 15 | 16 | ### Release 17 | 18 | Update [CHANGELOG.md](CHANGELOG.md) by running `changie merge` which will rebuild the changelog file with all the documented notes. 19 | 20 | ### Format & Lint 21 | 22 | - Run `trunk fmt --all; trunk check --all` to finalize run through. 23 | - Push changelog via PR or direct if you have permissions and this will trigger the [release-composite](.github/workflows/release-composite.yml). If any issues, retrigger manually via `gh workflow run release-composite`. 24 | - Release should be published in the [releases](https://github.com/DelineaXPM/dsv-repo-template/releases) 25 | - Edit the release and click "update release" to ensure it publishes to the marketplace. Unfortunately, creating a release doesn't trigger the marketplace release without doing this step. While this can be automated through other actions, I've opted due to time constraints to leave that last step as a manual one. 26 | 27 | ## FAQ 28 | 29 | ### What drives the version number for the release? 30 | 31 | Changie notes are named like `v1.0.4.md`. 32 | This version number will be used to set the version of the release, so the docs in essence will be the version source of truth. 33 | 34 | ### Conventional Commit 35 | 36 | We use [conventional commit](https://www.conventionalcommits.org/en). 37 | Pull requests must adhere to this to be merged. 38 | 39 | Description should be bullet point list or longer-form content to describe anything the title doesn't make clear. 40 | -------------------------------------------------------------------------------- /docs/setup-developer.md: -------------------------------------------------------------------------------- 1 | # Setup Developer 2 | 3 | > Important: All the core local workflow tasks to build and deploy to minikube are wrapped up in mage tasks 4 | > 5 | > Try `mage` by itself to list. 6 | > Use `mage job:*` tasks to help simplify the process. 7 | 8 | 1. [Setup developer tooling](setup-project.md) 9 | 2. [Create DSV Credentials for Testing](configure.md) 10 | 3. [Configure The Manifests](configure.md#update-manifests) 11 | 4. Once credentials are configured in `.cache/dsv-injector/values.yaml` 12 | 1. 1st time: `mage job:init`. 13 | 2. 1st time/Anytime You updated Go code: `mage job:rebuildimages`. 14 | 3. Any time you want to redeploy the kubernetes & helm charts to minikube: `mage job:redeploy`. 15 | 16 | As always, the source of truth is `mage` so if the task names in the doc don't work, check the CLI for the proper commands. 17 | 18 | ## Optional 19 | 20 | If you are using codespaces, most of the tooling should be ready out of the box as long as you open `zsh` terminal. 21 | Run `tilt up` and then you can invoke much of this (including watch the logs stream) from the terminal. 22 | 23 | ## Reference 24 | 25 | - Optional: [devcontainer/codespaces](devcontainer.md) 26 | - Local Kubernetes Overview: [Kubernetes](local-kubernetes.md) 27 | -------------------------------------------------------------------------------- /docs/setup-project.md: -------------------------------------------------------------------------------- 1 | # Setup Project 2 | 3 | ## Compatibility 4 | 5 | Linux & MacOS is supported out of the box for local development. 6 | Windows with WSL2 should also work fine. 7 | 8 | While the majority of this is cross-platform, the automatically linting and some other commands are only compatible with Linux/MacOS. 9 | 10 | ## Overview 11 | 12 | - Mage: Mage is a Go based automation alternative to Make and provides newer functionality for local Kind cluster setup, Go development tooling/linting, and more. 13 | Use [aqua](#aqua) to automatically install. 14 | - Run `mage` to list all available tasks, and `mage init` to setup developer tooling. 15 | Get more detail on a task, if it's available by running `mage -h init`. 16 | 17 | - Make: Makefiles provide core automation from the original project. 18 | - This has slowly been phased out for the more robust Mage tasks. 19 | Most of your usage won't touch Make. 20 | The only usage that _might_ use this is the local cert based setup for debugger. 21 | This hasn't been used in years by current maintainers, as log streaming from mage/tilt UI have been primary method. 22 | - For anything other than running the debugger, use `mage` commands which have been regularly improved and tested and move any new automation to the magefiles. 23 | 24 | ## Initial Setup 25 | 26 | ## Aqua 27 | 28 | This tool will ensure all the core development tools, including Go, are installed and setup without needing to run `apt` or other package managers. 29 | 30 | Install [aqua](https://aquaproj.github.io/docs/tutorial-basics/quick-start#install-aqua) and have it configured in your path per directions. 31 | 32 | Run `aqua install` for tooling such as changie or others for the project. 33 | 34 | Ensure your profile has this in it: 35 | 36 | ```shell 37 | export PATH="${AQUA_ROOT_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/aquaproj-aqua}/bin:$PATH" # for those using aqua this will ensure it's in the path with all tools if loading from home 38 | ``` 39 | 40 | ## When Using Without Devcontainer/Codespaces 41 | 42 | - Install Aqua 43 | - Alternative: Manually ensure Go is installed. 44 | - Run `aqua policy allow` to allow the custom `go install` package to run. 45 | - Run `mage init` to install tooling. 46 | - Done automatically by Mage -> Install [trunk](https://trunk.io/products/check) (quick install script: `curl https://get.trunk.io -fsSL | bash`) 47 | - This will allow faster installs of project tooling by grabbing binaries for your platform more quickly (most of the time release binaries instead of building from source). 48 | 49 | > If you get an error with a go installation, just try once more as aqua installs in parallel and might not have finished installing Go before trying to run `go install` first. 50 | 51 | ## Direnv 52 | 53 | This loads environment variables for the project automatically. 54 | 55 | You should hook into your shell, for example with zsh: `eval "$(direnv hook zsh)"`. 56 | 57 | Other shells are supported, but this project is only tested with zsh. 58 | 59 | > [Hook Into Your Shell](https://direnv.net/docs/hook.html) 60 | 61 | Direnv: Default test values are loaded on macOS/Linux based system using [direnv](https://direnv.net/docs/installation.html). 62 | 63 | Run `direnv allow` in the directory to load default env configuration for testing. 64 | -------------------------------------------------------------------------------- /docs/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## Supporting Alternative TLD 4 | 5 | If you are using an alternative TLD, such as `https://{mytenant}.secretsvaultcloud.eu`, try adding the tld to the credentials file. 6 | 7 | ```json 8 | { 9 | "default": { 10 | "credentials": { 11 | "clientId": "", 12 | "clientSecret": "" 13 | }, 14 | "tenant": "mytenant", 15 | "tld": "eu" 16 | } 17 | } 18 | ``` 19 | 20 | ## Obtaining Logs 21 | 22 | For both customers and development, stern allows easier debugging by providing a stream of the logs for both syncer & injector in one workflow. 23 | 24 | Use Stern to easily stream cross namespace logs with the `dsv-filter-selector` by running: 25 | 26 | Aqua installs this automatically, but if you want to do this manually grab from github releases like this 27 | 28 | If not using the `aqua`, you can modify the following command to match the version and OS you are using and download directly. 29 | 30 | ```shell 31 | $(curl -fSSl https://github.com/wercker/stern/releases/download/1.11.0/stern_linux_amd64 -o ./stern) && sudo chmod +x ./stern && sudo mv ./stern /usr/local/bin 32 | ``` 33 | 34 | Alternative install options: [Stern Installation](https://github.com/stern/stern#installation) 35 | 36 | ### Alternative - If Using Mage For Local Development 37 | 38 | While `mage k8s:logs` will run this for you, manually you can invoke like this: 39 | 40 | ```shell 41 | # For all pods in the namespace run 42 | stern --kubeconfig .cache/config --namespace dsv --timestamps . 43 | 44 | # For pods with the selector run 45 | stern --kubeconfig .cache/config --namespace dsv --timestamps --selector 'dsv-filter-name in (dsv-syncer, dsv-injector)' 46 | ``` 47 | 48 | ### Example for Providing Logs for Support 49 | 50 | If debugging, you can stream logs from Kubernetes with this tool and capture to a log file for providing in support cases. 51 | 52 | ```shell 53 | stern --kubeconfig .cache/config --namespace dsv --timestamps . > activity.log 54 | ``` 55 | -------------------------------------------------------------------------------- /docs/using.md: -------------------------------------------------------------------------------- 1 | # Using 2 | 3 | Once installed, any correctly annotated Kubernetes Secrets are modified on create and update. 4 | 5 | The four annotations that affect the behavior of the webhook are: 6 | 7 | ```golang 8 | const( 9 | credentialsAnnotation = "dsv.delinea.com/credentials" 10 | setAnnotation = "dsv.delinea.com/set-secret" 11 | addAnnotation = "dsv.delinea.com/add-to-secret" 12 | updateAnnotation = "dsv.delinea.com/update-secret" 13 | ) 14 | ``` 15 | 16 | `credentialsAnnotation` selects the credentials that the injector uses to retrieve the DSV Secret. 17 | If the credentials are present, it must map to Client Credential and Tenant mapping. 18 | The injector will use the _default_ Credential and Tenant mapping unless the `credentialsAnnotation` is declared. 19 | The `setAnnotation`, `addAnnotation` and `updateAnnotation`, must contain the path to the DSV Secret that the injector will use to mutate the Kubernetes Secret. 20 | 21 | - `addAnnotation` adds missing fields without overwriting or removing existing fields. 22 | - `updateAnnotation` adds and overwrites existing fields but does not remove fields. 23 | - `setAnnotation` overwrites fields and removes fields that do not exist in the DSV Secret. 24 | NOTE: A Kubernetes Secret should specify only one of the "add," "update," 25 | or "set" annotations. 26 | The order of precedence is `setAnnotation`, 27 | then `addAnnotation`, then `updateAnnotation` when multiple are present. 28 | 29 | ## Examples 30 | 31 | ```yaml 32 | --- 33 | apiVersion: v1 34 | kind: Secret 35 | metadata: 36 | name: example-secret 37 | annotations: 38 | dsv.delinea.com/credentials: app1 39 | dsv.delinea.com/set-secret: test:secret 40 | type: Opaque 41 | data: 42 | username: 43 | domain: 44 | password: 45 | ``` 46 | 47 | The above example specifies credentials, so a mapping for those credentials must exist in the current webhook configuration. 48 | It uses the `setAnnotation`, so the data in the injector will overwrite the existing contents of the Kubernetes Secret. 49 | 50 | If `/test/secret` contains a `username` and `password` but no `domain`, then the k8s secret would get the `username` and `password` from dsv secret data and the injector will remove the `domain` field. 51 | 52 | There are more examples in the `examples` directory. 53 | They show how the different annotations work. 54 | -------------------------------------------------------------------------------- /examples/add-to-secret.yml: -------------------------------------------------------------------------------- 1 | # trunk-ignore-all(trivy,checkov,gitleaks): ignore, examples file with hard coded values 2 | --- 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: user-domain 7 | annotations: 8 | dsv.delinea.com/add-to-secret: 'tests:dsv-k8s:food' 9 | type: Opaque 10 | data: 11 | username: dW5tb2RpZmllZC11c2VybmFtZQ== 12 | domain: dW5tb2RpZmllZC1kb21haW4= 13 | food: aW0gaHVuZ3J5IGFuZCB0aGlzIHNob3VsZCBiZSByZXBsYWNlZCB3aXRoIGdvb2QgZm9vZAo= 14 | -------------------------------------------------------------------------------- /examples/set-secret.yml: -------------------------------------------------------------------------------- 1 | # trunk-ignore-all(trivy,checkov,gitleaks): ignore, examples file with hard coded values 2 | --- 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: user-domain-pass 7 | annotations: 8 | # dsv.delinea.com/credentials: app1 # or default if not using multiple credentials 9 | dsv.delinea.com/set-secret: 'tests:dsv-k8s:food' 10 | type: Opaque 11 | data: 12 | username: dW5tb2RpZmllZC11c2VybmFtZQ== 13 | domain: dW5tb2RpZmllZC1kb21haW4= 14 | password: dW5tb2RpZmllZC1wYXNzd29yZA== 15 | food: aW0gaHVuZ3J5IGFuZCB0aGlzIHNob3VsZCBiZSByZXBsYWNlZCB3aXRoIGdvb2QgZm9vZAo= 16 | -------------------------------------------------------------------------------- /examples/update-secret.yml: -------------------------------------------------------------------------------- 1 | # trunk-ignore-all(trivy,checkov,gitleaks): ignore, examples file with hard coded values 2 | --- 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: pass-domain 7 | annotations: 8 | dsv.delinea.com/update-secret: 'tests:dsv-k8s:food' 9 | type: Opaque 10 | data: 11 | password: dW5tb2RpZmllZC1wYXNzd29yZA== 12 | domain: dW5tb2RpZmllZC1kb21haW4= 13 | food: aW0gaHVuZ3J5IGFuZCB0aGlzIHNob3VsZCBiZSByZXBsYWNlZCB3aXRoIGdvb2QgZm9vZAo= 14 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DelineaXPM/dsv-k8s/v2 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.1 6 | 7 | require ( 8 | al.essio.dev/pkg/shellescape v1.5.0 9 | github.com/DelineaXPM/dsv-sdk-go/v2 v2.1.2 10 | github.com/bitfield/script v0.22.1 11 | github.com/brianvoe/gofakeit/v6 v6.28.0 12 | github.com/caarlos0/env/v6 v6.10.1 13 | github.com/magefile/mage v1.15.0 14 | github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38 15 | github.com/pterm/pterm v0.12.79 16 | github.com/rs/zerolog v1.33.0 17 | github.com/sheldonhull/magetools v1.0.2 18 | k8s.io/api v0.30.3 19 | k8s.io/apimachinery v0.30.3 20 | k8s.io/client-go v0.30.3 21 | ) 22 | 23 | require ( 24 | atomicgo.dev/cursor v0.2.0 // indirect 25 | atomicgo.dev/keyboard v0.2.9 // indirect 26 | atomicgo.dev/schedule v0.1.0 // indirect 27 | github.com/aws/aws-sdk-go v1.55.5 // indirect 28 | github.com/containerd/console v1.0.4 // indirect 29 | github.com/davecgh/go-spew v1.1.1 // indirect 30 | github.com/dustin/go-humanize v1.0.1 // indirect 31 | github.com/emicklei/go-restful/v3 v3.12.1 // indirect 32 | github.com/go-logr/logr v1.4.2 // indirect 33 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 34 | github.com/go-openapi/jsonreference v0.21.0 // indirect 35 | github.com/go-openapi/swag v0.23.0 // indirect 36 | github.com/gogo/protobuf v1.3.2 // indirect 37 | github.com/golang/protobuf v1.5.4 // indirect 38 | github.com/google/gnostic-models v0.6.8 // indirect 39 | github.com/google/gofuzz v1.2.0 // indirect 40 | github.com/google/uuid v1.6.0 // indirect 41 | github.com/gookit/color v1.5.4 // indirect 42 | github.com/imdario/mergo v0.3.12 // indirect 43 | github.com/itchyny/gojq v0.12.16 // indirect 44 | github.com/itchyny/timefmt-go v0.1.6 // indirect 45 | github.com/jmespath/go-jmespath v0.4.0 // indirect 46 | github.com/josharian/intern v1.0.0 // indirect 47 | github.com/json-iterator/go v1.1.12 // indirect 48 | github.com/lithammer/fuzzysearch v1.1.8 // indirect 49 | github.com/mailru/easyjson v0.7.7 // indirect 50 | github.com/mattn/go-colorable v0.1.13 // indirect 51 | github.com/mattn/go-isatty v0.0.20 // indirect 52 | github.com/mattn/go-runewidth v0.0.16 // indirect 53 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 54 | github.com/modern-go/reflect2 v1.0.2 // indirect 55 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 56 | github.com/rivo/uniseg v0.4.7 // indirect 57 | github.com/spf13/pflag v1.0.5 // indirect 58 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect 59 | github.com/ztrue/tracerr v0.4.0 // indirect 60 | golang.org/x/mod v0.20.0 // indirect 61 | golang.org/x/net v0.38.0 // indirect 62 | golang.org/x/oauth2 v0.27.0 // indirect 63 | golang.org/x/sys v0.31.0 // indirect 64 | golang.org/x/term v0.30.0 // indirect 65 | golang.org/x/text v0.23.0 // indirect 66 | golang.org/x/time v0.6.0 // indirect 67 | google.golang.org/protobuf v1.34.2 // indirect 68 | gopkg.in/inf.v0 v0.9.1 // indirect 69 | gopkg.in/yaml.v2 v2.4.0 // indirect 70 | gopkg.in/yaml.v3 v3.0.1 // indirect 71 | k8s.io/klog/v2 v2.130.1 // indirect 72 | k8s.io/kube-openapi v0.0.0-20240808142205-8e686545bdb8 // indirect 73 | k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect 74 | mvdan.cc/sh/v3 v3.8.0 // indirect 75 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect 76 | sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect 77 | sigs.k8s.io/yaml v1.4.0 // indirect 78 | ) 79 | 80 | replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16 81 | -------------------------------------------------------------------------------- /internal/k8s/k8s.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "fmt" 5 | 6 | "k8s.io/client-go/kubernetes" 7 | v1 "k8s.io/client-go/kubernetes/typed/core/v1" 8 | "k8s.io/client-go/rest" 9 | ) 10 | 11 | // Config is a callback that returns a Kubernetes Client API config for the given namespace; namespace = "" for all namespaces. 12 | // see https://github.com/kubernetes/client-go/tree/v0.23.5/examples 13 | type Config func(namespace string) (*rest.Config, error) 14 | 15 | func GetKubernetesClientset(configCallback Config, namespace string) (*kubernetes.Clientset, error) { 16 | if config, err := configCallback(namespace); err != nil { 17 | return nil, fmt.Errorf("[ERROR] error calling k8s.Config: %s", err) 18 | } else { 19 | if clientset, err := kubernetes.NewForConfig(config); err != nil { 20 | return nil, fmt.Errorf("[ERROR] error getting Kubernetes Clientset: %s", err) 21 | } else { 22 | return clientset, nil 23 | } 24 | } 25 | } 26 | 27 | // getSecretsClient returns a Kubernetes Secrets client for the given namespace; namespace = "" for all namespaces. 28 | func GetSecretsClient(configCallback Config, namespace string) (v1.SecretInterface, error) { 29 | if clientset, err := GetKubernetesClientset(configCallback, namespace); err != nil { 30 | return nil, fmt.Errorf("[ERROR] error getting Kubernetes Clientset: %s", err) 31 | } else { 32 | return clientset.CoreV1().Secrets(namespace), nil 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /internal/logger/logger.go: -------------------------------------------------------------------------------- 1 | // logger handles setting the default output format for logging 2 | package logger 3 | 4 | import ( 5 | "os" 6 | "strconv" 7 | 8 | "github.com/rs/zerolog" 9 | ) 10 | 11 | // var Logger zerolog.Logger //nolint:gochecknoglobals // allow for logging at this time. 12 | 13 | // InitLogger initializes the zerolog logger settings. 14 | func New() zerolog.Logger { 15 | // see zerolog docs, this removes the fully qualified path and makes it just show the "logger.go:linenumber" so logs are much easier to read 16 | zerolog.CallerMarshalFunc = func(pc uintptr, file string, line int) string { 17 | short := file 18 | for i := len(file) - 1; i > 0; i-- { 19 | if file[i] == '/' { 20 | short = file[i+1:] 21 | break 22 | } 23 | } 24 | file = short 25 | return file + ":" + strconv.Itoa(line) 26 | } 27 | l := zerolog.New(os.Stdout).With().Caller().Logger().With().Timestamp().Logger() 28 | zerolog.SetGlobalLevel(zerolog.InfoLevel) 29 | return l 30 | } 31 | 32 | // EnableDebug enables debug logging. 33 | func EnableDebug() { 34 | zerolog.SetGlobalLevel(zerolog.DebugLevel) 35 | } 36 | -------------------------------------------------------------------------------- /internal/test/testing.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "os" 7 | "testing" 8 | 9 | "github.com/DelineaXPM/dsv-k8s/v2/pkg/config" 10 | ) 11 | 12 | const ( 13 | ConfigEnvVar = "DSV_K8S_TEST_CONFIG" 14 | ConfigFileEnvVar = "DSV_K8S_TEST_CONFIG_FILE" 15 | DefaultConfigFile = "../../configs/credentials.json" 16 | DefaultSecretPath = "/test/secret" 17 | SecretPathEnvVar = "DSV_K8S_TEST_SECRET_PATH" 18 | ) 19 | 20 | // Ensure log output doesn't pollute tests. 21 | func TestMain(m *testing.M) { 22 | log.SetOutput(io.Discard) 23 | } 24 | 25 | // SecretPath returns the secret path for testing 26 | func SecretPath() string { 27 | if v := os.Getenv(SecretPathEnvVar); v != "" { 28 | return v 29 | } 30 | return DefaultSecretPath 31 | } 32 | 33 | func credentialsFromFilePath(credentialsFilePath string) config.Credentials { 34 | if credentials, err := config.GetCredentials(credentialsFilePath); err == nil { 35 | return *credentials 36 | } else { 37 | panic(err) 38 | } 39 | } 40 | 41 | // Credentials returns the credentials for testing 42 | func Credentials() config.Credentials { 43 | if v := os.Getenv(ConfigEnvVar); v != "" { 44 | if credentials, err := config.MakeCredentials([]byte(v)); err == nil { 45 | return *credentials 46 | } else { 47 | panic(err) // FIXME: avoid using panic and error out gracefully 48 | } 49 | } else if v := os.Getenv(ConfigFileEnvVar); v != "" { 50 | return credentialsFromFilePath(v) 51 | } else { 52 | return credentialsFromFilePath(DefaultConfigFile) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /magefiles/constants/constants.mage.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | // Since we are dealing with builds, having a constants file until using a config input makes it easy. 4 | 5 | // Default target to run when none is specified 6 | // If not set, running mage will list available targets 7 | // var Default = Build. 8 | 9 | const ( 10 | // ArtifactDirectory is a directory containing artifacts for the project and shouldn't be committed to source. 11 | ArtifactDirectory = ".artifacts" 12 | 13 | // PermissionUserReadWriteExecute is the permissions for the artifact directory. 14 | PermissionUserReadWriteExecute = 0o0700 15 | 16 | // ConfigsDirectory is where the credentials used for testing is placed. 17 | // This isn't committed in git. 18 | ConfigsDirectory = "configs" 19 | 20 | // CacheDirectory is where the cache for the project is placed, ie artifacts that don't need to be rebuilt often. 21 | CacheDirectory = ".cache" 22 | 23 | // ExamplesDirectory is the directory where the kubernetes manifests are stored. 24 | ExamplesDirectory = "examples" 25 | 26 | // CacheChartsDirectory is the directory where the cached helm values file is copied to. 27 | CacheChartsDirectory = ".cache/charts" 28 | 29 | // CacheCredentialFile is the path to the credential file for the project, which is cached locally. 30 | CacheCredentialFile = ".cache/credentials.json" //nolint:gosec // this is a test project and this directory is excluded from source 31 | ) 32 | 33 | const ( 34 | // KindClusterName is the name of the kind cluster. 35 | KindClusterName = "dsvtest" 36 | // KindClusterName is the name of the kind cluster. 37 | KindContextName = "dsvtest" 38 | // KubeconfigPath is the path to the kubeconfig file for this project, which is cached locally. 39 | Kubeconfig = ".cache/config" 40 | // KubectlNamespace is the namespace used for all kubectl commands, so that they don't operate in default or other namespace by accident. 41 | KubectlNamespace = "dsv" 42 | 43 | // DockerImageQualified is the qualified path of the image in Docker Hub. 44 | DockerImageQualified = "docker.io/delineaxpm/dsv-k8s" 45 | // DockerImageNameLocal is the name of the built image to run locally and load with minikube/kind. 46 | DockerImageNameLocal = "dev.local/dsv-k8s" 47 | ) 48 | 49 | const ( 50 | 51 | // HelmTimeout is the timeout for helm commands using the CLI. 52 | HelmTimeout = "5m" 53 | // ChartsDirectory is the directory where the helm charts are placed, in sub directories. 54 | ChartsDirectory = "charts" 55 | // SternFilter is the filter for dsv-filter-name for streaming logs easily. 56 | SternFilter = "dsv-syncer, dsv-injector" 57 | ) 58 | 59 | const ( 60 | // MinikubeCPU is the CPU count for minikube. 61 | MinikubeCPU = "2" 62 | // MinikubeMemory is the memory for minikube. 63 | MinikubeMemory = "2048" 64 | ) 65 | -------------------------------------------------------------------------------- /magefiles/constants/variables.mage.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | import ( 4 | "path/filepath" 5 | "time" 6 | ) 7 | 8 | type HelmCharts struct { 9 | ReleaseName string 10 | ChartPath string 11 | Namespace string 12 | // Values is the customized yaml file placed in the CacheDirectory to run install with. 13 | // Copy the values.yaml from the helm chart to start here. 14 | Values string 15 | } 16 | 17 | var HelmChartsList = []HelmCharts{ 18 | { 19 | ReleaseName: "dsv-syncer", 20 | ChartPath: filepath.Join(ChartsDirectory, "dsv-syncer"), 21 | Namespace: "dsv", 22 | Values: filepath.Join(CacheDirectory, "dsv-syncer", "values.yaml"), 23 | }, 24 | { 25 | ReleaseName: "dsv-injector", 26 | ChartPath: filepath.Join(ChartsDirectory, "dsv-injector"), 27 | Namespace: "dsv", 28 | Values: filepath.Join(CacheDirectory, "dsv-injector", "values.yaml"), 29 | }, 30 | } 31 | 32 | // DefaultHelmTimeoutMinutes is the default timeout for helm commands. 33 | var DefaultHelmTimeoutMinutes = time.Minute * 5 34 | 35 | // CacheManifestDirectory is the directory where helm charts are cached for local tweaking. 36 | // They are copied from the examples directory to allow editing without committing to source control. 37 | var CacheManifestDirectory = filepath.Join(CacheDirectory, "manifests") 38 | -------------------------------------------------------------------------------- /magefiles/dev-cli-tools.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // ToolList is a list of tools that are installed as binaries for development usage. 4 | // This list gets installed to go bin directory once `mage init` is run. 5 | var ToolList = []string{} 6 | -------------------------------------------------------------------------------- /magefiles/goreleaser.mage.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "hash" 6 | "hash/fnv" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "time" 11 | 12 | "github.com/brianvoe/gofakeit/v6" 13 | "github.com/magefile/mage/sh" 14 | "github.com/pterm/pterm" 15 | "github.com/sheldonhull/magetools/pkg/magetoolsutils" 16 | "github.com/sheldonhull/magetools/pkg/req" 17 | ) 18 | 19 | // FNV64a hashes using fnv64a algorithm 20 | // 21 | // Sourced from: https://github.com/shomali11/util/blob/master/xhashes/xhashes.go 22 | func FNV64a(text string) uint64 { 23 | algorithm := fnv.New64a() 24 | return uint64Hasher(algorithm, text) 25 | } 26 | 27 | // uint64Hasher returns a uint64 28 | // 29 | // Sourced from: https://github.com/shomali11/util/blob/master/xhashes/xhashes.go 30 | func uint64Hasher(algorithm hash.Hash64, text string) uint64 { 31 | algorithm.Write([]byte(text)) 32 | return algorithm.Sum64() 33 | } 34 | 35 | func randomBuildName() (petname string) { 36 | v := time.Now().Unix() 37 | gofakeit.Seed(v) 38 | animal := gofakeit.Animal() 39 | adjective := gofakeit.AdjectiveDescriptive() 40 | petname = strings.ToLower(strings.Join([]string{adjective, animal}, "-")) 41 | pterm.Info.Printfln("Random Pet Calculated at Runtime: %s\n", petname) 42 | 43 | return petname 44 | } 45 | 46 | func checkEnvVar(envVar string, required bool) (string, error) { 47 | envVarValue := os.Getenv(envVar) 48 | if envVarValue == "" && required { 49 | pterm.Error.Printfln( 50 | "%s is required and unable to proceed without this being provided. terminating task.", 51 | envVar, 52 | ) 53 | return "", fmt.Errorf("%s is required", envVar) 54 | } 55 | if envVarValue == "" { 56 | pterm.Debug.Printfln( 57 | "checkEnvVar() found no value for: %q, however this is marked as optional, so not exiting task", 58 | envVar, 59 | ) 60 | } 61 | pterm.Debug.Printfln("checkEnvVar() found value: %q=%q", envVar, envVarValue) 62 | return envVarValue, nil 63 | } 64 | 65 | // 🔨 Build builds the project for the current platform. 66 | func Build() error { 67 | magetoolsutils.CheckPtermDebug() 68 | binary, err := req.ResolveBinaryByInstall("goreleaser", "github.com/goreleaser/goreleaser@latest") 69 | if err != nil { 70 | return err 71 | } 72 | 73 | releaserArgs := []string{ 74 | "build", 75 | "--clean", 76 | "--snapshot", 77 | "--single-target", 78 | } 79 | pterm.Debug.Printfln("goreleaser: %+v", releaserArgs) 80 | 81 | return sh.RunWithV( 82 | map[string]string{ 83 | "BUILD_NAME": randomBuildName(), 84 | }, 85 | binary, releaserArgs...) // "--skip-announce",. 86 | } 87 | 88 | // 🔨 BuildAll builds all the binaries defined in the project, for all platforms. This includes Docker image generation but skips publish. 89 | // If there is no additional platforms configured in the task, then basically this will just be the same as `mage build`. 90 | func BuildAll() error { 91 | magetoolsutils.CheckPtermDebug() 92 | binary, err := req.ResolveBinaryByInstall("goreleaser", "github.com/goreleaser/goreleaser@latest") 93 | if err != nil { 94 | return err 95 | } 96 | 97 | releaserArgs := []string{ 98 | "release", 99 | "--snapshot", 100 | "--clean", 101 | "--skip", "publish,sbom", 102 | } 103 | pterm.Debug.Printfln("goreleaser: %+v", releaserArgs) 104 | _ = os.Setenv("BUILD_NAME", randomBuildName()) 105 | return sh.RunWithV( 106 | map[string]string{ 107 | "BUILD_NAME": randomBuildName(), 108 | }, binary, releaserArgs...) 109 | // To pass in explicit version mapping, you can do this. I'm not using at this time. 110 | // Return sh.RunWithV(map[string]string{ 111 | // "GORELEASER_CURRENT_TAG": "latest", 112 | // }, binary, releaserArgs...) 113 | } 114 | 115 | // 🔨 Release generates a release for the current platform. 116 | func Release() error { 117 | magetoolsutils.CheckPtermDebug() 118 | binary, err := req.ResolveBinaryByInstall("goreleaser", "github.com/goreleaser/goreleaser@latest") 119 | if err != nil { 120 | return err 121 | } 122 | 123 | if _, err = checkEnvVar("DOCKER_ORG", true); err != nil { 124 | return err 125 | } 126 | 127 | changieBinary, err := req.ResolveBinaryByInstall("changie", "github.com/miniscruff/changie@latest") 128 | if err != nil { 129 | pterm.Error.Println("unable to install changelog binary") 130 | return err 131 | } 132 | releaseVersion, err := sh.Output(changieBinary, "latest") 133 | if err != nil { 134 | pterm.Warning.Printfln("changie pulling latest release note version failure: %v", err) 135 | } 136 | cleanVersion := strings.TrimSpace(releaseVersion) 137 | cleanpath := filepath.Join(".changes", cleanVersion+".md") 138 | if os.Getenv("GITHUB_WORKSPACE") != "" { 139 | cleanpath = filepath.Join(os.Getenv("GITHUB_WORKSPACE"), ".changes", cleanVersion+".md") 140 | } 141 | 142 | releaserArgs := []string{ 143 | "release", 144 | "--clean", 145 | "--skip=validate", 146 | fmt.Sprintf("--release-notes=%s", cleanpath), 147 | } 148 | pterm.Debug.Printfln("goreleaser: %+v", releaserArgs) 149 | 150 | return sh.RunWithV( 151 | map[string]string{ 152 | "GORELEASER_CURRENT_TAG": cleanVersion, 153 | "BUILD_NAME": randomBuildName(), 154 | }, 155 | binary, 156 | releaserArgs..., 157 | ) 158 | } 159 | -------------------------------------------------------------------------------- /magefiles/install.mage.go: -------------------------------------------------------------------------------- 1 | package main 2 | -------------------------------------------------------------------------------- /magefiles/jobs.mage.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/DelineaXPM/dsv-k8s/v2/magefiles/constants" 5 | "github.com/DelineaXPM/dsv-k8s/v2/magefiles/helm" 6 | "github.com/DelineaXPM/dsv-k8s/v2/magefiles/k8s" 7 | // "github.com/DelineaXPM/dsv-k8s/v2/magefiles/kind" 8 | "github.com/DelineaXPM/dsv-k8s/v2/magefiles/minikube" 9 | "github.com/magefile/mage/mg" 10 | "github.com/pterm/pterm" 11 | ) 12 | 13 | // Job is a namespace to contain chained sets of automation actions, to reduce the need to chain many commands together for common workflows. 14 | type Job mg.Namespace 15 | 16 | // Init runs the setup tasks to initialize the local resources and files, without trying to apply yet. 17 | // 18 | // Setup initializes all the required steps for the cluster creation, initial helm chart copies, and kubeconfig copies. 19 | func (Job) Init() { 20 | pterm.DefaultSection.Println("(Job) Init()") 21 | mg.SerialDeps( 22 | // kind.Kind{}.Init, 23 | minikube.Minikube{}.Init, 24 | k8s.K8s{}.Init, 25 | helm.Helm{}.Init, 26 | ) 27 | } 28 | 29 | // Redeploy removes k8s resources, helm uninstall, and then runs k8s apply and helm install. 30 | func (Job) Redeploy() { 31 | pterm.DefaultSection.Println("(Job) Redeploy()") 32 | 33 | mg.Deps( 34 | helm.Helm{}.Uninstall, 35 | mg.F(k8s.K8s{}.Delete, constants.CacheManifestDirectory), 36 | ) 37 | 38 | mg.SerialDeps( 39 | minikube.Minikube{}.RemoveImages, 40 | minikube.Minikube{}.LoadImages, // just be sure in case forget to load local images that the latest is always used 41 | helm.Helm{}.Install, // this should take place first so the creation of the manifests can benefit from the resulting injector/syncer 42 | mg.F(k8s.K8s{}.Apply, constants.CacheManifestDirectory), 43 | // k8s.K8s{}.Logs, // use chained command 44 | ) 45 | } 46 | 47 | // RebuildImages runs the build and minikube load commands so the new source is able to be run by `job:redeploy`. 48 | func (Job) RebuildImages() { 49 | pterm.DefaultSection.Println("(Job) RebuildImages()") 50 | mg.SerialDeps( 51 | BuildAll, 52 | minikube.Minikube{}.LoadImages, 53 | ) 54 | pterm.Success.Printfln("RebuildImages() complete. Run `mage job:redeploy` to redeploy the new images.") 55 | } 56 | -------------------------------------------------------------------------------- /magefiles/k8s/k8s.mage.go: -------------------------------------------------------------------------------- 1 | // K8s contains commands for kubectl and other kubernetes related commands. 2 | package k8s 3 | 4 | import ( 5 | "encoding/base64" 6 | "errors" 7 | "fmt" 8 | "os" 9 | "os/exec" 10 | "path/filepath" 11 | "strings" 12 | 13 | "github.com/DelineaXPM/dsv-k8s/v2/magefiles/constants" 14 | "github.com/magefile/mage/mg" 15 | "github.com/magefile/mage/sh" 16 | "github.com/pterm/pterm" 17 | "github.com/sheldonhull/magetools/pkg/magetoolsutils" 18 | ) 19 | 20 | // k8s contains commands for kubectl and other kubernetes related commands. 21 | type K8s mg.Namespace 22 | 23 | // Init copies the k8 yaml manifest files from the examples directory to the cache directory. 24 | func (K8s) Init() error { 25 | magetoolsutils.CheckPtermDebug() 26 | pterm.DefaultHeader.Println("(K8s) Init()") 27 | // Create the cache directory if it doesn't exist. 28 | if _, err := os.Stat(constants.CacheManifestDirectory); os.IsNotExist(err) { 29 | if err := os.MkdirAll(constants.CacheManifestDirectory, constants.PermissionUserReadWriteExecute); err != nil { 30 | return fmt.Errorf("os.MkdirAll(): %w", err) 31 | } 32 | } 33 | // For each file in the examples directory, create a copy in the CacheManifestDirectory. 34 | de, err := os.ReadDir(constants.ExamplesDirectory) 35 | if err != nil { 36 | return err 37 | } 38 | for _, file := range de { 39 | originalFile := filepath.Join(constants.ExamplesDirectory, file.Name()) 40 | targetFile := filepath.Join(constants.CacheManifestDirectory, file.Name()) 41 | // If the file doesn't exist in the manifest directory, read it and copy it to the manifest directory. 42 | if _, err := os.Stat(targetFile); os.IsNotExist(err) { 43 | // Read the original file. 44 | original, err := os.ReadFile(originalFile) 45 | if err != nil { 46 | return fmt.Errorf("unable to read original file: %s, os.ReadFile(): %w", original, err) 47 | } 48 | // Create the new file from the contents of the original file. 49 | if err := os.WriteFile(targetFile, original, constants.PermissionUserReadWriteExecute); err != nil { 50 | return fmt.Errorf("unable to write new file: %s, os.WriteFile(): %w", targetFile, err) 51 | } 52 | pterm.Success.Printfln("copied starter example (edit and apply to use): %s", targetFile) 53 | } 54 | } 55 | pterm.Success.Println("(K8s) Init()") 56 | return nil 57 | } 58 | 59 | // Apply applies a kubernetes manifest. 60 | func (K8s) Apply(manifest string) error { 61 | magetoolsutils.CheckPtermDebug() 62 | pterm.DefaultHeader.Println("(K8s) Apply()") 63 | return sh.Run( 64 | "kubectl", 65 | "apply", 66 | "--kubeconfig", constants.Kubeconfig, 67 | "--context", constants.KindContextName, 68 | "--namespace", constants.KubectlNamespace, 69 | "--cluster", constants.KindContextName, 70 | "--wait=true", 71 | "--overwrite=true", 72 | "-f", manifest, 73 | ) 74 | } 75 | 76 | // Apply applies a kubernetes manifest. 77 | func (K8s) Delete(manifest string) { 78 | magetoolsutils.CheckPtermDebug() 79 | pterm.DefaultHeader.Println("(K8s) Delete()") 80 | if err := sh.Run( 81 | "kubectl", 82 | "delete", 83 | "--kubeconfig", constants.Kubeconfig, 84 | "--context", constants.KindContextName, 85 | "--namespace", constants.KubectlNamespace, 86 | "--cluster", constants.KindContextName, 87 | "-f", manifest, 88 | ); err != nil { 89 | pterm.Warning.Printfln("(K8s) Delete() error [non-terminating]: %s", err) 90 | } 91 | } 92 | 93 | // Logs streams logs until canceled for the dsv syncing jobs, based on the label `dsv.delinea.com: syncer`. 94 | func (K8s) Logs() error { 95 | magetoolsutils.CheckPtermDebug() 96 | if _, err := exec.LookPath("stat"); err != nil { 97 | pterm.Error.Printfln("install stern tool manually (see .devcontainer/Dockerfile for install command) to run this") 98 | return errors.New("stern tool not installed yet") 99 | } 100 | pterm.DefaultHeader.Println("(K8s) Logs()") 101 | pterm.Info.Printfln("if you run into log output issues, just try running:\n\n\t\tkubectl logs --context %s --namespace %s --selector 'dsv-filter-name in (dsv-syncer, dsv-injector)' --follow --prefix\n", constants.KindContextName, constants.KubectlNamespace) 102 | pterm.Info.Println("🔍 query without selector:\n\n\tstern --kubeconfig .cache/config --namespace dsv --timestamps . ") 103 | pterm.Info.Println( 104 | "🔍 Manually run stern with the following:\n\n\t", 105 | "stern", 106 | "--namespace", constants.KubectlNamespace, 107 | "--timestamps", 108 | "--selector", "dsv-filter-name in (dsv-syncer, dsv-injector)", 109 | ) 110 | 111 | pterm.Info.Println( 112 | "🔍 Manually run stern againt entire cluster with following:\n\n\t", 113 | "stern", 114 | "--all-namespaces", 115 | "--timestamps", 116 | ".", 117 | ) 118 | pterm.DefaultHeader.Println("kubectl output first") 119 | _ = sh.RunV("kubectl", 120 | "logs", 121 | "--kubeconfig", constants.Kubeconfig, 122 | "--context", constants.KindContextName, 123 | "--namespace", constants.KubectlNamespace, 124 | "--cluster", constants.KindContextName, 125 | "--selector", "dsv-filter-name in (dsv-syncer, dsv-injector)", 126 | // "--follow", 127 | "--since=5m", 128 | "--prefix", 129 | ) 130 | pterm.DefaultHeader.Println("stern streaming output") 131 | return sh.RunV( 132 | "stern", 133 | "--namespace", constants.KubectlNamespace, 134 | "--timestamps", 135 | "--selector", "dsv-filter-name in (dsv-syncer, dsv-injector)", 136 | ) 137 | } 138 | 139 | // 🔍 OutputSecret outputs the base64 decoded values for local minikube style testing. 140 | func (K8s) OutputSecret() { 141 | magetoolsutils.CheckPtermDebug() 142 | for _, secretname := range []string{"user-domain-pass", "user-domain", "pass-domain"} { 143 | response, err := sh.Output( 144 | "kubectl", 145 | "--kubeconfig", constants.Kubeconfig, 146 | "--context", constants.KindContextName, 147 | "--namespace", constants.KubectlNamespace, 148 | "--cluster", constants.KindContextName, 149 | "get", 150 | "secret", secretname, 151 | "-o", `go-template={{.data.password}}`, 152 | "--ignore-not-found", 153 | ) 154 | if err != nil { 155 | pterm.Warning.Printfln("not able to find this %q: %v", secretname, err) 156 | } else { 157 | cleanedoutput := strings.TrimSpace(response) 158 | b, err := base64.StdEncoding.DecodeString(cleanedoutput) 159 | if err != nil { 160 | pterm.Warning.Printfln("issue decoding string: %v", err) 161 | pterm.Debug.Printfln( 162 | "kubectl --kubeconfig %s --context %s --namespace %s --cluster %s get secret %s -o go-template='{{.data.password}}' --ignore-not-found", 163 | constants.Kubeconfig, 164 | constants.KindContextName, 165 | constants.KubectlNamespace, 166 | constants.KindContextName, 167 | secretname, 168 | ) 169 | } else { 170 | pterm.Info.Printfln("🔑 [only for local testing] %q: %q", secretname, string(b)) // ♥️ nested if statements 😀 171 | } 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /magefiles/kind-3-nodes.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Cluster 3 | apiVersion: kind.x-k8s.io/v1alpha4 4 | # One control plane node and three "workers". 5 | # 6 | # While these will not add more real compute capacity and 7 | # have limited isolation, this can be useful for testing 8 | # rolling updates etc. 9 | # 10 | # The API-server and other control plane components will be 11 | # on the control-plane node. 12 | # 13 | # You probably don't need this unless you are testing Kubernetes itself. 14 | nodes: 15 | - role: control-plane 16 | - role: worker 17 | - role: worker 18 | - role: worker 19 | -------------------------------------------------------------------------------- /magefiles/kind/kind.mage.go: -------------------------------------------------------------------------------- 1 | // Kind package contains all the tasks for automation of kind cluster creation and tear down, and the required kubectl commands to correctly use this. 2 | package kind 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "strings" 9 | "time" 10 | 11 | "github.com/DelineaXPM/dsv-k8s/v2/magefiles/constants" 12 | 13 | "github.com/magefile/mage/mg" 14 | "github.com/magefile/mage/sh" 15 | "github.com/pterm/pterm" 16 | mtu "github.com/sheldonhull/magetools/pkg/magetoolsutils" 17 | ) 18 | 19 | // Kind contains the kind cli commands. 20 | type Kind mg.Namespace 21 | 22 | func createCluster() error { 23 | mtu.CheckPtermDebug() 24 | kindargs := []string{ 25 | "create", 26 | "cluster", 27 | "--name", constants.KindClusterName, 28 | "--wait", 29 | "300s", 30 | } 31 | if os.Getenv("KIND_SETUP_CONFIG") != "" { 32 | pterm.Info.Printfln("KIND_SETUP_CONFIG: %s", os.Getenv("KIND_SETUP_CONFIG")) 33 | kindargs = append(kindargs, "--config", os.Getenv("KIND_SETUP_CONFIG")) 34 | } 35 | if err := sh.RunV( 36 | "kind", 37 | kindargs..., 38 | ); err != nil { 39 | return err 40 | } 41 | return nil 42 | } 43 | 44 | func updateKubeconfig() error { 45 | mtu.CheckPtermDebug() 46 | if _, err := os.Stat(constants.Kubeconfig); os.IsNotExist(err) { 47 | if _, err := os.Create(constants.Kubeconfig); err != nil { 48 | pterm.Error.Printfln("unable to create empty placeholder file: %v", err) 49 | } 50 | } 51 | kc, err := sh.Output("kind", "get", "cluster", "kubeconfig", "--name", constants.KindClusterName) 52 | if err != nil { 53 | pterm.Error.Println("unable to get kind cluster info, maybe you need to run mage kind:init first?") 54 | return err 55 | } 56 | 57 | if err := os.WriteFile(constants.Kubeconfig, []byte(kc), constants.PermissionUserReadWriteExecute); err != nil { 58 | pterm.Error.Printfln("unable to write kubeconfig to file: %v", err) 59 | return err 60 | } 61 | pterm.Info.Printfln("kubeconfig updated: %s", constants.Kubeconfig) 62 | // for now this is only going to be run against Kind cluster. 63 | // if err := sh.Run( 64 | // "kubectl", 65 | // "cluster-info", "--context", constants.KindContextName, 66 | // "--cluster", constants.KindContextName, 67 | // ); err != nil { 68 | // return err 69 | // } 70 | return nil 71 | } 72 | 73 | // ➕ Create creates a new Kind cluster and populates a kubeconfig in cachedirectory. 74 | func (Kind) Init() error { 75 | mtu.CheckPtermDebug() 76 | 77 | out, err := sh.Output("kind", "get", "clusters") 78 | if err := err; err != nil { 79 | return err 80 | } 81 | cleanOutput := strings.TrimSpace(out) 82 | matchedCluster := regexp.MustCompile(constants.KindClusterName) 83 | pterm.Debug.Printfln("cleanOutput: %s", cleanOutput) 84 | if !matchedCluster.MatchString(cleanOutput) { 85 | pterm.Info.Printfln("simple match not found, so attempting to recreate cluster") 86 | if err := createCluster(); err != nil { 87 | return err 88 | } 89 | } 90 | dspin, _ := pterm.DefaultSpinner. 91 | WithDelay(time.Second). 92 | WithRemoveWhenDone(true). 93 | WithShowTimer(true). 94 | WithText("Init()\n"). 95 | WithSequence("|", "/", "-", "|", "/", "-", "\\").Start() 96 | dspin.SuccessPrinter.Println("ensuring it's in kubeconfig") 97 | if err := updateKubeconfig(); err != nil { 98 | pterm.Error.Printfln("updateKubeconfig(): %v", err) 99 | } 100 | dspin.UpdateText("setting context") 101 | if err := sh.Run("kubectl", "config", "use-context", constants.KindContextName); err != nil { 102 | dspin.WarningPrinter.Printfln("default context might not be setup correct to new context: %v", err) 103 | } 104 | if err := sh.Run("kubectl", "config", "set-context", "--context", constants.KindContextName, "--current", "--namespace", constants.KubectlNamespace); err != nil { 105 | dspin.WarningPrinter.Printfln("default namespace might not be setup correct to new namespace: %v", err) 106 | } 107 | // Create the namespace if it doesn't exist. 108 | dspin.UpdateText("creating namespace if not exists") 109 | if _, err := sh.Output("kubectl", "get", "namespace", constants.KubectlNamespace); err != nil { 110 | dspin.UpdateText(fmt.Sprintf("namespace does not exist, creating namespace: %s...", constants.KubectlNamespace)) 111 | 112 | if err := sh.Run("kubectl", "create", "namespace", constants.KubectlNamespace); err != nil { 113 | dspin.FailPrinter.Printfln("unable to create namespace: %v", err) 114 | return fmt.Errorf("kubectl create namespace %s: %w", constants.KubectlNamespace, err) 115 | } 116 | dspin.SuccessPrinter.Printfln("namespace created: %s", constants.KubectlNamespace) 117 | } 118 | dspin.UpdateText("pulling docker images") 119 | if err := sh.Run("docker", "pull", constants.DockerImageQualified); err != nil { 120 | dspin.WarningPrinter.Printfln("docker pull: %v", err) 121 | return fmt.Errorf("docker pull: %w", err) 122 | } 123 | dspin.SuccessPrinter.Println("docker image for " + constants.DockerImageQualified) 124 | // Not working right now, can't find nodes for Kind to preload. Not critical so commenting out for now - sheldon. 125 | // Sp.UpdateText("preloading docker image into kind cluster") 126 | // if err := sh.Run("kind", "load", "docker-image", "quay.io/delinea/dsv-k8s:latest"); err != nil { 127 | // return fmt.Errorf("kind load docker-image: %w", err) 128 | // }. 129 | dspin.SuccessPrinter.Println("(Kind) Init()") 130 | _ = dspin.Stop() 131 | return nil 132 | } 133 | 134 | // 🗑️ Destroy tears down the Kind cluster. 135 | func (Kind) Destroy() error { 136 | mtu.CheckPtermDebug() 137 | if err := sh.Run("kind", "delete", "cluster", "--name", constants.KindClusterName); err != nil { 138 | pterm.Error.Printfln("kind delete error: %v", err) 139 | return err 140 | } 141 | if err := sh.Run("kubectl", "config", "unset", fmt.Sprintf("clusters.%s", constants.KindContextName)); err != nil { 142 | pterm.Warning.Printfln("default context might not be setup correct to new context: %v", err) 143 | } 144 | 145 | pterm.Success.Println("(Kind) Destroy()") 146 | return nil 147 | } 148 | -------------------------------------------------------------------------------- /magefiles/mage.go: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | 3 | // This file lets you run mage with a no-install option as long as you have go. 4 | // To invoke just run go run main.go [task] [parameters] 5 | // To use mage directly, install it, then run mage [task] [parameters] 6 | package main 7 | 8 | import ( 9 | "os" 10 | 11 | "github.com/magefile/mage/mage" 12 | ) 13 | 14 | func main() { os.Exit(mage.Main()) } 15 | -------------------------------------------------------------------------------- /magefiles/magefile.go: -------------------------------------------------------------------------------- 1 | // ⚡ Core Mage Tasks. 2 | package main 3 | 4 | import ( 5 | "os" 6 | "os/exec" 7 | "runtime" 8 | 9 | "github.com/DelineaXPM/dsv-k8s/v2/magefiles/constants" 10 | "github.com/bitfield/script" 11 | 12 | // mage:import 13 | "github.com/DelineaXPM/dsv-k8s/v2/magefiles/helm" 14 | // mage:import 15 | "github.com/DelineaXPM/dsv-k8s/v2/magefiles/k8s" 16 | // mage:import 17 | _ "github.com/DelineaXPM/dsv-k8s/v2/magefiles/kind" 18 | // mage:import 19 | _ "github.com/DelineaXPM/dsv-k8s/v2/magefiles/minikube" 20 | // mage:import 21 | _ "github.com/DelineaXPM/dsv-k8s/v2/magefiles/vault" 22 | "github.com/magefile/mage/mg" 23 | "github.com/magefile/mage/sh" 24 | "github.com/pterm/pterm" 25 | "github.com/sheldonhull/magetools/ci" 26 | "github.com/sheldonhull/magetools/fancy" 27 | "github.com/sheldonhull/magetools/pkg/magetoolsutils" 28 | 29 | // mage:import 30 | "github.com/sheldonhull/magetools/gotools" 31 | ) 32 | 33 | // createDirectories creates the local working directories for build artifacts and tooling. 34 | func createDirectories() error { 35 | for _, dir := range []string{constants.ArtifactDirectory, constants.CacheDirectory, constants.ConfigsDirectory} { 36 | if err := os.MkdirAll(dir, constants.PermissionUserReadWriteExecute); err != nil { 37 | pterm.Error.Printf("failed to create dir: [%s] with error: %v\n", dir, err) 38 | 39 | return err 40 | } 41 | pterm.Success.Printf("✅ [%s] dir created\n", dir) 42 | } 43 | 44 | return nil 45 | } 46 | 47 | // Init runs multiple tasks to initialize all the requirements for running a project for a new contributor. 48 | func Init() error { //nolint:deadcode // Not dead, it's alive. 49 | fancy.IntroScreen(ci.IsCI()) 50 | pterm.Success.Println("running Init()...") 51 | 52 | mg.SerialDeps( 53 | Clean, 54 | createDirectories, 55 | (gotools.Go{}.Tidy), 56 | ) 57 | 58 | _, err := exec.LookPath("aqua") 59 | if err != nil && os.IsNotExist(err) { 60 | pterm.Error.Printfln("unable to resolve aqua cli tool, please install for automated project tooling setup: https://aquaproj.github.io/docs/tutorial-basics/quick-start#install-aqua") 61 | return err 62 | } 63 | 64 | if ci.IsCI() { 65 | installArgs := []string{} 66 | 67 | if mg.Verbose() { 68 | installArgs = append(installArgs, "--log-level") 69 | installArgs = append(installArgs, "debug") 70 | } 71 | installArgs = append(installArgs, "install") 72 | installArgs = append(installArgs, "aqua") 73 | if err := sh.RunV("aqua", installArgs...); err != nil { 74 | pterm.Error.Printfln("aqua-ci%v", err) 75 | return err 76 | } 77 | pterm.Debug.Println("CI detected, done with init") 78 | return nil 79 | } 80 | 81 | pterm.DefaultSection.Println("Aqua install (any first packages)") 82 | if err := sh.RunV("aqua", "install", "--tags", "first"); err != nil { 83 | pterm.Warning.Printfln("aqua install failed, continuing: %v", err) 84 | } 85 | pterm.Success.Println("aqua install --tags first complete") 86 | pterm.DefaultSection.Println("Aqua install remaining tools") 87 | if err := sh.RunV("aqua", "install"); err != nil { 88 | pterm.Warning.Printfln("aqua install failed, continuing: %v", err) 89 | } 90 | pterm.Success.Println("aqua install complete") 91 | // These can run in parallel as different toolchains. 92 | mg.SerialDeps( 93 | (InstallTrunk), 94 | (TrunkInit), 95 | ) 96 | pterm.Info.Printfln("Initializing .cache/ directory with copies of Kubernetes YAML + Helm charts.\nUse this to edit your local configurations for minikube based testing") 97 | mg.Deps( 98 | k8s.K8s{}.Init, 99 | helm.Helm{}.Init, 100 | ) 101 | return nil 102 | } 103 | 104 | // InstallTrunk installs trunk.io tooling if it isn't already found. 105 | func InstallTrunk() error { 106 | magetoolsutils.CheckPtermDebug() 107 | pterm.DefaultSection.Println("InstallTrunk()") 108 | if runtime.GOOS == "windows" { 109 | pterm.Warning.Println("InstallTrunk() trunk.io not supported on windows, skipping") 110 | return nil 111 | } 112 | _, err := exec.LookPath("trunk") 113 | if err != nil { 114 | // if os.IsNotExist(err) { 115 | pterm.Warning.Printfln("unable to resolve aqua cli tool, please install for automated project tooling setup: https://aquaproj.github.io/docs/tutorial-basics/quick-start#install-aqua") 116 | _, err := script.Exec("curl https://get.trunk.io -fsSL").Exec("bash -s -- -y").Stdout() 117 | if err != nil { 118 | return err 119 | } 120 | // } 121 | } else { 122 | pterm.Success.Printfln("trunk.io already installed, skipping") 123 | } 124 | return nil 125 | } 126 | 127 | // TrunkInit ensures the required runtimes are installed. 128 | func TrunkInit() error { 129 | return sh.RunV("trunk", "install") 130 | } 131 | 132 | // 🗑️ Clean removes just the artifacts (generated by goreleaser). 133 | func Clean() { 134 | pterm.Success.Println("Cleaning...") 135 | for _, dir := range []string{constants.ArtifactDirectory} { 136 | err := os.RemoveAll(dir) 137 | if err != nil { 138 | pterm.Error.Printf("failed to removeall: [%s] with error: %v\n", dir, err) 139 | } 140 | pterm.Success.Printf("🧹 [%s] dir removed\n", dir) 141 | } 142 | mg.Deps(createDirectories) 143 | } 144 | 145 | // 🗑️ DeepClean removes artifacts, as well as local cached working files and generated credential artifacts. 146 | func DeepClean() { 147 | pterm.Success.Println("Cleaning...") 148 | for _, dir := range []string{constants.ArtifactDirectory, constants.CacheDirectory, constants.ConfigsDirectory} { 149 | err := os.RemoveAll(dir) 150 | if err != nil { 151 | pterm.Error.Printf("failed to removeall: [%s] with error: %v\n", dir, err) 152 | } 153 | pterm.Success.Printf("🧹 [%s] dir removed\n", dir) 154 | } 155 | mg.Deps(createDirectories) 156 | } 157 | -------------------------------------------------------------------------------- /pkg/config/credentials.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "os" 8 | 9 | "github.com/DelineaXPM/dsv-sdk-go/v2/vault" 10 | ) 11 | 12 | // Credentials is a mapping of credentialName to dsv-sdk-go/vault/Configuration objects 13 | type Credentials map[string]struct { 14 | vault.Configuration 15 | } 16 | 17 | // GetCredentials opens the credentialsFile and calls GetCredentialsFromFile on the resulting file 18 | func GetCredentials(credentialsFilePath string) (*Credentials, error) { 19 | if credentialsFile, err := os.Open(credentialsFilePath); err != nil { 20 | return nil, fmt.Errorf("unable to open configuration file '%s': %w", credentialsFilePath, err) 21 | } else { 22 | defer credentialsFile.Close() 23 | return GetCredentialsFromFile(credentialsFile) 24 | } 25 | } 26 | 27 | // GetCredentialsFromFile parses the credentialsFile and returns the resulting Credentials object 28 | func GetCredentialsFromFile(credentialsFile *os.File) (*Credentials, error) { 29 | if contents, err := io.ReadAll(credentialsFile); err != nil { 30 | return nil, fmt.Errorf("unable to read configuration file '%s': %w", credentialsFile.Name(), err) 31 | } else { 32 | return MakeCredentials(contents) 33 | } 34 | } 35 | 36 | func MakeCredentials(credentialJSON []byte) (*Credentials, error) { 37 | credentials := new(Credentials) 38 | 39 | if err := json.Unmarshal(credentialJSON, credentials); err != nil { 40 | return nil, fmt.Errorf("unable to unmarshal configuration: %w", err) 41 | } else { 42 | return credentials, nil 43 | } 44 | } 45 | 46 | // Names returns the list of credential names 47 | func (credentials Credentials) Names() []string { 48 | names := make([]string, 0, len(credentials)) 49 | 50 | for name := range credentials { 51 | names = append(names, name) 52 | } 53 | return names 54 | } 55 | -------------------------------------------------------------------------------- /pkg/config/credentials_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "testing" 7 | ) 8 | 9 | // Ensure log output doesn't pollute tests. 10 | func TestMain(m *testing.M) { 11 | log.SetOutput(io.Discard) 12 | } 13 | 14 | func TestMakeCredentialsValid(t *testing.T) { 15 | if _, err := MakeCredentials([]byte(` 16 | { 17 | "a": { 18 | "credentials": { 19 | "clientId": "x", 20 | "clientSecret": "y" 21 | }, 22 | "tenant": "i" 23 | }, 24 | "b": { 25 | "credentials": { 26 | "clientId": "x", 27 | "clientSecret": "y" 28 | }, 29 | "tenant": "j" 30 | } 31 | }`)); err != nil { 32 | t.Errorf("MakeCredentials should not have failed") 33 | } 34 | } 35 | 36 | func TestMakeCredentialsInvalid(t *testing.T) { 37 | if _, err := MakeCredentials([]byte(` 38 | { 39 | "a": { 40 | "credentials": { 41 | "clientId": "x", 42 | }, 43 | "tenant": "i" 44 | } 45 | }`)); err == nil { 46 | t.Errorf("MakeCredentials should have failed") 47 | } 48 | } 49 | 50 | func TestCredentials(t *testing.T) { 51 | if credentials, err := MakeCredentials([]byte(` 52 | { 53 | "a": { 54 | "credentials": { 55 | "clientId": "x", 56 | "clientSecret": "y" 57 | }, 58 | "tenant": "i" 59 | }, 60 | "b": { 61 | "credentials": { 62 | "clientId": "x", 63 | "clientSecret": "y" 64 | }, 65 | "tenant": "j" 66 | }, 67 | "c": { 68 | "credentials": { 69 | "clientId": "x", 70 | "clientSecret": "y" 71 | }, 72 | "tenant": "k" 73 | } 74 | } 75 | `)); err != nil { 76 | t.Error(err) 77 | } else { 78 | if len(*credentials) != 3 { 79 | t.Errorf("expected 3 credential, got %d", len(*credentials)) 80 | } 81 | if names := credentials.Names(); len(names) != 3 { 82 | t.Errorf("expected 3 name, got %d", len(names)) 83 | } else { 84 | switch names[0] { 85 | case "a": 86 | case "b": 87 | case "c": 88 | break 89 | default: 90 | t.Errorf("unexpected name '%s'", names[0]) 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /pkg/injector/inject.go: -------------------------------------------------------------------------------- 1 | package injector 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/DelineaXPM/dsv-k8s/v2/pkg/config" 7 | patch "github.com/DelineaXPM/dsv-k8s/v2/pkg/patch" 8 | "github.com/rs/zerolog" 9 | v1 "k8s.io/api/admission/v1" 10 | corev1 "k8s.io/api/core/v1" 11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | "k8s.io/apimachinery/pkg/types" 13 | ) 14 | 15 | // Inject adds to, updates or replaces the k8s Secret.Data with dsv Secret.Data (see above) 16 | func Inject(secret corev1.Secret, uID types.UID, credentials config.Credentials, log zerolog.Logger) (*v1.AdmissionResponse, error) { 17 | if jsonPatch, err := patch.GenerateJsonPatch(secret, credentials); err != nil { 18 | log.Error().Err(err). 19 | Str("secret_name", secret.Name). 20 | Str("namespace", secret.Namespace). 21 | Msg("unable to patch secret") 22 | return nil, fmt.Errorf("unable to generate JSON patch for Secret '%s': %w", secret.Name, err) 23 | } else if jsonPatch != nil { 24 | patchType := v1.PatchTypeJSONPatch 25 | 26 | log.Debug().Str("secret_name", secret.Name).Msg("patching secret") 27 | return &v1.AdmissionResponse{ 28 | Allowed: true, 29 | Result: &metav1.Status{ 30 | Status: metav1.StatusSuccess, 31 | }, 32 | UID: uID, 33 | PatchType: &patchType, 34 | Patch: jsonPatch, 35 | }, nil 36 | } else { 37 | log.Debug().Str("secret_name", secret.Name).Msg("no patching required") 38 | return &v1.AdmissionResponse{ 39 | Allowed: true, 40 | Result: &metav1.Status{ 41 | Status: metav1.StatusSuccess, 42 | }, 43 | UID: uID, 44 | }, nil 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /pkg/patch/patch.go: -------------------------------------------------------------------------------- 1 | package patch 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/json" 6 | "fmt" 7 | "strings" 8 | "time" 9 | 10 | "github.com/DelineaXPM/dsv-k8s/v2/pkg/config" 11 | "github.com/DelineaXPM/dsv-sdk-go/v2/vault" 12 | "github.com/mattbaird/jsonpatch" 13 | v1 "k8s.io/api/core/v1" 14 | ) 15 | 16 | /* 17 | credentialsAnnotation contains a name that maps to a set of credentials in Credentials (see below) 18 | setAnnotation, addAnnoation and updateAnnotation contain the path to the DSV Secret 19 | that will be used to modified this Secret. 20 | addAnnotation adds missing fields without overwriting or removing existing fields 21 | updateAnnotation adds and overwrites existing fields but does not remove fields 22 | setAnnotation overwrites fields and removes fields that do not exist in the DSV Secret 23 | */ 24 | const ( 25 | credentialsAnnotation = "dsv.delinea.com/credentials" 26 | setAnnotation = "dsv.delinea.com/set-secret" 27 | addAnnotation = "dsv.delinea.com/add-to-secret" 28 | updateAnnotation = "dsv.delinea.com/update-secret" 29 | tsAnnotation = "dsv.delinea.com/modified" 30 | versionAnnotation = "dsv.delinea.com/version" 31 | ) 32 | 33 | /* 34 | noPatch causes Inject to approve without patching 35 | patchAdd causes Inject to add entries without overwriting or removing existing ones 36 | patchUpdate causes Inject to add and update entries without removing existing ones 37 | patchOverwrite causes Inject to completely overwrite all entries including removal 38 | of existing entries that are not present in the DSV Secret 39 | */ 40 | const ( 41 | noPatch = iota 42 | patchAdd 43 | patchUpdate 44 | patchOverwrite 45 | ) 46 | 47 | // GeneratePatch generates a JSON Patch that applies changes to the k8s Secret based on the DSV Secret that it refers to 48 | func GenerateJsonPatch(secret v1.Secret, credentials config.Credentials) ([]byte, error) { 49 | if patchOperations, err := makePatchOperations(secret, credentials); err != nil { 50 | return nil, err 51 | } else if patchOperations != nil { 52 | if jsonPatch, err := json.Marshal(patchOperations); err != nil { 53 | return nil, fmt.Errorf("unable to marshal JsonPatch: %s", err) 54 | } else { 55 | return jsonPatch, nil 56 | } 57 | } else { 58 | return nil, nil 59 | } 60 | } 61 | 62 | func makePatchOperations(secret v1.Secret, credentials config.Credentials) ([]jsonpatch.JsonPatchOperation, error) { 63 | annotations := secret.ObjectMeta.Annotations 64 | patchMode := noPatch 65 | var secretPath string 66 | var ok bool 67 | 68 | if secretPath, ok = annotations[setAnnotation]; ok { 69 | patchMode = patchOverwrite 70 | } else if secretPath, ok = annotations[addAnnotation]; ok { 71 | patchMode = patchAdd 72 | } else if secretPath, ok = annotations[updateAnnotation]; ok { 73 | patchMode = patchUpdate 74 | } 75 | 76 | if patchMode == noPatch { 77 | return nil, nil 78 | } 79 | 80 | var config vault.Configuration 81 | /* 82 | If there is a credentials annotation, use the credentials by that name 83 | and return an error if there are no credentials for that name. 84 | Otherwise, use the default credentials or, finally, 85 | do nothing if there aren't any. 86 | */ 87 | if name, ok := annotations[credentialsAnnotation]; ok { 88 | if credentials, ok := credentials[name]; ok { 89 | config = credentials.Configuration 90 | } else { 91 | return nil, fmt.Errorf("no credentials for: %s", name) 92 | } 93 | } else if credentials, ok := credentials["default"]; ok { 94 | config = credentials.Configuration 95 | } else { 96 | return nil, nil 97 | } 98 | 99 | var vaultSecret *vault.Secret 100 | 101 | /* 102 | If there's a patch annotation, and a credentials annotation that matches 103 | a set of credentials, use them to get a Vault Secret. 104 | */ 105 | if vault, err := vault.New(config); err != nil { 106 | return nil, fmt.Errorf("configuration error: %s", err) 107 | } else if vaultSecret, err = vault.Secret(secretPath); err != nil { 108 | return nil, fmt.Errorf("unable to get the secret: %s", err) 109 | } 110 | /* 111 | Use the version annotation to determine if the secret has been modified 112 | */ 113 | if vaultSecret.Version == annotations[versionAnnotation] { 114 | return nil, nil 115 | } 116 | /* 117 | CreatePatch returns the difference between the JSON representation of 118 | the DSV Secret Data and the k8s Secret Data, as an RFC 6902 JSON Patch. 119 | */ 120 | vsdj, _ := json.Marshal(vaultSecret.Data) 121 | sdj, _ := json.Marshal(secret.Data) 122 | diff, _ := jsonpatch.CreatePatch(sdj, vsdj) 123 | ops := []jsonpatch.JsonPatchOperation{} 124 | /* 125 | Each patch operation has to be updated so that k8s can apply it to 126 | the entire Secret: 127 | 1) the Path must be relative to /Secret rather than /Secret/Data 128 | 2) the Values must be Base64 encoded 129 | 3) the Operations that conflict with patchMode must be removed 130 | */ 131 | for _, op := range diff { 132 | op.Path = "/data" + op.Path 133 | 134 | switch v := op.Value.(type) { 135 | case []byte: 136 | op.Value = base64.StdEncoding.EncodeToString(v) 137 | case string: 138 | op.Value = base64.StdEncoding.EncodeToString([]byte(v)) 139 | case map[string]interface{}: 140 | if json, err := json.Marshal(v); err != nil { 141 | return nil, fmt.Errorf("unable to marshal value for %s operation on %s: %s", 142 | op.Operation, op.Path, err) 143 | } else { 144 | op.Value = base64.StdEncoding.EncodeToString([]byte(json)) 145 | } 146 | } 147 | // remove operations that conflict with the patchMode 148 | switch op.Operation { 149 | case "replace": 150 | if patchMode == patchAdd { 151 | continue 152 | } 153 | case "remove": 154 | if patchMode == patchAdd || patchMode == patchUpdate { 155 | continue 156 | } 157 | } 158 | ops = append(ops, op) 159 | } 160 | /* 161 | If there is at least one patch operation add an operation to replace update the annotations 162 | */ 163 | if len(ops) > 0 { 164 | operation := "add" 165 | if annotations[versionAnnotation] != "" { 166 | operation = "replace" 167 | } 168 | annotation_ops := []jsonpatch.JsonPatchOperation{ 169 | { 170 | Operation: operation, 171 | Path: "/metadata/annotations/" + strings.Replace(tsAnnotation, "/", "~1", -1), 172 | Value: time.Now().UTC().Format(time.UnixDate), 173 | }, 174 | { 175 | Operation: operation, 176 | Path: "/metadata/annotations/" + strings.Replace(versionAnnotation, "/", "~1", -1), 177 | Value: vaultSecret.Version, 178 | }, 179 | } 180 | ops = append(ops, annotation_ops...) 181 | return ops, nil 182 | } 183 | return nil, nil 184 | } 185 | -------------------------------------------------------------------------------- /pkg/syncer/sync.go: -------------------------------------------------------------------------------- 1 | // package syncer handles the syncing actions for secret reading, patching and injecting into kubernetes secrets. 2 | package syncer 3 | 4 | import ( 5 | "context" 6 | "fmt" 7 | "sync" 8 | "time" 9 | 10 | "github.com/rs/zerolog" 11 | 12 | "github.com/DelineaXPM/dsv-k8s/v2/internal/k8s" 13 | "github.com/DelineaXPM/dsv-k8s/v2/pkg/config" 14 | "github.com/DelineaXPM/dsv-k8s/v2/pkg/patch" 15 | corev1 "k8s.io/api/core/v1" 16 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 | "k8s.io/apimachinery/pkg/types" 18 | ) 19 | 20 | // Sync does the same thing as Inject, but by iterating over the existing k8s Secrets 21 | func Sync(config k8s.Config, namespace string, credentials config.Credentials, log zerolog.Logger) error { 22 | secretsClient, err := k8s.GetSecretsClient(config, namespace) 23 | if err != nil { 24 | return fmt.Errorf("[ERROR] error getting a Kubernetes Client API Secrets Client: %w", err) 25 | } 26 | log.Debug().Str("namespace", namespace).Msg("getting a list of secrets") 27 | 28 | if secrets, err := secretsClient.List(context.TODO(), metav1.ListOptions{}); err != nil { 29 | return fmt.Errorf("[ERROR] unable to get a list of secrets in namespace %q: %w", namespace, err) 30 | } else { 31 | wg := sync.WaitGroup{} 32 | 33 | log.Info().Msgf("processing %d Secrets", len(secrets.Items)) 34 | for _, secret := range secrets.Items { 35 | log.Debug().Msgf("processing k8s Secret %q", secret.Name) 36 | wg.Add(1) 37 | go pp(secret, credentials, config, &wg, log) 38 | } // TODO: put an upper limit on the number of goroutines to spawn in one go 39 | wg.Wait() 40 | if secrets.RemainingItemCount != nil && *secrets.RemainingItemCount > 0 { 41 | log.Warn().Msgf("this server pages; %d Secrets were not processed", secrets.RemainingItemCount) 42 | } 43 | } 44 | return nil 45 | } 46 | 47 | // pp possibly patches the Kubernetes Secret 48 | func pp(secret corev1.Secret, credentials config.Credentials, config k8s.Config, wg *sync.WaitGroup, log zerolog.Logger) { 49 | defer wg.Done() 50 | start := time.Now() 51 | defer func() { 52 | log.Debug(). 53 | Dur("duration", time.Since(start)). 54 | Str("secret_name", secret.Name). 55 | Bool("patched", true). 56 | Msg("possible patch complete") 57 | }() 58 | 59 | if jsonPatch, err := patch.GenerateJsonPatch(secret, credentials); err != nil { //nolint:nestif // known issue, but will would be refactoring for the future 60 | log.Error(). 61 | Err(err). 62 | Str("secret_name", secret.Name). 63 | Str("secret_namespace", secret.Namespace). 64 | Bool("patched", false). 65 | Msg("patch.GenerateJsonPatch") 66 | } else if jsonPatch == nil { 67 | log.Debug(). 68 | Str("secret_name", secret.Name). 69 | Str("secret_namespace", secret.Namespace). 70 | Bool("patched", false). 71 | Msg("GenerateJsonPatch") 72 | } else { 73 | if secretsClient, err := k8s.GetSecretsClient(config, secret.Namespace); err != nil { 74 | log.Error(). 75 | Err(err). 76 | Str("secret_name", secret.Name). 77 | Str("secret_namespace", secret.Namespace). 78 | Bool("patched", false). 79 | Msg("k8s.GetSecretsClient") 80 | } else { 81 | if _, err := secretsClient.Patch( 82 | context.TODO(), secret.Name, types.JSONPatchType, jsonPatch, metav1.PatchOptions{}, 83 | ); err != nil { 84 | log.Error(). 85 | Err(err). 86 | Str("secret_name", secret.Name). 87 | Str("secret_namespace", secret.Namespace). 88 | Bool("patched", true). 89 | Msg("secretsClient.Patch") 90 | } else { 91 | log.Info(). 92 | Str("secret_name", secret.Name). 93 | Str("secret_namespace", secret.Namespace). 94 | Bool("patched", true). 95 | Msg("patch successful") 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["DelineaXPM/renovate-config:security"], 4 | "packageRules": [ 5 | { 6 | "matchManagers": ["gomod"], 7 | "matchPackageNames": [ 8 | "helm.sh/helm/v3", 9 | "github.com/mittwald/go-helm-client" 10 | ], 11 | "groupName": "risky-breaking-changes", 12 | "enabled": false 13 | }, 14 | { 15 | "matchManagers": ["gomod"], 16 | "matchPackageNames": ["k8s.io/api"], 17 | "groupName": "risky-k8s-changes", 18 | "matchUpdateTypes": ["major", "minor"], 19 | "enabled": false 20 | } 21 | ] 22 | } 23 | --------------------------------------------------------------------------------