diff --git a/.gitignore b/.gitignore index 04c9455d6..785353b97 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,7 @@ dist/ # Dev configuration associated with authentication */*/*/secret.yaml -gha-creds-*.json \ No newline at end of file +gha-creds-*.json + +deployment/chainloop/charts/ +values.local.yaml diff --git a/deployment/chainloop/.helmignore b/deployment/chainloop/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/deployment/chainloop/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/deployment/chainloop/Chart.lock b/deployment/chainloop/Chart.lock new file mode 100644 index 000000000..37e0ec564 --- /dev/null +++ b/deployment/chainloop/Chart.lock @@ -0,0 +1,12 @@ +dependencies: +- name: common + repository: https://charts.bitnami.com/bitnami + version: 2.2.4 +- name: postgresql + repository: https://charts.bitnami.com/bitnami + version: 12.2.7 +- name: vault + repository: https://helm.releases.hashicorp.com + version: 0.24.0 +digest: sha256:1115e837f9f10727a71e94e1da5e048abe3266123b2c0819e45729e2f0159219 +generated: "2023-04-12T12:23:34.711511918+02:00" diff --git a/deployment/chainloop/Chart.yaml b/deployment/chainloop/Chart.yaml new file mode 100644 index 000000000..8c8d41e09 --- /dev/null +++ b/deployment/chainloop/Chart.yaml @@ -0,0 +1,22 @@ +apiVersion: v2 +name: chainloop +description: Chainloop is an open source software supply chain control plane, a single source of truth for artifacts plus a declarative attestation crafting process. + +type: application +version: 0.1.0 +appVersion: "v0.8.96" + +dependencies: + - name: common + repository: https://charts.bitnami.com/bitnami + tags: + - bitnami-common + version: 2.x.x + - condition: postgresql.enabled + name: postgresql + repository: https://charts.bitnami.com/bitnami + version: 12.x.x + - condition: development + name: vault + repository: https://helm.releases.hashicorp.com + version: 0.24.x \ No newline at end of file diff --git a/deployment/chainloop/README.md b/deployment/chainloop/README.md new file mode 100644 index 000000000..a15c9000e --- /dev/null +++ b/deployment/chainloop/README.md @@ -0,0 +1,329 @@ +# Chainloop Helm Chart + +[Chainloop](https://github.com/chainloop-dev/chainloop) is an open-source software supply chain control plane, a single source of truth for artifacts plus a declarative attestation crafting process. + +## Introduction + +This chart bootstraps a [Chainloop](https://github.com/chainloop-dev/chainloop) deployment on a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.2.0+ +- PV provisioner support in the underlying infrastructure +- ReadWriteMany volumes for deployment scaling + +## TLDR; + +Deploy Chainloop in [development mode](#development) by running + +```sh +helm install [RELEASE_NAME] . \ + --set development=true \ + --set controlplane.auth.oidc.url=[OIDC URL] \ + --set controlplane.auth.oidc.clientID=[clientID] \ + --set controlplane.auth.oidc.clientSecret=[clientSecret] +``` + +## Installing the Chart + +This chart comes in **two flavors**, `standard` and [`development`](#development). + +### Standard (default) + +![Deployment](../../docs/img/deployment.png) + +The default deployment mode relies on external dependencies to be available in advance. + +The Helm Chart in this mode includes + +- Chainloop [Controlplane](../../app/controlplane) +- Chainloop [Artifact proxy](../../app/artifact-cas) +- A PostgreSQL dependency enabled by default + +During installation, you'll need to provide + +- Open ID Connect Identity Provider (IDp) settings i.e [Auth0 settings](https://auth0.com/docs/get-started/applications/application-settings#basic-information) +- Connection settings for a secrets storage backend, either [Hashicorp Vault](https://www.vaultproject.io/) or [AWS Secret Manager](https://aws.amazon.com/secrets-manager) +- ECDSA (ES512) key-pair used for Controlplane <-> CAS Authentication + +You can generate the ECDSA key-pair by running + +```sh +# Private Key (private.ec.key) +openssl ecparam -name secp521r1 -genkey -noout -out private.ec.key +# Public Key (public.pem) +openssl ec -in private.ec.key -pubout -out public.pem +``` + +#### Installation Examples + +Deploy Chainloop configured to talk to the bundled PostgreSQL an external OIDC IDp and a Vault instance. + +```sh +helm install [RELEASE_NAME] . \ + # Open ID Connect (OIDC) + --set controlplane.auth.oidc.url=[OIDC URL] \ + --set controlplane.auth.oidc.clientID=[clientID] \ + --set controlplane.auth.oidc.clientSecret=[clientSecret] \ + # Secrets backend + --set secretsBackend.vault.address="https://[vault address]:8200" \ + --set secretsBackend.vault.token=[token] \ + # Server Auth KeyPair + --set casJWTPrivateKey="$(cat private.ec.key)" \ + --set casJWTPublicKey="$(cat public.pem)" +``` + +Deploy using AWS secret manager instead of Vault + +```sh +helm install [RELEASE_NAME] . \ + # Open ID Connect (OIDC) + # ... + # Secrets backend + --set secretsBackend.awsSecretManager.accessKey=[AWS ACCESS KEY ID] \ + --set secretsBackend.awsSecretManager.secretKey=[AWS SECRET KEY] \ + --set secretsBackend.awsSecretManager.region=[AWS region]\ + # Server Auth KeyPair + # ... +``` + +Connect to an external PostgreSQL database instead + +```sh +helm install [RELEASE_NAME] . \ + # Open ID Connect (OIDC) + # ... + # Secrets backend + # ... + # Server Auth KeyPair + # ... + # External DB setup + --set postgresql.enabled=false \ + --set controlplane.externalDatabase.host=[DB_HOST] \ + --set controlplane.externalDatabase.user=[DB_USER] \ + --set controlplane.externalDatabase.password=[DB_PASSWORD] \ + --set controlplane.externalDatabase.database=[DB_NAME] +``` + +### Development + +To provide an easy way to give Chainloop a try, this Helm Chart has an **opt-in development** mode that can be enabled with the flag `development=true` + +> IMPORTANT: DO NOT USE THIS MODE IN PRODUCTION + +![Deployment](../../docs/img/deployment-dev.png) + +The Helm Chart in this mode includes + +- Chainloop [Controlplane](../../app/controlplane) +- Chainloop [Artifact proxy](../../app/artifact-cas) +- A PostgreSQL dependency enabled by default +- **A pre-configured Hashicorp Vault instance running in development mode (unsealed, in-memory, insecure)** + +During installation, you'll need to provide + +- Open ID Connect Identity Provider (IDp) settings i.e [Auth0 settings](https://auth0.com/docs/get-started/applications/application-settings#basic-information) +- ~~Connection settings for a secrets storage backend, either [Hashicorp Vault](https://www.vaultproject.io/) or [AWS Secret Manager](https://aws.amazon.com/secrets-manager)~~ +- ~~ECDSA (ES512) key-pair used for Controlplane <-> CAS Authentication~~ + +#### Installation Examples + +Deploy by leveraging built-in Vault and PostgreSQL instances + +```sh +helm install [RELEASE_NAME] . \ + --set development=true \ + --set controlplane.auth.oidc.url=[OIDC URL] \ + --set controlplane.auth.oidc.clientID=[clientID] \ + --set controlplane.auth.oidc.clientSecret=[clientSecret] +``` + +## Parameters + +### Common parameters + +| Name | Description | Value | +| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | +| `kubeVersion` | Override Kubernetes version | `""` | +| `development` | Deploys Chainloop pre-configured FOR DEVELOPMENT ONLY. It includes a Vault instance in development mode and pre-configured authentication certificates and passphrases | `false` | +| `GKEMonitoring.enabled` | Enable GKE podMonitoring (prometheus.io scrape) to scrape the controlplane and CAS /metrics endpoints | `false` | +| `sentry.enabled` | Enable sentry.io alerting | `false` | +| `sentry.dsn` | DSN endpoint https://docs.sentry.io/product/sentry-basics/dsn-explainer/ | `""` | +| `sentry.environment` | Environment tag | `production` | + +### Secrets Backend + +| Name | Description | Value | +| ------------------------------------------- | -------------------------------------------------------------------- | ----------- | +| `secretsBackend.backend` | Secrets backend type ("vault" or "awsSecretManager") | `vault` | +| `secretsBackend.secretPrefix` | Prefix that will be pre-pended to all secrets in the storage backend | `chainloop` | +| `secretsBackend.vault.address` | Vault address | | +| `secretsBackend.vault.token` | Vault authentication token | | +| `secretsBackend.awsSecretManager.accessKey` | AWS Access KEY ID | | +| `secretsBackend.awsSecretManager.secretKey` | AWS Secret Key | | +| `secretsBackend.awsSecretManager.region` | AWS Secret Manager Region | | + +### Authentication + +| Name | Description | Value | +| ------------------ | ---------------------------------------------------------------------- | ----- | +| `casJWTPrivateKey` | ECDSA (ES512) private key used for Controlplane <-> CAS Authentication | `""` | +| `casJWTPublicKey` | ECDSA (ES512) public key | `""` | + +### Control Plane + +| Name | Description | Value | +| ------------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------- | +| `controlplane.replicaCount` | Number of replicas | `2` | +| `controlplane.image.repository` | FQDN uri for the image | `ghcr.io/chainloop-dev/chainloop/control-plane` | +| `controlplane.image.tag` | Image tag (immutable tags are recommended). If no set chart.appVersion will be used | | + +### Control Plane Database + +| Name | Description | Value | +| ---------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------- | +| `controlplane.externalDatabase` | External PostgreSQL configuration. These values are only used when postgresql.enabled is set to false | | +| `controlplane.externalDatabase.host` | Database host | `""` | +| `controlplane.externalDatabase.port` | Database port number | `5432` | +| `controlplane.externalDatabase.user` | Non-root username | `""` | +| `controlplane.externalDatabase.database` | Database name | `""` | +| `controlplane.externalDatabase.password` | Password for the non-root username | `""` | +| `controlplane.sqlProxy.enabled` | Enable sidecar to connect to DB via Google Cloud SQL proxy | `false` | +| `controlplane.sqlProxy.connectionName` | Google Cloud SQL connection name | `""` | +| `controlplane.sqlProxy.resources` | Sidecar container resources | `{}` | + +### Control Plane Authentication + +| Name | Description | Value | +| --------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------- | +| `controlplane.auth.passphrase` | Passphrase used to sign the Auth Tokens generated by the controlplane. Leave empty for auto-generation | `""` | +| `controlplane.auth.oidc.url` | Full authentication path, it should match the issuer URL of the Identity provider (IDp) | `""` | +| `controlplane.auth.oidc.clientID` | OIDC IDp clientID | `""` | +| `controlplane.auth.oidc.clientSecret` | OIDC IDp clientSecret | `""` | +| `controlplane.auth.redirectURLScheme` | Schema that will be used during authentication | `https` | + +### Control Plane Networking + +| Name | Description | Value | +| ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| `controlplane.service.type` | Service type | `ClusterIP` | +| `controlplane.service.port` | Service port | `80` | +| `controlplane.service.targetPort` | Service target Port | `http` | +| `controlplane.service.nodePorts.http` | Node port for HTTP. NOTE: choose port between <30000-32767> | | +| `controlplane.serviceAPI.type` | Service type | `ClusterIP` | +| `controlplane.serviceAPI.port` | Service port | `80` | +| `controlplane.serviceAPI.targetPort` | Service target Port | `grpc` | +| `controlplane.serviceAPI.annotations` | Service annotations | | +| `controlplane.serviceAPI.nodePorts.http` | Node port for HTTP. NOTE: choose port between <30000-32767> | | +| `controlplane.ingress.enabled` | Enable ingress record generation for %%MAIN_CONTAINER_NAME%% | `false` | +| `controlplane.ingress.pathType` | Ingress path type | `ImplementationSpecific` | +| `controlplane.ingress.hostname` | Default host for the ingress record | `cp.dev.local` | +| `controlplane.ingress.ingressClassName` | IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) | `""` | +| `controlplane.ingress.path` | Default path for the ingress record | `/` | +| `controlplane.ingress.annotations` | Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. | `{}` | +| `controlplane.ingress.tls` | Enable TLS configuration for the host defined at `controlplane.ingress.hostname` parameter | `false` | +| `controlplane.ingress.selfSigned` | Create a TLS secret for this ingress record using self-signed certificates generated by Helm | `false` | +| `controlplane.ingress.extraHosts` | An array with additional hostname(s) to be covered with the ingress record | `[]` | +| `controlplane.ingress.extraPaths` | An array with additional arbitrary paths that may need to be added to the ingress under the main host | `[]` | +| `controlplane.ingress.extraTls` | TLS configuration for additional hostname(s) to be covered with this ingress record | `[]` | +| `controlplane.ingress.secrets` | Custom TLS certificates as secrets | `[]` | +| `controlplane.ingress.extraRules` | Additional rules to be covered with this ingress record | `[]` | +| `controlplane.ingressAPI.enabled` | Enable ingress record generation for %%MAIN_CONTAINER_NAME%% | `false` | +| `controlplane.ingressAPI.pathType` | Ingress path type | `ImplementationSpecific` | +| `controlplane.ingressAPI.hostname` | Default host for the ingress record | `api.cp.dev.local` | +| `controlplane.ingressAPI.ingressClassName` | IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) | `""` | +| `controlplane.ingressAPI.path` | Default path for the ingress record | `/` | +| `controlplane.ingressAPI.annotations` | Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. | `{}` | +| `controlplane.ingressAPI.tls` | Enable TLS configuration for the host defined at `controlplane.ingress.hostname` parameter | `false` | +| `controlplane.ingressAPI.selfSigned` | Create a TLS secret for this ingress record using self-signed certificates generated by Helm | `false` | +| `controlplane.ingressAPI.extraHosts` | An array with additional hostname(s) to be covered with the ingress record | `[]` | +| `controlplane.ingressAPI.extraPaths` | An array with additional arbitrary paths that may need to be added to the ingress under the main host | `[]` | +| `controlplane.ingressAPI.extraTls` | TLS configuration for additional hostname(s) to be covered with this ingress record | `[]` | +| `controlplane.ingressAPI.secrets` | Custom TLS certificates as secrets | `[]` | +| `controlplane.ingressAPI.extraRules` | Additional rules to be covered with this ingress record | `[]` | + +### Controlplane Misc + +| Name | Description | Value | +| ------------------------------------------------------------ | ----------------------------- | ------- | +| `controlplane.resources.limits` | Container resource limits | `{}` | +| `controlplane.resources.requests` | Container resource requests | `{}` | +| `controlplane.autoscaling.enabled` | Enable deployment autoscaling | `false` | +| `controlplane.autoscaling.minReplicas` | Minimum number of replicas | `1` | +| `controlplane.autoscaling.maxReplicas` | Maximum number of replicas | `100` | +| `controlplane.autoscaling.targetCPUUtilizationPercentage` | Target CPU percentage | `80` | +| `controlplane.autoscaling.targetMemoryUtilizationPercentage` | Target CPU memory | `80` | + +### Artifact Content Addressable (CAS) API + +| Name | Description | Value | +| ---------------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------- | +| `cas.replicaCount` | Number of replicas | `2` | +| `cas.image.repository` | FQDN uri for the image | `ghcr.io/chainloop-dev/chainloop/artifact-cas` | +| `cas.image.tag` | Image tag (immutable tags are recommended). If no set chart.appVersion will be used | | + +### CAS Networking + +| Name | Description | Value | +| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| `cas.serviceAPI.type` | Service type | `ClusterIP` | +| `cas.serviceAPI.port` | Service port | `80` | +| `cas.serviceAPI.targetPort` | Service target Port | `grpc` | +| `cas.serviceAPI.annotations` | Service annotations | | +| `cas.serviceAPI.nodePorts.http` | Node port for HTTP. NOTE: choose port between <30000-32767> | | +| `cas.ingressAPI.enabled` | Enable ingress record generation for %%MAIN_CONTAINER_NAME%% | `false` | +| `cas.ingressAPI.pathType` | Ingress path type | `ImplementationSpecific` | +| `cas.ingressAPI.hostname` | Default host for the ingress record | `api.cp.dev.local` | +| `cas.ingressAPI.ingressClassName` | IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) | `""` | +| `cas.ingressAPI.path` | Default path for the ingress record | `/` | +| `cas.ingressAPI.annotations` | Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. | `{}` | +| `cas.ingressAPI.tls` | Enable TLS configuration for the host defined at `controlplane.ingress.hostname` parameter | `false` | +| `cas.ingressAPI.selfSigned` | Create a TLS secret for this ingress record using self-signed certificates generated by Helm | `false` | +| `cas.ingressAPI.extraHosts` | An array with additional hostname(s) to be covered with the ingress record | `[]` | +| `cas.ingressAPI.extraPaths` | An array with additional arbitrary paths that may need to be added to the ingress under the main host | `[]` | +| `cas.ingressAPI.extraTls` | TLS configuration for additional hostname(s) to be covered with this ingress record | `[]` | +| `cas.ingressAPI.secrets` | Custom TLS certificates as secrets | `[]` | +| `cas.ingressAPI.extraRules` | Additional rules to be covered with this ingress record | `[]` | + +### CAS Misc + +| Name | Description | Value | +| --------------------------------------------------- | ----------------------------- | ------- | +| `cas.resources.limits` | Container resource limits | `{}` | +| `cas.resources.requests` | Container resource requests | `{}` | +| `cas.autoscaling.enabled` | Enable deployment autoscaling | `false` | +| `cas.autoscaling.minReplicas` | Minimum number of replicas | `1` | +| `cas.autoscaling.maxReplicas` | Maximum number of replicas | `100` | +| `cas.autoscaling.targetCPUUtilizationPercentage` | Target CPU percentage | `80` | +| `cas.autoscaling.targetMemoryUtilizationPercentage` | Target CPU memory | `80` | + +### Dependencies + +| Name | Description | Value | +| ------------------------------------ | ------------------------------------------------------------------------------------------------------ | -------------- | +| `postgresql.enabled` | Switch to enable or disable the PostgreSQL helm chart | `true` | +| `postgresql.auth.enablePostgresUser` | Assign a password to the "postgres" admin user. Otherwise, remote access will be blocked for this user | `false` | +| `postgresql.auth.username` | Name for a custom user to create | `chainloop` | +| `postgresql.auth.password` | Password for the custom user to create | `chainlooppwd` | +| `postgresql.auth.database` | Name for a custom database to create | `chainloop-cp` | +| `postgresql.auth.existingSecret` | Name of existing secret to use for PostgreSQL credentials | `""` | +| `vault.server.dev.enabled` | Enable development mode (unsealed, in-memory, insecure) | `true` | +| `vault.server.dev.devRootToken` | Connection token | `notapassword` | + + +## License + +Copyright © 2023 The Chainloop Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/deployment/chainloop/templates/NOTES.txt b/deployment/chainloop/templates/NOTES.txt new file mode 100644 index 000000000..2285f5513 --- /dev/null +++ b/deployment/chainloop/templates/NOTES.txt @@ -0,0 +1,47 @@ + +** Please be patient while the chart is being deployed ** + +{{- if .Values.development }} + +########################################################################### + DEVELOPMENT MODE +########################################################################### + +██████╗ ███████╗██╗ ██╗ █████╗ ██████╗ ███████╗ +██╔══██╗██╔════╝██║ ██║██╔══██╗██╔══██╗██╔════╝ +██████╔╝█████╗ ██║ █╗ ██║███████║██████╔╝█████╗ +██╔══██╗██╔══╝ ██║███╗██║██╔══██║██╔══██╗██╔══╝ +██████╔╝███████╗╚███╔███╔╝██║ ██║██║ ██║███████╗ +╚═════╝ ╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ + +Instance running in development mode! + +Development mode, by default + +- Runs an insecure, unsealed, non-persistent instance of Vault +- Is configured with development authentication keys + +DO NOT USE IT FOR PRODUCTION PURPOSES + +########################################################################### + CONFIGURE CLI +########################################################################### + +Configure the CLI to point to this instance, for example + + chainloop --insecure config save \ + --control-plane my-controlplane.acme.com:80 \ + --artifact-cas cas.acme.com:80 + +Refer to this link for more information +https://docs.chainloop.dev/getting-started/installation#configure-cli-optional + +########################################################################### + USEFUL LINKS +########################################################################### + +- GitHub repository: https://github.com/chainloop-dev/chainloop +- Documentation: https://docs.chainloop.dev + +{{- end }} + diff --git a/deployment/chainloop/templates/_helpers.tpl b/deployment/chainloop/templates/_helpers.tpl new file mode 100644 index 000000000..e7b717ede --- /dev/null +++ b/deployment/chainloop/templates/_helpers.tpl @@ -0,0 +1,291 @@ + +{{- define "chainloop.postgresql.fullname" -}} +{{- include "common.names.dependency.fullname" (dict "chartName" "postgresql" "chartValues" .Values.postgresql "context" $) -}} +{{- end -}} + +{{- define "chainloop.vault.fullname" -}} +{{- include "common.names.dependency.fullname" (dict "chartName" "vault" "chartValues" .Values.vault "context" $) -}} +{{- end -}} + +{{/* +Returns a private key used for CAS <-> Controlplane communication +If we are running ind development mode we add a default one otherwise we require providing it +*/}} +{{- define "chainloop.casjwt.private_key" -}} + {{- if .Values.development }} + {{- coalesce .Values.casJWTPrivateKey (include "chainloop.casjwt.private_key.devel" .) }} + {{- else }} + {{- required "Authentication Private Key \"casJWTPrivateKey\" required" .Values.casJWTPrivateKey }} + {{- end }} +{{- end }} + +{{/* +Returns a public key used for CAS <-> Controlplane communication +If we are running ind development mode we add a default one otherwise we require providing it +*/}} +{{- define "chainloop.casjwt.public_key" -}} + {{- if .Values.development }} + {{- coalesce .Values.casJWTPublicKey (include "chainloop.casjwt.public_key.devel" .) }} + {{- else }} + {{- required "Authentication Public Key \"casJWTPublicKey\" required" .Values.casJWTPublicKey }} + {{- end }} +{{- end }} + +{{/* +DEVELOPMENT ONLY PRIVATE KEY +NOTE: It can not be generated by HELM because we also need a public key +*/}} +{{- define "chainloop.casjwt.private_key.devel" -}} +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIA762MbJK9IBnaqG0sd9uFRM+Z7Y+Aq5UfmbWf0+acKMYpYoy/8kBE +tI6cpcA2KvmW5qurOjIMh5ISr+P2GmzSZX+gBwYFK4EEACOhgYkDgYYABAFzPMcM +NUnPoC7b+s+/OyxRC7V/+elthj6Cq85WCj0KZ2qDvmd4QsYnsTIQ7NM7E+9WztdP +rJBaMdfauMarLlc7/AAHqoa0lv7HNIa0PpupZD4VXmnIe/ZkhHvKOuw0Bdoq2D2B +3U25sylQQto3nZ4IqnsXmrtYGIFI9om3PoliT9/J7g== +-----END EC PRIVATE KEY----- +{{- end -}} + +{{/* +DEVELOPMENT ONLY PUBLIC KEY +*/}} +{{- define "chainloop.casjwt.public_key.devel" -}} +-----BEGIN PUBLIC KEY----- +MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBczzHDDVJz6Au2/rPvzssUQu1f/np +bYY+gqvOVgo9Cmdqg75neELGJ7EyEOzTOxPvVs7XT6yQWjHX2rjGqy5XO/wAB6qG +tJb+xzSGtD6bqWQ+FV5pyHv2ZIR7yjrsNAXaKtg9gd1NubMpUELaN52eCKp7F5q7 +WBiBSPaJtz6JYk/fye4= +-----END PUBLIC KEY----- +{{- end -}} + +{{- define "chainloop.credentials_service_settings" -}} +{{- with .Values.secretsBackend }} +{{- if eq .backend "vault" }} +vault: + secretPrefix: {{ required "secret prefix required" .secretPrefix | quote }} + {{- if and $.Values.development (or (not .vault) not .vault.address) }} + address: {{ printf "http://%s:8200" (include "chainloop.vault.fullname" $) | quote }} + token: {{ $.Values.vault.server.dev.devRootToken | quote }} + {{- else if (required "vault backend selected but configuration not provided" .vault ) }} + address: {{ required "vault address required" .vault.address | quote }} + token: {{ required "vault token required" .vault.token | quote }} + {{- end }} + +{{- else if eq .backend "awsSecretManager" }} +awsSecretManager: + secretPrefix: {{ required "secret prefix required" .secretPrefix | quote }} + region: {{ required "region required" .awsSecretManager.region | quote }} + creds: + accessKey: {{ required "access key required" .awsSecretManager.accessKey | quote }} + secretKey: {{ required "secret key required" .awsSecretManager.secretKey | quote }} +{{- end }} +{{- end }} +{{- end -}} + +{{- define "chainloop.node_port" -}} +{{- if (and (or (eq .type "NodePort") (eq .type "LoadBalancer")) .nodePorts (not (empty .nodePorts.http))) }} +{{- .nodePorts.http }} +{{- else -}} +null +{{- end -}} +{{- end -}} + +{{/* +Figure out the external URL the controlplane can be reached at +This endpoint is used for the CLI to know where to go for log in +NOTE: Load balancer service type is not supported +*/}} +{{- define "chainloop.controlplane.external_url" -}} +{{- $service := .Values.controlplane.service }} +{{- $ingress := .Values.controlplane.ingress }} + +{{- if (and $ingress $ingress.enabled $ingress.hostname) }} +{{- $ingress.hostname }} +{{- else if (and (eq $service.type "NodePort") $service.nodePorts (not (empty $service.nodePorts.http))) }} +{{- printf "localhost:%s" $service.nodePorts.http }} +{{- else -}} +null +{{- end -}} +{{- end -}} + +{{/* +############################################################################## +Controlplane helpers +############################################################################## +*/}} + +{{/* +Chainloop Controlplane release name +*/}} +{{- define "chainloop.controlplane.fullname" -}} +{{- printf "%s-%s" (include "common.names.fullname" .) "controlplane" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Chainloop Controlplane Chart fullname +*/}} +{{- define "chainloop.controlplane.name" -}} +{{- printf "%s-%s" (include "common.names.name" .) "controlplane" | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{/* + +Common labels +*/}} +{{- define "chainloop.controlplane.labels" -}} +{{- include "common.labels.standard" . }} +app.kubernetes.io/part-of: chainloop +app.kubernetes.io/component: controlplane +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "chainloop.controlplane.selectorLabels" -}} +{{- include "common.labels.matchLabels" .}} +app.kubernetes.io/component: controlplane +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "controlplane.serviceAccountName" -}} +{{- if .Values.controlplane.serviceAccount.create }} +{{- default (include "chainloop.controlplane.fullname" .) .Values.controlplane.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.controlplane.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Return the Postgresql connection string +*/}} +{{- define "controlplane.database.connection_string" -}} +{{- printf "postgresql://%s:%s@%s:%s/%s" (include "controlplane.database.user" .) (include "controlplane.database.escapedPassword" .) (include "controlplane.database.host" .) (include "controlplane.database.port" .) (include "controlplane.database.name" .) }} +{{- end -}} + +{{/* +Return the Postgresql hostname +*/}} +{{- define "controlplane.database.host" -}} +{{- ternary (include "chainloop.postgresql.fullname" .) .Values.controlplane.externalDatabase.host .Values.postgresql.enabled -}} +{{- end -}} + +{{/* +Return the Postgresql port +*/}} +{{- define "controlplane.database.port" -}} +{{- ternary 5432 .Values.controlplane.externalDatabase.port .Values.postgresql.enabled -}} +{{- end -}} + +{{/* +Return the Postgresql password +*/}} +{{- define "controlplane.database.password" -}} +{{- if .Values.postgresql.enabled }} + {{- if .Values.global.postgresql }} + {{- if .Values.global.postgresql.auth }} + {{- coalesce .Values.global.postgresql.auth.password .Values.postgresql.auth.password -}} + {{- else -}} + {{- .Values.postgresql.auth.password -}} + {{- end -}} + {{- else -}} + {{- .Values.postgresql.auth.password -}} + {{- end -}} +{{- else -}} + {{- .Values.controlplane.externalDatabase.password -}} +{{- end -}} +{{- end -}} + + +{{/* +Return the URL-scaped Postgresql password +*/}} +{{ define "controlplane.database.escapedPassword" -}} + {{- include "controlplane.database.password" . | urlquery | replace "+" "%20" -}} +{{- end -}} + +{{/* +Return the Postgresql database name +*/}} +{{- define "controlplane.database.name" -}} +{{- if .Values.postgresql.enabled }} + {{- if .Values.global.postgresql }} + {{- if .Values.global.postgresql.auth }} + {{- coalesce .Values.global.postgresql.auth.database .Values.postgresql.auth.database -}} + {{- else -}} + {{- .Values.postgresql.auth.database -}} + {{- end -}} + {{- else -}} + {{- .Values.postgresql.auth.database -}} + {{- end -}} +{{- else -}} + {{- .Values.controlplane.externalDatabase.database -}} +{{- end -}} +{{- end -}} + +{{/* +Return the Postgresql user +*/}} +{{- define "controlplane.database.user" -}} +{{- if .Values.postgresql.enabled }} + {{- if .Values.global.postgresql }} + {{- if .Values.global.postgresql.auth }} + {{- coalesce .Values.global.postgresql.auth.username .Values.postgresql.auth.username -}} + {{- else -}} + {{- .Values.postgresql.auth.username -}} + {{- end -}} + {{- else -}} + {{- .Values.postgresql.auth.username -}} + {{- end -}} +{{- else -}} + {{- .Values.controlplane.externalDatabase.user -}} +{{- end -}} +{{- end -}} + +{{/* +############################################################################## +CAS Helpers +############################################################################## +*/}} + +{{/* +Chainloop CAS release name +*/}} + +{{- define "chainloop.cas.fullname" -}} +{{- printf "%s-%s" (include "common.names.fullname" .) "cas" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Chainloop CAS Chart fullname +*/}} +{{- define "chainloop.cas.name" -}} +{{- printf "%s-%s" (include "common.names.name" .) "cas" | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{/* + +Common labels +*/}} +{{- define "chainloop.cas.labels" -}} +{{- include "common.labels.standard" . }} +app.kubernetes.io/part-of: chainloop +app.kubernetes.io/component: cas +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "chainloop.cas.selectorLabels" -}} +{{- include "common.labels.matchLabels" .}} +app.kubernetes.io/component: cas +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "chainloop.cas.serviceAccountName" -}} +{{- if .Values.cas.serviceAccount.create }} +{{- default (include "chainloop.cas.fullname" .) .Values.cas.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.cas.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/deployment/chainloop/templates/cas/config.configmap.yaml b/deployment/chainloop/templates/cas/config.configmap.yaml new file mode 100644 index 000000000..6170961a7 --- /dev/null +++ b/deployment/chainloop/templates/cas/config.configmap.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "chainloop.cas.fullname" . }} + labels: + {{- include "chainloop.cas.labels" . | nindent 4 }} +data: + server.yaml: | + server: + http: + addr: 0.0.0.0:8000 + timeout: 1s + grpc: + addr: 0.0.0.0:9000 + timeout: 1s + http_metrics: + addr: 0.0.0.0:5000 \ No newline at end of file diff --git a/deployment/chainloop/templates/cas/config.secret.yaml b/deployment/chainloop/templates/cas/config.secret.yaml new file mode 100644 index 000000000..4fbab6d6f --- /dev/null +++ b/deployment/chainloop/templates/cas/config.secret.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "chainloop.cas.fullname" . }} + labels: + {{- include "chainloop.cas.labels" . | nindent 4 }} +type: Opaque +stringData: + {{- if and .Values.sentry .Values.sentry.enabled }} + config.observability.yaml: | + observability: + sentry: + dsn: {{ required "Sentry DSN required" .Values.sentry.dsn | quote }} + environment: {{ required "Sentry environment required" .Values.sentry.environment | quote }} + {{- end }} + config.secret.yaml: | + credentials_service: {{- include "chainloop.credentials_service_settings" . | indent 6 }} + auth: + robot_account_public_key_path: "/tmp/cas.public.pem" + # TODO: add observability \ No newline at end of file diff --git a/deployment/chainloop/templates/cas/deployment.yaml b/deployment/chainloop/templates/cas/deployment.yaml new file mode 100644 index 000000000..1726aa7fe --- /dev/null +++ b/deployment/chainloop/templates/cas/deployment.yaml @@ -0,0 +1,71 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "chainloop.cas.fullname" . }} + labels: + {{- include "chainloop.cas.labels" . | nindent 4 }} +spec: + {{- if not .Values.cas.autoscaling.enabled }} + replicas: {{ .Values.cas.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "chainloop.cas.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/cas" "/config.configmap.yaml") . | sha256sum }} + checksum/config-secret: {{ include (print $.Template.BasePath "/cas" "/config.secret.yaml") . | sha256sum }} + checksum/public-key-secret: {{ include (print $.Template.BasePath "/cas" "/jwt_public_key.secret.yaml") . | sha256sum }} + labels: + {{- include "chainloop.cas.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.cas.image.pullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "chainloop.cas.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.cas.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.cas.securityContext | nindent 12 }} + image: "{{ .Values.cas.image.repository }}:{{ .Values.cas.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.cas.image.pullPolicy }} + ports: + - name: http + containerPort: 8000 + protocol: TCP + - name: grpc + containerPort: 9000 + protocol: TCP + - name: metrics + containerPort: 5000 + protocol: TCP + livenessProbe: + httpGet: + path: /statusz + port: http + readinessProbe: + httpGet: + path: /statusz?readiness=1 + port: http + resources: + {{- toYaml .Values.cas.resources | nindent 12 }} + volumeMounts: + - name: config + mountPath: "/data/conf" + - name: jwt-public-key + mountPath: "/tmp" + volumes: + - name: config + projected: + sources: + - configMap: + name: {{ include "chainloop.cas.fullname" . }} + - secret: + name: {{ include "chainloop.cas.fullname" . }} + - name: jwt-public-key + secret: + secretName: {{ include "chainloop.cas.fullname" . }}-jwt-public-key diff --git a/deployment/chainloop/templates/cas/gke_monitoring.yaml b/deployment/chainloop/templates/cas/gke_monitoring.yaml new file mode 100644 index 000000000..9260d461b --- /dev/null +++ b/deployment/chainloop/templates/cas/gke_monitoring.yaml @@ -0,0 +1,13 @@ +{{- if .Values.GKEMonitoring.enabled -}} +apiVersion: monitoring.googleapis.com/v1 +kind: PodMonitoring +metadata: + name: {{ include "chainloop.cas.fullname" . }} +spec: + selector: + matchLabels: + {{- include "chainloop.cas.selectorLabels" . | nindent 6 }} + endpoints: + - port: metrics + interval: 30s +{{- end }} \ No newline at end of file diff --git a/deployment/chainloop/templates/cas/hpa.yaml b/deployment/chainloop/templates/cas/hpa.yaml new file mode 100644 index 000000000..c556e9f5b --- /dev/null +++ b/deployment/chainloop/templates/cas/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.cas.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "chainloop.cas.fullname" . }} + labels: + {{- include "chainloop.cas.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "chainloop.cas.fullname" . }} + minReplicas: {{ .Values.cas.autoscaling.minReplicas }} + maxReplicas: {{ .Values.cas.autoscaling.maxReplicas }} + metrics: + {{- if .Values.cas.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.cas.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.cas.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.cas.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/deployment/chainloop/templates/cas/ingress_grpc.yaml b/deployment/chainloop/templates/cas/ingress_grpc.yaml new file mode 100644 index 000000000..35cb6aa44 --- /dev/null +++ b/deployment/chainloop/templates/cas/ingress_grpc.yaml @@ -0,0 +1,60 @@ +{{- if .Values.cas.ingressAPI.enabled }} +{{- $fullName := printf "%s-%s" (include "chainloop.cas.fullname" .) "api" -}} +apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "chainloop.cas.labels" . | nindent 4 }} + {{- if or .Values.cas.ingressAPI.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.cas.ingressAPI.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.cas.ingressAPI.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + {{- if and .Values.cas.ingressAPI.ingressClassName (eq "true" (include "common.ingress.supportsIngressClassname" .)) }} + ingressClassName: {{ .Values.cas.ingressAPI.ingressClassName | quote }} + {{- end }} + rules: + {{- if .Values.cas.ingressAPI.hostname }} + - host: {{ .Values.cas.ingressAPI.hostname }} + http: + paths: + {{- if .Values.cas.ingressAPI.extraPaths }} + {{- toYaml .Values.cas.ingressAPI.extraPaths | nindent 10 }} + {{- end }} + - path: {{ .Values.cas.ingressAPI.path }} + {{- if eq "true" (include "common.ingress.supportsPathType" .) }} + pathType: {{ .Values.cas.ingressAPI.pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" $fullName "servicePort" "grpc" "context" $) | nindent 14 }} + {{- end }} + {{- range .Values.cas.ingressAPI.extraHosts }} + - host: {{ .name | quote }} + http: + paths: + - path: {{ default "/" .path }} + {{- if eq "true" (include "common.ingress.supportsPathType" $) }} + pathType: {{ default "ImplementationSpecific" .pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" $fullName "servicePort" "grpc" "context" $) | nindent 14 }} + {{- end }} + {{- if .Values.cas.ingressAPI.extraRules }} + {{- include "common.tplvalues.render" (dict "value" .Values.cas.ingressAPI.extraRules "context" $) | nindent 4 }} + {{- end }} + {{- if or (and .Values.cas.ingressAPI.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.cas.ingressAPI.annotations )) .Values.cas.ingressAPI.selfSigned)) .Values.cas.ingressAPI.extraTls }} + tls: + {{- if and .Values.cas.ingressAPI.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.cas.ingressAPI.annotations )) .Values.cas.ingressAPI.selfSigned) }} + - hosts: + - {{ .Values.cas.ingressAPI.hostname | quote }} + secretName: {{ printf "%s-tls" .Values.cas.ingressAPI.hostname }} + {{- end }} + {{- if .Values.cas.ingressAPI.extraTls }} + {{- include "common.tplvalues.render" (dict "value" .Values.cas.ingressAPI.extraTls "context" $) | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/deployment/chainloop/templates/cas/jwt_public_key.secret.yaml b/deployment/chainloop/templates/cas/jwt_public_key.secret.yaml new file mode 100644 index 000000000..6880bdab1 --- /dev/null +++ b/deployment/chainloop/templates/cas/jwt_public_key.secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "chainloop.cas.fullname" . }}-jwt-public-key + labels: + {{- include "chainloop.cas.labels" . | nindent 4 }} +type: Opaque +data: + cas.public.pem: {{ include "chainloop.casjwt.public_key" . | b64enc | quote }} \ No newline at end of file diff --git a/deployment/chainloop/templates/cas/service_grpc.yaml b/deployment/chainloop/templates/cas/service_grpc.yaml new file mode 100644 index 000000000..6bcd23f4a --- /dev/null +++ b/deployment/chainloop/templates/cas/service_grpc.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "chainloop.cas.fullname" . }}-api + labels: + {{- include "chainloop.cas.labels" . | nindent 4 }} + {{- with .Values.cas.serviceAPI.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.cas.serviceAPI.type }} + ports: + - port: {{ .Values.cas.serviceAPI.port }} + targetPort: {{ .Values.cas.serviceAPI.targetPort }} + protocol: TCP + name: grpc + nodePort: {{ include "chainloop.node_port" .Values.cas.serviceAPI }} + selector: + {{- include "chainloop.cas.selectorLabels" . | nindent 4 }} diff --git a/deployment/chainloop/templates/cas/serviceaccount.yaml b/deployment/chainloop/templates/cas/serviceaccount.yaml new file mode 100644 index 000000000..f204aa417 --- /dev/null +++ b/deployment/chainloop/templates/cas/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.cas.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "chainloop.cas.serviceAccountName" . }} + labels: + {{- include "chainloop.cas.labels" . | nindent 4 }} + {{- with .Values.cas.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/deployment/chainloop/templates/controlplane/config.configmap.yaml b/deployment/chainloop/templates/controlplane/config.configmap.yaml new file mode 100644 index 000000000..01d9a2c90 --- /dev/null +++ b/deployment/chainloop/templates/controlplane/config.configmap.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "chainloop.controlplane.fullname" . }} + labels: + {{- include "chainloop.controlplane.labels" . | nindent 4 }} +data: + {{- if .Values.controlplane.allowList }} + allow_list.yaml: | + auth: + allow_list: + {{- range .Values.controlplane.allowList }} + - {{ . | quote }} + {{- end }} + {{- end }} + config.yaml: | + server: + http: + addr: 0.0.0.0:8000 + timeout: 1s + external_addr: {{ include "chainloop.controlplane.external_url" . }} + http_metrics: + addr: 0.0.0.0:5000 + grpc: + addr: 0.0.0.0:9000 + timeout: 10s + cas_server: + grpc: + addr: {{ printf "%s-api:%.0f" (include "chainloop.cas.fullname" .) .Values.cas.serviceAPI.port }} + insecure: true \ No newline at end of file diff --git a/deployment/chainloop/templates/controlplane/config.secret.yaml b/deployment/chainloop/templates/controlplane/config.secret.yaml new file mode 100644 index 000000000..02b237f17 --- /dev/null +++ b/deployment/chainloop/templates/controlplane/config.secret.yaml @@ -0,0 +1,43 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "chainloop.controlplane.fullname" . }} + labels: + {{- include "chainloop.controlplane.labels" . | nindent 4 }} +type: Opaque +{{- $hmacpass := include "common.secrets.passwords.manage" (dict "secret" (include "chainloop.controlplane.fullname" .) "key" "generated_jws_hmac_secret" "providedValues" (list "controlplane.auth.passphrase") "context" $) }} +data: + # We store it also as a different key so it can be reused during upgrades by the common.secrets.passwords.manage helper + generated_jws_hmac_secret: {{ $hmacpass }} +stringData: + {{- if and .Values.sentry .Values.sentry.enabled }} + config.observability.yaml: | + observability: + sentry: + dsn: {{ required "Sentry DSN required" .Values.sentry.dsn | quote }} + environment: {{ required "Sentry environment required" .Values.sentry.environment | quote }} + {{- end }} + config.secret.yaml: | + data: + database: + driver: pgx + source: {{include "controlplane.database.connection_string" . }} + + credentials_service: {{- include "chainloop.credentials_service_settings" . | indent 6 }} + + auth: + oidc: + {{- with .Values.controlplane.auth }} + redirectURLScheme: {{ .redirectURLScheme | default "http" }} + domain: "{{ required "oidc URL endpoint required" .oidc.url }}" + clientID: "{{ required "oidc clientID required" .oidc.clientID }}" + clientSecret: "{{ required "oidc clientSecret required" .oidc.clientSecret }}" + {{- end }} + + # HMAC key used to sign the JWTs generated by the controlplane + # NOTE: We are base64 encoding the value but can't remove it because it's quoted too by the helper + # TODO: Make sure we inject the pass here verbatim + generated_jws_hmac_secret: {{ $hmacpass }} + + # Private key used to sign the JWTs meant to be consumed by the CAS + cas_robot_account_private_key_path: "/tmp/cas.private.key" diff --git a/deployment/chainloop/templates/controlplane/deployment.yaml b/deployment/chainloop/templates/controlplane/deployment.yaml new file mode 100644 index 000000000..7a83fe5d6 --- /dev/null +++ b/deployment/chainloop/templates/controlplane/deployment.yaml @@ -0,0 +1,96 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "chainloop.controlplane.fullname" . }} + labels: + {{- include "chainloop.controlplane.labels" . | nindent 4 }} +spec: + {{- if not .Values.controlplane.autoscaling.enabled }} + replicas: {{ .Values.controlplane.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "chainloop.controlplane.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/controlplane" "/config.configmap.yaml") . | sha256sum }} + checksum/secret-config: {{ include (print $.Template.BasePath "/controlplane" "/config.secret.yaml") . | sha256sum }} + checksum/cas-private-key: {{ include (print $.Template.BasePath "/controlplane" "/jwt_cas_private_key.secret.yaml") . | sha256sum }} + kubectl.kubernetes.io/default-container: controlplane + labels: + {{- include "chainloop.controlplane.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.controlplane.image.pullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "controlplane.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.controlplane.podSecurityContext | nindent 8 }} + containers: + {{ if .Values.controlplane.sqlProxy.enabled }} + - name: cloud-sql-proxy + # It is recommended to use the latest version of the Cloud SQL proxy + # Make sure to update on a regular schedule! + image: gcr.io/cloudsql-docker/gce-proxy:1.28.0 # make sure the use the latest version + command: + - "/cloud_sql_proxy" + # If connecting from a VPC-native GKE cluster, you can use the + # following flag to have the proxy connect over private IP + # - "-ip_address_types=PRIVATE" + + # By default, the proxy will write all logs to stderr. In some + # environments, anything printed to stderr is consider an error. To + # disable this behavior and write all logs to stdout (except errors + # which will still go to stderr), use: + - "-log_debug_stdout" + - "-instances={{ .Values.controlplane.sqlProxy.connectionName }}=tcp:5432" + securityContext: + runAsNonRoot: true + resources: + {{- toYaml .Values.controlplane.sqlProxy.resources | nindent 12 }} + {{- end }} + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.controlplane.securityContext | nindent 12 }} + image: "{{ .Values.controlplane.image.repository }}:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.controlplane.image.pullPolicy }} + ports: + - name: http + containerPort: 8000 + protocol: TCP + - name: metrics + containerPort: 5000 + protocol: TCP + - name: grpc + containerPort: 9000 + protocol: TCP + livenessProbe: + httpGet: + path: /statusz + port: http + periodSeconds: 5 + readinessProbe: + httpGet: + path: /statusz?readiness=1 + port: http + periodSeconds: 5 + resources: + {{- toYaml .Values.controlplane.resources | nindent 12 }} + volumeMounts: + - name: config + mountPath: "/data/conf" + - name: jwt-cas-private-key + mountPath: "/tmp" + volumes: + - name: config + projected: + sources: + - secret: + name: {{ include "chainloop.controlplane.fullname" . }} + - configMap: + name: {{ include "chainloop.controlplane.fullname" . }} + - name: jwt-cas-private-key + secret: + secretName: {{ include "chainloop.controlplane.fullname" . }}-jwt-cas diff --git a/deployment/chainloop/templates/controlplane/gke_monitoring.yaml b/deployment/chainloop/templates/controlplane/gke_monitoring.yaml new file mode 100644 index 000000000..e3db70e44 --- /dev/null +++ b/deployment/chainloop/templates/controlplane/gke_monitoring.yaml @@ -0,0 +1,13 @@ +{{- if .Values.GKEMonitoring.enabled -}} +apiVersion: monitoring.googleapis.com/v1 +kind: PodMonitoring +metadata: + name: {{ include "chainloop.controlplane.fullname" . }} +spec: + selector: + matchLabels: + {{- include "chainloop.controlplane.selectorLabels" . | nindent 6 }} + endpoints: + - port: metrics + interval: 30s +{{- end }} \ No newline at end of file diff --git a/deployment/chainloop/templates/controlplane/hpa.yaml b/deployment/chainloop/templates/controlplane/hpa.yaml new file mode 100644 index 000000000..8e7e520fc --- /dev/null +++ b/deployment/chainloop/templates/controlplane/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.controlplane.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "chainloop.controlplane.fullname" . }} + labels: + {{- include "chainloop.controlplane.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "chainloop.controlplane.fullname" . }} + minReplicas: {{ .Values.controlplane.autoscaling.minReplicas }} + maxReplicas: {{ .Values.controlplane.autoscaling.maxReplicas }} + metrics: + {{- if .Values.controlplane.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.controlplane.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.controlplane.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.controlplane.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/deployment/chainloop/templates/controlplane/ingress.yaml b/deployment/chainloop/templates/controlplane/ingress.yaml new file mode 100644 index 000000000..0a8b6e5d0 --- /dev/null +++ b/deployment/chainloop/templates/controlplane/ingress.yaml @@ -0,0 +1,60 @@ +{{- if .Values.controlplane.ingress.enabled }} +{{- $fullName := include "chainloop.controlplane.fullname" . -}} +apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "chainloop.controlplane.labels" . | nindent 4 }} + {{- if or .Values.controlplane.ingress.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.controlplane.ingress.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.controlplane.ingress.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + {{- if and .Values.controlplane.ingress.ingressClassName (eq "true" (include "common.ingress.supportsIngressClassname" .)) }} + ingressClassName: {{ .Values.controlplane.ingress.ingressClassName | quote }} + {{- end }} + rules: + {{- if .Values.controlplane.ingress.hostname }} + - host: {{ .Values.controlplane.ingress.hostname }} + http: + paths: + {{- if .Values.controlplane.ingress.extraPaths }} + {{- toYaml .Values.controlplane.ingress.extraPaths | nindent 10 }} + {{- end }} + - path: {{ .Values.controlplane.ingress.path }} + {{- if eq "true" (include "common.ingress.supportsPathType" .) }} + pathType: {{ .Values.controlplane.ingress.pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" $fullName "servicePort" "http" "context" $) | nindent 14 }} + {{- end }} + {{- range .Values.controlplane.ingress.extraHosts }} + - host: {{ .name | quote }} + http: + paths: + - path: {{ default "/" .path }} + {{- if eq "true" (include "common.ingress.supportsPathType" $) }} + pathType: {{ default "ImplementationSpecific" .pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" $fullName "servicePort" "http" "context" $) | nindent 14 }} + {{- end }} + {{- if .Values.controlplane.ingress.extraRules }} + {{- include "common.tplvalues.render" (dict "value" .Values.controlplane.ingress.extraRules "context" $) | nindent 4 }} + {{- end }} + {{- if or (and .Values.controlplane.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.controlplane.ingress.annotations )) .Values.controlplane.ingress.selfSigned)) .Values.controlplane.ingress.extraTls }} + tls: + {{- if and .Values.controlplane.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.controlplane.ingress.annotations )) .Values.controlplane.ingress.selfSigned) }} + - hosts: + - {{ .Values.controlplane.ingress.hostname | quote }} + secretName: {{ printf "%s-tls" .Values.controlplane.ingress.hostname }} + {{- end }} + {{- if .Values.controlplane.ingress.extraTls }} + {{- include "common.tplvalues.render" (dict "value" .Values.controlplane.ingress.extraTls "context" $) | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/deployment/chainloop/templates/controlplane/ingress_grpc.yaml b/deployment/chainloop/templates/controlplane/ingress_grpc.yaml new file mode 100644 index 000000000..aa4cd020d --- /dev/null +++ b/deployment/chainloop/templates/controlplane/ingress_grpc.yaml @@ -0,0 +1,60 @@ +{{- if .Values.controlplane.ingressAPI.enabled }} +{{- $fullName := printf "%s-%s" (include "chainloop.controlplane.fullname" .) "api" -}} +apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "chainloop.controlplane.labels" . | nindent 4 }} + {{- if or .Values.controlplane.ingressAPI.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.controlplane.ingressAPI.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.controlplane.ingressAPI.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + {{- if and .Values.controlplane.ingressAPI.ingressClassName (eq "true" (include "common.ingress.supportsIngressClassname" .)) }} + ingressClassName: {{ .Values.controlplane.ingressAPI.ingressClassName | quote }} + {{- end }} + rules: + {{- if .Values.controlplane.ingressAPI.hostname }} + - host: {{ .Values.controlplane.ingressAPI.hostname }} + http: + paths: + {{- if .Values.controlplane.ingressAPI.extraPaths }} + {{- toYaml .Values.controlplane.ingressAPI.extraPaths | nindent 10 }} + {{- end }} + - path: {{ .Values.controlplane.ingressAPI.path }} + {{- if eq "true" (include "common.ingress.supportsPathType" .) }} + pathType: {{ .Values.controlplane.ingressAPI.pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" $fullName "servicePort" "grpc" "context" $) | nindent 14 }} + {{- end }} + {{- range .Values.controlplane.ingressAPI.extraHosts }} + - host: {{ .name | quote }} + http: + paths: + - path: {{ default "/" .path }} + {{- if eq "true" (include "common.ingress.supportsPathType" $) }} + pathType: {{ default "ImplementationSpecific" .pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" $fullName "servicePort" "grpc" "context" $) | nindent 14 }} + {{- end }} + {{- if .Values.controlplane.ingressAPI.extraRules }} + {{- include "common.tplvalues.render" (dict "value" .Values.controlplane.ingressAPI.extraRules "context" $) | nindent 4 }} + {{- end }} + {{- if or (and .Values.controlplane.ingressAPI.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.controlplane.ingressAPI.annotations )) .Values.controlplane.ingressAPI.selfSigned)) .Values.controlplane.ingressAPI.extraTls }} + tls: + {{- if and .Values.controlplane.ingressAPI.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.controlplane.ingressAPI.annotations )) .Values.controlplane.ingressAPI.selfSigned) }} + - hosts: + - {{ .Values.controlplane.ingressAPI.hostname | quote }} + secretName: {{ printf "%s-tls" .Values.controlplane.ingressAPI.hostname }} + {{- end }} + {{- if .Values.controlplane.ingressAPI.extraTls }} + {{- include "common.tplvalues.render" (dict "value" .Values.controlplane.ingressAPI.extraTls "context" $) | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/deployment/chainloop/templates/controlplane/jwt_cas_private_key.secret.yaml b/deployment/chainloop/templates/controlplane/jwt_cas_private_key.secret.yaml new file mode 100644 index 000000000..5a8ec3bbd --- /dev/null +++ b/deployment/chainloop/templates/controlplane/jwt_cas_private_key.secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "chainloop.controlplane.fullname" . }}-jwt-cas + labels: + {{- include "chainloop.controlplane.labels" . | nindent 4 }} +type: Opaque +data: + cas.private.key: {{ include "chainloop.casjwt.private_key" . | b64enc | quote }} diff --git a/deployment/chainloop/templates/controlplane/service_grpc.yaml b/deployment/chainloop/templates/controlplane/service_grpc.yaml new file mode 100644 index 000000000..6c468a628 --- /dev/null +++ b/deployment/chainloop/templates/controlplane/service_grpc.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "chainloop.controlplane.fullname" . }}-api + labels: + {{- include "chainloop.controlplane.labels" . | nindent 4 }} + {{- with .Values.controlplane.serviceAPI.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.controlplane.serviceAPI.type }} + ports: + - port: {{ .Values.controlplane.serviceAPI.port }} + targetPort: {{ .Values.controlplane.serviceAPI.targetPort }} + protocol: TCP + name: grpc + nodePort: {{ include "chainloop.node_port" .Values.controlplane.serviceAPI }} + selector: + {{- include "chainloop.controlplane.selectorLabels" . | nindent 4 }} diff --git a/deployment/chainloop/templates/controlplane/service_http.yaml b/deployment/chainloop/templates/controlplane/service_http.yaml new file mode 100644 index 000000000..fbd5ed699 --- /dev/null +++ b/deployment/chainloop/templates/controlplane/service_http.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "chainloop.controlplane.fullname" . }} + labels: {{- include "chainloop.controlplane.labels" . | nindent 4 }} +spec: + type: {{ .Values.controlplane.service.type }} + ports: + - port: {{ .Values.controlplane.service.port }} + targetPort: {{ .Values.controlplane.service.targetPort }} + protocol: TCP + name: http + nodePort: {{ include "chainloop.node_port" .Values.controlplane.service }} + selector: {{- include "chainloop.controlplane.selectorLabels" . | nindent 4 }} diff --git a/deployment/chainloop/templates/controlplane/serviceaccount.yaml b/deployment/chainloop/templates/controlplane/serviceaccount.yaml new file mode 100644 index 000000000..98abb3566 --- /dev/null +++ b/deployment/chainloop/templates/controlplane/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.controlplane.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "controlplane.serviceAccountName" . }} + labels: + {{- include "chainloop.controlplane.labels" . | nindent 4 }} + {{- with .Values.controlplane.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/deployment/chainloop/values.yaml b/deployment/chainloop/values.yaml new file mode 100644 index 000000000..00a994a15 --- /dev/null +++ b/deployment/chainloop/values.yaml @@ -0,0 +1,600 @@ +## Default values for Chainloop Helm Chart + +## @skip global +global: {} + +## @section Common parameters +## +## @param kubeVersion Override Kubernetes version +## +kubeVersion: "" + +## @param development Deploys Chainloop pre-configured FOR DEVELOPMENT ONLY. It includes a Vault instance in development mode and pre-configured authentication certificates and passphrases +## +development: false + +## @param GKEMonitoring.enabled Enable GKE podMonitoring (prometheus.io scrape) to scrape the controlplane and CAS /metrics endpoints +GKEMonitoring: + enabled: false + +## @param sentry.enabled Enable sentry.io alerting +## @param sentry.dsn DSN endpoint https://docs.sentry.io/product/sentry-basics/dsn-explainer/ +## @param sentry.environment Environment tag +sentry: + enabled: false + dsn: "" + environment: production + +## @section Secrets Backend +## + +## Location where to store sensitive data. If development.true? and no overrides provided, the setup will connect to a development instance of Vault +secretsBackend: + ## @param secretsBackend.backend Secrets backend type ("vault" or "awsSecretManager") + ## + backend: "vault" # "awsSecretManager" + ## @param secretsBackend.secretPrefix Prefix that will be pre-pended to all secrets in the storage backend + ## + secretPrefix: "chainloop" + + # Either vault or AWS secret manager enabled at the same time + ## @extra secretsBackend.vault.address Vault address + ## @extra secretsBackend.vault.token Vault authentication token + ## + # vault: + # address: "" + # token: "" + + ## @extra secretsBackend.awsSecretManager.accessKey AWS Access KEY ID + ## @extra secretsBackend.awsSecretManager.secretKey AWS Secret Key + ## @extra secretsBackend.awsSecretManager.region AWS Secret Manager Region + ## + # awsSecretManager: + # accessKey: "" + # secretKey: "" + # region: "" + +## @section Authentication +## + +## ECDSA (ES512) key-pair used for Controlplane <-> CAS Authentication +## The controlplane will use the private key to generate a JWT at user request +## The CAS will use the public key to verify the authenticity of that token +## If development=true is set, a development key will be configured automatically +## otherwise you'll need to provide new keys via .Values.casJWTPrivateKey and .Values.cas.casJWTPublicKey + +## @param casJWTPrivateKey ECDSA (ES512) private key used for Controlplane <-> CAS Authentication +## +## To generate one +## openssl ecparam -name secp521r1 -genkey -noout -out private.ec.key +## casJWTPrivateKey: |- +## -----BEGIN EC PRIVATE KEY----- +## -----END EC PRIVATE KEY----- +## +casJWTPrivateKey: "" + +## @param casJWTPublicKey ECDSA (ES512) public key +## +# openssl ec -in private.ec.key -pubout -out public.pem +# casJWTPublicKey: | +# -----BEGIN PUBLIC KEY----- +# -----END PUBLIC KEY----- +casJWTPublicKey: "" + +## @section Control Plane +################################### +## CONTROL PLANE # +################################### +controlplane: + ## @param controlplane.replicaCount Number of replicas + replicaCount: 2 + + ## @param controlplane.image.repository FQDN uri for the image + ## @extra controlplane.image.tag Image tag (immutable tags are recommended). If no set chart.appVersion will be used + image: + repository: ghcr.io/chainloop-dev/chainloop/control-plane + # Overrides the image tag whose default is the chart appVersion. + # tag: latest + + ## @skip controlplane.serviceAccount + serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + + ## @section Control Plane Database + + ## @extra controlplane.externalDatabase External PostgreSQL configuration. These values are only used when postgresql.enabled is set to false + ## @param controlplane.externalDatabase.host Database host + ## @param controlplane.externalDatabase.port Database port number + ## @param controlplane.externalDatabase.user Non-root username + ## @param controlplane.externalDatabase.database Database name + ## @param controlplane.externalDatabase.password Password for the non-root username + ## + externalDatabase: + host: "" + port: 5432 + user: "" + database: "" + password: "" + + sqlProxy: + ## @param controlplane.sqlProxy.enabled Enable sidecar to connect to DB via Google Cloud SQL proxy + enabled: false + ## @param controlplane.sqlProxy.connectionName Google Cloud SQL connection name + connectionName: "" + ## @param controlplane.sqlProxy.resources Sidecar container resources + resources: {} + + ## @section Control Plane Authentication + auth: + ## @param controlplane.auth.passphrase Passphrase used to sign the Auth Tokens generated by the controlplane. Leave empty for auto-generation + ## + passphrase: "" + + ## @param controlplane.auth.oidc.url Full authentication path, it should match the issuer URL of the Identity provider (IDp) + ## @param controlplane.auth.oidc.clientID OIDC IDp clientID + ## @param controlplane.auth.oidc.clientSecret OIDC IDp clientSecret + ## @param controlplane.auth.redirectURLScheme Schema that will be used during authentication + oidc: + url: "" + clientID: "" + clientSecret: "" + # TODO: look into automatically inference https://github.com/chainloop-dev/chainloop/issues/61 + redirectURLScheme: "https" + + ## @section Control Plane Networking + service: + ## @param controlplane.service.type Service type + type: ClusterIP + ## @param controlplane.service.port Service port + port: 80 + ## @param controlplane.service.targetPort Service target Port + targetPort: http + ## @extra controlplane.service.nodePorts.http Node port for HTTP. NOTE: choose port between <30000-32767> + # nodePorts: + # http: "30800" + + serviceAPI: + ## @param controlplane.serviceAPI.type Service type + type: ClusterIP + ## @param controlplane.serviceAPI.port Service port + port: 80 + ## @param controlplane.serviceAPI.targetPort Service target Port + targetPort: grpc + ## @extra controlplane.serviceAPI.annotations Service annotations + annotations: + ## @skip controlplane.serviceAPI.annotations.traefik.ingress.kubernetes.io/service.serversscheme + traefik.ingress.kubernetes.io/service.serversscheme: h2c + + ## @extra controlplane.serviceAPI.nodePorts.http Node port for HTTP. NOTE: choose port between <30000-32767> + # nodePorts: + # http: "30900" + + ## ref: http://kubernetes.io/docs/user-guide/ingress/ + ingress: + ## @param controlplane.ingress.enabled Enable ingress record generation for %%MAIN_CONTAINER_NAME%% + ## + enabled: false + ## @param controlplane.ingress.pathType Ingress path type + ## + pathType: ImplementationSpecific + ## @param controlplane.ingress.hostname Default host for the ingress record + ## + hostname: cp.dev.local + ## @param controlplane.ingress.ingressClassName IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) + ## This is supported in Kubernetes 1.18+ and required if you have more than one IngressClass marked as the default for your cluster . + ## ref: https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/ + ## + ingressClassName: "" + ## @param controlplane.ingress.path Default path for the ingress record + ## NOTE: You may need to set this to '/*' in order to use this with ALB ingress controllers + ## + path: / + ## @param controlplane.ingress.annotations Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. + ## Use this parameter to set the required annotations for cert-manager, see + ## ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations + ## e.g: + ## annotations: + ## kubernetes.io/controlplane.ingress.class: nginx + ## cert-manager.io/cluster-issuer: cluster-issuer-name + ## + annotations: {} + ## @param controlplane.ingress.tls Enable TLS configuration for the host defined at `controlplane.ingress.hostname` parameter + ## TLS certificates will be retrieved from a TLS secret with name: `{{- printf "%s-tls" .Values.controlplane.ingress.hostname }}` + ## You can: + ## - Use the `controlplane.ingress.secrets` parameter to create this TLS secret + ## - Rely on cert-manager to create it by setting the corresponding annotations + ## - Rely on Helm to create self-signed certificates by setting `controlplane.ingress.selfSigned=true` + ## + tls: false + ## @param controlplane.ingress.selfSigned Create a TLS secret for this ingress record using self-signed certificates generated by Helm + ## + selfSigned: false + ## @param controlplane.ingress.extraHosts An array with additional hostname(s) to be covered with the ingress record + ## e.g: + ## extraHosts: + ## - name: cp.dev.local + ## path: / + ## + extraHosts: [] + ## @param controlplane.ingress.extraPaths An array with additional arbitrary paths that may need to be added to the ingress under the main host + ## e.g: + ## extraPaths: + ## - path: /* + ## backend: + ## serviceName: ssl-redirect + ## servicePort: use-annotation + ## + extraPaths: [] + ## @param controlplane.ingress.extraTls TLS configuration for additional hostname(s) to be covered with this ingress record + ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls + ## e.g: + ## extraTls: + ## - hosts: + ## - cp.dev.local + ## secretName: cp.dev.local-tls + ## + extraTls: [] + ## @param controlplane.ingress.secrets Custom TLS certificates as secrets + ## NOTE: 'key' and 'certificate' are expected in PEM format + ## NOTE: 'name' should line up with a 'secretName' set further up + ## If it is not set and you're using cert-manager, this is unneeded, as it will create a secret for you with valid certificates + ## If it is not set and you're NOT using cert-manager either, self-signed certificates will be created valid for 365 days + ## It is also possible to create and manage the certificates outside of this helm chart + ## Please see README.md for more information + ## e.g: + ## secrets: + ## - name: cp.dev.local-tls + ## key: |- + ## -----BEGIN RSA PRIVATE KEY----- + ## ... + ## -----END RSA PRIVATE KEY----- + ## certificate: |- + ## -----BEGIN CERTIFICATE----- + ## ... + ## -----END CERTIFICATE----- + ## + secrets: [] + ## @param controlplane.ingress.extraRules Additional rules to be covered with this ingress record + ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-rules + ## e.g: + ## extraRules: + ## - host: example.local + ## http: + ## path: / + ## backend: + ## service: + ## name: example-svc + ## port: + ## name: http + ## + extraRules: [] + + ## ref: http://kubernetes.io/docs/user-guide/ingress/ + ingressAPI: + ## @param controlplane.ingressAPI.enabled Enable ingress record generation for %%MAIN_CONTAINER_NAME%% + ## + enabled: false + ## @param controlplane.ingressAPI.pathType Ingress path type + ## + pathType: ImplementationSpecific + ## @param controlplane.ingressAPI.hostname Default host for the ingress record + ## + hostname: api.cp.dev.local + ## @param controlplane.ingressAPI.ingressClassName IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) + ## This is supported in Kubernetes 1.18+ and required if you have more than one IngressClass marked as the default for your cluster . + ## ref: https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/ + ## + ingressClassName: "" + ## @param controlplane.ingressAPI.path Default path for the ingress record + ## NOTE: You may need to set this to '/*' in order to use this with ALB ingress controllers + ## + path: / + ## @param controlplane.ingressAPI.annotations Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. + ## Use this parameter to set the required annotations for cert-manager, see + ## ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations + ## e.g: + ## annotations: + ## kubernetes.io/controlplane.ingress.class: nginx + ## cert-manager.io/cluster-issuer: cluster-issuer-name + ## + annotations: {} + + ## @param controlplane.ingressAPI.tls Enable TLS configuration for the host defined at `controlplane.ingress.hostname` parameter + ## TLS certificates will be retrieved from a TLS secret with name: `{{- printf "%s-tls" .Values.controlplane.ingress.hostname }}` + ## You can: + ## - Use the `controlplane.ingress.secrets` parameter to create this TLS secret + ## - Rely on cert-manager to create it by setting the corresponding annotations + ## - Rely on Helm to create self-signed certificates by setting `controlplane.ingress.selfSigned=true` + ## + tls: false + ## @param controlplane.ingressAPI.selfSigned Create a TLS secret for this ingress record using self-signed certificates generated by Helm + ## + selfSigned: false + ## @param controlplane.ingressAPI.extraHosts An array with additional hostname(s) to be covered with the ingress record + ## e.g: + ## extraHosts: + ## - name: cp.dev.local + ## path: / + ## + extraHosts: [] + ## @param controlplane.ingressAPI.extraPaths An array with additional arbitrary paths that may need to be added to the ingress under the main host + ## e.g: + ## extraPaths: + ## - path: /* + ## backend: + ## serviceName: ssl-redirect + ## servicePort: use-annotation + ## + extraPaths: [] + ## @param controlplane.ingressAPI.extraTls TLS configuration for additional hostname(s) to be covered with this ingress record + ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls + ## e.g: + ## extraTls: + ## - hosts: + ## - cp.dev.local + ## secretName: cp.dev.local-tls + ## + extraTls: [] + ## @param controlplane.ingressAPI.secrets Custom TLS certificates as secrets + ## NOTE: 'key' and 'certificate' are expected in PEM format + ## NOTE: 'name' should line up with a 'secretName' set further up + ## If it is not set and you're using cert-manager, this is unneeded, as it will create a secret for you with valid certificates + ## If it is not set and you're NOT using cert-manager either, self-signed certificates will be created valid for 365 days + ## It is also possible to create and manage the certificates outside of this helm chart + ## Please see README.md for more information + ## e.g: + ## secrets: + ## - name: cp.dev.local-tls + ## key: |- + ## -----BEGIN RSA PRIVATE KEY----- + ## ... + ## -----END RSA PRIVATE KEY----- + ## certificate: |- + ## -----BEGIN CERTIFICATE----- + ## ... + ## -----END CERTIFICATE----- + ## + secrets: [] + ## @param controlplane.ingressAPI.extraRules Additional rules to be covered with this ingress record + ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-rules + ## e.g: + ## extraRules: + ## - host: example.local + ## http: + ## path: / + ## backend: + ## service: + ## name: example-svc + ## port: + ## name: http + ## + extraRules: [] + + ## @section Controlplane Misc + + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## @param controlplane.resources.limits Container resource limits + ## @param controlplane.resources.requests Container resource requests + resources: + limits: {} + requests: {} + + ## Deployment autoscaling + ## @param controlplane.autoscaling.enabled Enable deployment autoscaling + ## @param controlplane.autoscaling.minReplicas Minimum number of replicas + ## @param controlplane.autoscaling.maxReplicas Maximum number of replicas + ## @param controlplane.autoscaling.targetCPUUtilizationPercentage Target CPU percentage + ## @param controlplane.autoscaling.targetMemoryUtilizationPercentage Target CPU memory + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + +## @section Artifact Content Addressable (CAS) API +################################## +# Artifacts CAS # +################################## +cas: + ## @param cas.replicaCount Number of replicas + replicaCount: 2 + + ## @param cas.image.repository FQDN uri for the image + ## @extra cas.image.tag Image tag (immutable tags are recommended). If no set chart.appVersion will be used + image: + repository: ghcr.io/chainloop-dev/chainloop/artifact-cas + # Overrides the image tag whose default is the chart appVersion. + # tag: latest + + ## @skip cas.serviceAccount + serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + + ## @section CAS Networking + serviceAPI: + ## @param cas.serviceAPI.type Service type + type: ClusterIP + ## @param cas.serviceAPI.port Service port + port: 80 + ## @param cas.serviceAPI.targetPort Service target Port + targetPort: grpc + ## @extra cas.serviceAPI.annotations Service annotations + annotations: + ## @skip cas.serviceAPI.annotations.traefik.ingress.kubernetes.io/service.serversscheme + traefik.ingress.kubernetes.io/service.serversscheme: h2c + + ## @extra cas.serviceAPI.nodePorts.http Node port for HTTP. NOTE: choose port between <30000-32767> + # nodePorts: + # http: "30901" + + + ## ref: http://kubernetes.io/docs/user-guide/ingress/ + ingressAPI: + ## @param cas.ingressAPI.enabled Enable ingress record generation for %%MAIN_CONTAINER_NAME%% + ## + enabled: false + ## @param cas.ingressAPI.pathType Ingress path type + ## + pathType: ImplementationSpecific + ## @param cas.ingressAPI.hostname Default host for the ingress record + ## + hostname: api.cp.dev.local + ## @param cas.ingressAPI.ingressClassName IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) + ## This is supported in Kubernetes 1.18+ and required if you have more than one IngressClass marked as the default for your cluster . + ## ref: https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/ + ## + ingressClassName: "" + ## @param cas.ingressAPI.path Default path for the ingress record + ## NOTE: You may need to set this to '/*' in order to use this with ALB ingress controllers + ## + path: / + ## @param cas.ingressAPI.annotations Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. + ## Use this parameter to set the required annotations for cert-manager, see + ## ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations + ## e.g: + ## annotations: + ## kubernetes.io/controlplane.ingress.class: nginx + ## cert-manager.io/cluster-issuer: cluster-issuer-name + ## + annotations: {} + + ## @param cas.ingressAPI.tls Enable TLS configuration for the host defined at `controlplane.ingress.hostname` parameter + ## TLS certificates will be retrieved from a TLS secret with name: `{{- printf "%s-tls" .Values.controlplane.ingress.hostname }}` + ## You can: + ## - Use the `controlplane.ingress.secrets` parameter to create this TLS secret + ## - Rely on cert-manager to create it by setting the corresponding annotations + ## - Rely on Helm to create self-signed certificates by setting `controlplane.ingress.selfSigned=true` + ## + tls: false + ## @param cas.ingressAPI.selfSigned Create a TLS secret for this ingress record using self-signed certificates generated by Helm + ## + selfSigned: false + ## @param cas.ingressAPI.extraHosts An array with additional hostname(s) to be covered with the ingress record + ## e.g: + ## extraHosts: + ## - name: cp.dev.local + ## path: / + ## + extraHosts: [] + ## @param cas.ingressAPI.extraPaths An array with additional arbitrary paths that may need to be added to the ingress under the main host + ## e.g: + ## extraPaths: + ## - path: /* + ## backend: + ## serviceName: ssl-redirect + ## servicePort: use-annotation + ## + extraPaths: [] + ## @param cas.ingressAPI.extraTls TLS configuration for additional hostname(s) to be covered with this ingress record + ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls + ## e.g: + ## extraTls: + ## - hosts: + ## - cp.dev.local + ## secretName: cp.dev.local-tls + ## + extraTls: [] + ## @param cas.ingressAPI.secrets Custom TLS certificates as secrets + ## NOTE: 'key' and 'certificate' are expected in PEM format + ## NOTE: 'name' should line up with a 'secretName' set further up + ## If it is not set and you're using cert-manager, this is unneeded, as it will create a secret for you with valid certificates + ## If it is not set and you're NOT using cert-manager either, self-signed certificates will be created valid for 365 days + ## It is also possible to create and manage the certificates outside of this helm chart + ## Please see README.md for more information + ## e.g: + ## secrets: + ## - name: cp.dev.local-tls + ## key: |- + ## -----BEGIN RSA PRIVATE KEY----- + ## ... + ## -----END RSA PRIVATE KEY----- + ## certificate: |- + ## -----BEGIN CERTIFICATE----- + ## ... + ## -----END CERTIFICATE----- + ## + secrets: [] + ## @param cas.ingressAPI.extraRules Additional rules to be covered with this ingress record + ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-rules + ## e.g: + ## extraRules: + ## - host: example.local + ## http: + ## path: / + ## backend: + ## service: + ## name: example-svc + ## port: + ## name: http + ## + extraRules: [] + + ## @section CAS Misc + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## @param cas.resources.limits Container resource limits + ## @param cas.resources.requests Container resource requests + resources: + limits: {} + requests: {} + + ## Deployment autoscaling + ## @param cas.autoscaling.enabled Enable deployment autoscaling + ## @param cas.autoscaling.minReplicas Minimum number of replicas + ## @param cas.autoscaling.maxReplicas Maximum number of replicas + ## @param cas.autoscaling.targetCPUUtilizationPercentage Target CPU percentage + ## @param cas.autoscaling.targetMemoryUtilizationPercentage Target CPU memory + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + +## @section Dependencies +# ################################## +# # Dependencies # +# ################################## + +## PostgreSQL chart configuration +## ref: https://github.com/bitnami/charts/blob/main/bitnami/postgresql/values.yaml +## @param postgresql.enabled Switch to enable or disable the PostgreSQL helm chart +## @param postgresql.auth.enablePostgresUser Assign a password to the "postgres" admin user. Otherwise, remote access will be blocked for this user +## @param postgresql.auth.username Name for a custom user to create +## @param postgresql.auth.password Password for the custom user to create +## @param postgresql.auth.database Name for a custom database to create +## @param postgresql.auth.existingSecret Name of existing secret to use for PostgreSQL credentials +postgresql: + enabled: true + auth: + enablePostgresUser: false + username: "chainloop" + password: "chainlooppwd" + database: "chainloop-cp" + existingSecret: "" + + +# Vault server running in development mode --set development=true +# IMPORTANT: This is not meant to run in production + +## Hashicorp Vault chart configuration +## ref: https://github.com/hashicorp/vault-helm/blob/main/values.yaml +## @param vault.server.dev.enabled Enable development mode (unsealed, in-memory, insecure) +## @param vault.server.dev.devRootToken Connection token +vault: + server: + dev: + enabled: true + devRootToken: "notapassword" \ No newline at end of file diff --git a/docs/img/deployment-dev.png b/docs/img/deployment-dev.png new file mode 100644 index 000000000..bda403cbc Binary files /dev/null and b/docs/img/deployment-dev.png differ diff --git a/docs/img/deployment.png b/docs/img/deployment.png new file mode 100644 index 000000000..9a789157d Binary files /dev/null and b/docs/img/deployment.png differ