From 2476d05efc3e9054c9719135c4aaac5089100407 Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Tue, 12 Nov 2024 12:09:27 +0000 Subject: [PATCH] fix: enable commit signing (#21) --- .gitleaks.toml | 6 +- README.md | 90 ++++++++++--- snyk-universal-broker/templates/_helpers.tpl | 7 ++ .../templates/commit-signing-secret.yaml | 22 ++++ .../templates/statefulset.yaml | 4 + .../tests/commit_signing_test.yaml | 119 ++++++++++++++++++ snyk-universal-broker/values.schema.json | 37 ++++++ snyk-universal-broker/values.yaml | 16 +++ 8 files changed, 284 insertions(+), 17 deletions(-) create mode 100644 snyk-universal-broker/templates/commit-signing-secret.yaml create mode 100644 snyk-universal-broker/tests/commit_signing_test.yaml diff --git a/.gitleaks.toml b/.gitleaks.toml index cebbc93..f54224c 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -2,4 +2,8 @@ useDefault = true [allowList] -paths = ['''snyk-universal-broker/tests'''] \ No newline at end of file +paths = [ + '''snyk-universal-broker/tests''', + '''snyk-universal-broker/values.schema.json''', + '''README.md''' + ] diff --git a/README.md b/README.md index e857cd6..57a0fd8 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,58 @@ image: - name: containers-company-secret ``` +### Commit Signing + +Snyk Broker commit signing is in [Early Access](https://docs.snyk.io/getting-started/snyk-release-process#open-beta). If you would like to use this feature, contact your Snyk representative or team. + +This feature requires a GitHub Account that has a GPG key configured for commit signing. + +#### Via Helm + +Provide the GPG key (exported as an ASCII armored version), the passphrase, and associated email/user: + +```yaml +commitSigning: + name: "Account Name" + email: "email@company.tld" + passphrase: "passphrase" + gpgPrivateKey: -----BEGIN PGP PRIVATE KEY BLOCK-----\n....\n-----END PGP PRIVATE KEY BLOCK----- +``` + +The Universal Broker Helm Chart creates this secret for you. + +#### Via External Secret + +First create or otherwise ensure the secret exists: + +```yaml +kind: Secret +apiVersion: v1 +metadata: + name: gpg-signing-key +data: + GPG_PRIVATE_KEY: "-----BEGIN PRIVATE KEY BLOCK-----..." + GPG_PASSPRHASE: "passphrase" + GIT_COMMITTER_EMAIL: "user@comapny.io" + GIT_COMMITTER_NAME: "User Name" +``` + +The keys **must** match the example above. + +Then set values within `.Values.commitSigningSecret` to reference this external Secret: +```yaml +commitSigning: + enabled: true +commitSigningSecret: + name: gpg-signing-key +``` + +Commit signing is enabled if the following entry appears in Universal Broker logs at startup: + +``` +loading commit signing rules (enabled=true, rulesCount=5) +``` + ## Parameters ### Snyk Broker parameters @@ -190,22 +242,28 @@ Credential References should contain one or more key/value pairs where each key helm install ... --set credentialReferences.MY_GITHUB_TOKEN= ``` -| Name | Description | Value | -| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -| `brokerClientUrl` | is the address of the broker. This needs to be the address of itself. In the case of Kubernetes, you need to ensure that you are pointing to the cluster ingress you have setup. | `""` | -| `region` | Optionally specify a Snyk Region - e.g. "eu" for "SNYK-EU-01". Defaults to "SNYK-US-01", app.snyk.io | `""` | -| `preflightChecks.enabled` | broker client preflight checks | `true` | -| `deploymentId` | Obtained by installing the Broker App | `""` | -| `clientId` | Obtained by installing the Broker App | `""` | -| `clientSecret` | Obtained by installing the Broker App | `""` | -| `platformAuthSecret.name` | Optionally provide an external secret containing three keys: `DEPLOYMENT_ID`, `CLIENT_ID` and `CLIENT_SECRET` | `""` | -| `credentialReferences` | Credential References to pass to Broker | `{}` | -| `credentialReferencesSecret.name` | Optionally provide a pre-existing secret with SCM credential reference data | `""` | -| `acceptCode` | Set to false to block Broker rules relating to Snyk Code analysis | `true` | -| `acceptAppRisk` | Set to false to block Broker rules relating to AppRisk | `true` | -| `acceptIaC` | Defaults to "tf,yaml,yml,json,tpl". Optionally remove any extensions not required. Must be comma separated. Set to "" to block Broker rules relating to Snyk IaC analysis | `""` | -| `acceptCustomPrTemplates` | Set to false to block Broker rules relating to Snyk Custom PR Templates | `true` | -| `acceptLargeManifests` | Set to false to block Broker rules relating to fetching of large files from GitHub/GitHub Enterprise | `true` | +| Name | Description | Value | +| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `brokerClientUrl` | is the address of the broker. This needs to be the address of itself. In the case of Kubernetes, you need to ensure that you are pointing to the cluster ingress you have setup. | `""` | +| `region` | Optionally specify a Snyk Region - e.g. "eu" for "SNYK-EU-01". Defaults to "SNYK-US-01", app.snyk.io | `""` | +| `preflightChecks.enabled` | broker client preflight checks | `true` | +| `deploymentId` | Obtained by installing the Broker App | `""` | +| `clientId` | Obtained by installing the Broker App | `""` | +| `clientSecret` | Obtained by installing the Broker App | `""` | +| `platformAuthSecret.name` | Optionally provide an external secret containing three keys: `DEPLOYMENT_ID`, `CLIENT_ID` and `CLIENT_SECRET` | `""` | +| `credentialReferences` | Credential References to pass to Broker | `{}` | +| `credentialReferencesSecret.name` | Optionally provide a pre-existing secret with SCM credential reference data | `""` | +| `acceptCode` | Set to false to block Broker rules relating to Snyk Code analysis | `true` | +| `acceptAppRisk` | Set to false to block Broker rules relating to AppRisk | `true` | +| `acceptIaC` | Defaults to "tf,yaml,yml,json,tpl". Optionally remove any extensions not required. Must be comma separated. Set to "" to block Broker rules relating to Snyk IaC analysis | `""` | +| `acceptCustomPrTemplates` | Set to false to block Broker rules relating to Snyk Custom PR Templates | `true` | +| `acceptLargeManifests` | Set to false to block Broker rules relating to fetching of large files from GitHub/GitHub Enterprise | `true` | +| `commitSigning.enabled` | Set to true to sign any commits made to GitHub or GitHub Enterprise. Requires `name`, `email`, `passphrase`, `privateKey` _or_ `commitSigningSecret` | `false` | +| `commitSigning.name` | The name to associate with any signed commits | `""` | +| `commitSigning.email` | The email to associate with any signed commits | `""` | +| `commitSigning.gpgPrivateKey` | The GPG private key to sign commits with (ASCII armored version) | `""` | +| `commitSigning.passphrase` | The passpharse for the GPG key | `""` | +| `commitSigningSecret` | An external secret containing `GIT_COMMITTER_NAME`, `GIT_COMMITTER_EMAIL`, `GPG_PASSPHRASE` and `GPG_PRIVATE_KEY` | `""` | ### Networking Parameters diff --git a/snyk-universal-broker/templates/_helpers.tpl b/snyk-universal-broker/templates/_helpers.tpl index 6c6af90..ab6e5ff 100644 --- a/snyk-universal-broker/templates/_helpers.tpl +++ b/snyk-universal-broker/templates/_helpers.tpl @@ -104,6 +104,13 @@ Each credential must be a valid env var, with associated string value {{- end }} {{/* +Create a name for the Commit Signing secret, using a provided override if present +*/}} +{{- define "snyk-broker.commitSigningSecretName" -}} +{{- .Values.commitSigningSecret.name | default ( include "snyk-broker.genericSecretName" (dict "Context" . "secretName" "commit-signing-secret" ) ) -}} +{{- end }} + +{{/*}} Snyk Broker ACCEPT_ vars */}} {{- define "snyk-broker.accepts" -}} diff --git a/snyk-universal-broker/templates/commit-signing-secret.yaml b/snyk-universal-broker/templates/commit-signing-secret.yaml new file mode 100644 index 0000000..0c74b62 --- /dev/null +++ b/snyk-universal-broker/templates/commit-signing-secret.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.commitSigning.enabled ( not .Values.commitSigningSecret.name ) }} +{{- $unsetKeys:= list -}} +{{- range ( unset .Values.commitSigning "enabled" | keys ) -}} +{{- if not (get $.Values.commitSigning . ) }} +{{- $unsetKeys = append $unsetKeys . -}} +{{- end }} +{{- end }} +{{- if gt (len $unsetKeys) 0 -}} +{{- fail (printf "Missing value(s) under .Values.commitSigning for: %s" (join "," $unsetKeys) ) }} +{{- end }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "snyk-broker.commitSigningSecretName" . }} + namespace: {{ .Release.Namespace }} +labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} +data: + GPG_PRIVATE_KEY: {{ .Values.commitSigning.gpgPrivateKey | b64enc }} + GPG_PASSPHRASE: {{ .Values.commitSigning.passphrase | b64enc }} + GIT_COMMITER_NAME: {{ .Values.commitSigning.name | b64enc }} + GIT_COMMITER_EMAIL: {{ .Values.commitSigning.email | b64enc }} +{{- end }} diff --git a/snyk-universal-broker/templates/statefulset.yaml b/snyk-universal-broker/templates/statefulset.yaml index 1aeca25..cc905c1 100644 --- a/snyk-universal-broker/templates/statefulset.yaml +++ b/snyk-universal-broker/templates/statefulset.yaml @@ -94,6 +94,10 @@ spec: - secretRef: name: {{ include "snyk-broker.credentialReferencesSecretName" . }} {{- end }} + {{- if .Values.commitSigning.enabled }} + - secretRef: + name: {{ include "snyk-broker.commitSigningSecretName" . }} + {{- end }} volumeMounts: {{- if or .Values.caCert .Values.caCertSecret.name }} - name: {{ .Release.Name }}-cacert-volume diff --git a/snyk-universal-broker/tests/commit_signing_test.yaml b/snyk-universal-broker/tests/commit_signing_test.yaml new file mode 100644 index 0000000..5ade6d0 --- /dev/null +++ b/snyk-universal-broker/tests/commit_signing_test.yaml @@ -0,0 +1,119 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/helm-unittest/helm-unittest/main/schema/helm-testsuite.json +suite: Commit Signing +templates: + - statefulset.yaml + - commit-signing-secret.yaml +values: +- ../values.yaml +- fixtures/default_values.yaml + +tests: + - it: enables commit signing with inline private key + set: + commitSigning: + enabled: true + name: "Bot" + email: "bot@corp.io" + passphrase: "fake" + gpgPrivateKey: "-----BEGIN GPG PRIVATE KEY BLOCK-----\nFAKE\n-----END GPG PRIVATE KEY BLOCK-----" + asserts: + - contains: + path: spec.template.spec.containers[0].envFrom + content: + secretRef: + name: RELEASE-NAME-commit-signing-secret + template: statefulset.yaml + - equal: + path: data.GPG_PRIVATE_KEY + value: LS0tLS1CRUdJTiBHUEcgUFJJVkFURSBLRVkgQkxPQ0stLS0tLQpGQUtFCi0tLS0tRU5EIEdQRyBQUklWQVRFIEtFWSBCTE9DSy0tLS0t + template: commit-signing-secret.yaml + - equal: + path: data.GPG_PASSPHRASE + value: ZmFrZQ== + template: commit-signing-secret.yaml + - equal: + path: data.GIT_COMMITER_NAME + value: Qm90 + template: commit-signing-secret.yaml + - equal: + path: data.GIT_COMMITER_EMAIL + value: Ym90QGNvcnAuaW8= + template: commit-signing-secret.yaml + + - it: enables commit signing with multiline private key + set: + commitSigning: + enabled: true + name: "Bot" + email: "bot@corp.io" + passphrase: "fake" + gpgPrivateKey: |- + -----BEGIN GPG PRIVATE KEY BLOCK----- + FAKE + -----END GPG PRIVATE KEY BLOCK----- + + asserts: + - contains: + path: spec.template.spec.containers[0].envFrom + content: + secretRef: + name: RELEASE-NAME-commit-signing-secret + template: statefulset.yaml + - equal: + path: data.GPG_PRIVATE_KEY + value: LS0tLS1CRUdJTiBHUEcgUFJJVkFURSBLRVkgQkxPQ0stLS0tLQpGQUtFCi0tLS0tRU5EIEdQRyBQUklWQVRFIEtFWSBCTE9DSy0tLS0t + template: commit-signing-secret.yaml + - equal: + path: data.GPG_PASSPHRASE + value: ZmFrZQ== + template: commit-signing-secret.yaml + - equal: + path: data.GIT_COMMITER_NAME + value: Qm90 + template: commit-signing-secret.yaml + - equal: + path: data.GIT_COMMITER_EMAIL + value: Ym90QGNvcnAuaW8= + template: commit-signing-secret.yaml + + - it: fails if one of the commit signing values is missing and no external secret is specified + set: + commitSigning: + enabled: true + name: "Bot" + email: "bot@corp.io" + gpgPrivateKey: "-----BEGIN GPG PRIVATE KEY BLOCK-----\nFAKE\n-----END GPG PRIVATE KEY BLOCK-----" + asserts: + - failedTemplate: + errorMessage: "Missing value(s) under .Values.commitSigning for: passphrase" + template: commit-signing-secret.yaml + + - it: uses an external secret if specified + set: + commitSigning: + enabled: true + commitSigningSecret: + name: "my-commit-signing-secret" + asserts: + - contains: + path: spec.template.spec.containers[0].envFrom + content: + secretRef: + name: my-commit-signing-secret + template: statefulset.yaml + + - it: uses an external secret if specified, ignoring any values set under commitSigning + set: + commitSigning: + enabled: true + name: "Bot" + email: "bot@corp.io" + commitSigningSecret: + name: "my-commit-signing-secret" + asserts: + - contains: + path: spec.template.spec.containers[0].envFrom + content: + secretRef: + name: my-commit-signing-secret + template: statefulset.yaml diff --git a/snyk-universal-broker/values.schema.json b/snyk-universal-broker/values.schema.json index dc22fa7..7b8d444 100644 --- a/snyk-universal-broker/values.schema.json +++ b/snyk-universal-broker/values.schema.json @@ -294,6 +294,43 @@ } } }, + "commitSigning": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "email": { + "type": "string", + "oneOf": [ + { + "maxLength": 0 + }, + { + "format": "email" + } + ] + }, + "gpgPrivateKey": { + "type": "string", + "regex": "^$|^\\s*-----BEGIN PGP PRIVATE KEY BLOCK-----(?:.|\\s)*-----END PGP PRIVATE KEY BLOCK-----\\s*$" + }, + "passphrase": { + "type": "string" + } + } + }, + "commitSigningSecret": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, "global": { "type": "object", "additionalProperties": true diff --git a/snyk-universal-broker/values.yaml b/snyk-universal-broker/values.yaml index b7fd7fb..b0e6887 100644 --- a/snyk-universal-broker/values.yaml +++ b/snyk-universal-broker/values.yaml @@ -60,6 +60,22 @@ acceptIaC: "tf,yaml,yml,json,tpl" acceptCustomPrTemplates: true acceptLargeManifests: true +## @param commitSigning.enabled [default: false] Set to true to sign any commits made to GitHub or GitHub Enterprise. Requires `name`, `email`, `passphrase`, `privateKey` _or_ `commitSigningSecret` +## @param commitSigning.name [string] The name to associate with any signed commits +## @param commitSigning.email [string] The email to associate with any signed commits +## @param commitSigning.gpgPrivateKey [string] The GPG private key to sign commits with (ASCII armored version) +## @param commitSigning.passphrase [string] The passpharse for the GPG key +## @param commitSigningSecret [string] An external secret containing `GIT_COMMITTER_NAME`, `GIT_COMMITTER_EMAIL`, `GPG_PASSPHRASE` and `GPG_PRIVATE_KEY` +commitSigning: + enabled: false + name: "" + email: "" + gpgPrivateKey: "" + passphrase: "" + +commitSigningSecret: + name: "" + ## @section Networking Parameters ## @param containerPort Port to open for HTTP in Broker