From f1544eaa8dbe3f51448b237c77d7366342ca2faa Mon Sep 17 00:00:00 2001 From: Crossplane Date: Fri, 1 Apr 2022 13:08:14 +0000 Subject: [PATCH] docs snapshot for crossplane version `v1.7` --- .../adding_external_secret_store_support.md | 14 +- docs/v1.7/guides/vault-as-secret-store.md | 482 ++++++++++++++++++ 2 files changed, 491 insertions(+), 5 deletions(-) create mode 100644 docs/v1.7/guides/vault-as-secret-store.md diff --git a/docs/v1.7/contributing/adding_external_secret_store_support.md b/docs/v1.7/contributing/adding_external_secret_store_support.md index 4dc8ee87..83704977 100644 --- a/docs/v1.7/contributing/adding_external_secret_store_support.md +++ b/docs/v1.7/contributing/adding_external_secret_store_support.md @@ -1,5 +1,5 @@ --- -title: Adding External Secret Store Support to an Existing Provider +title: Adding Secret Store Support toc: true weight: 1004 indent: true @@ -20,6 +20,9 @@ feature enabled. In this document, we will go through each step in details. You can check [this PR as a complete example]. +> If your provider is a Terrajet based provider, then please check +> [this PR instead]. + ## Steps **1. Bump Crossplane Runtime and Crossplane Tools to latest and generate @@ -39,7 +42,7 @@ expect errors similar to below: make: *** [generate] Error 2 ``` -First we need to consume a temporary runtime version together with the latest +First, we need to consume a temporary runtime version together with the latest Crossplane Tools: ```shell @@ -76,9 +79,9 @@ Runtime: See [this commit as an example on how to add the type]. It is expected to be almost same for all providers except groupName which includes the name short -name of the provider (e.g. `gcp.secrets.crossplane.io`) +name of the provider (e.g. `gcp.crossplane.io`) -Generate CRD with: +Generate the CRD with: ```shell make generate @@ -130,6 +133,7 @@ example. [External Secret Stores]: https://github.com/crossplane/crossplane/blob/master/design/design-doc-external-secret-stores.md [this PR as a complete example]: https://github.com/crossplane/provider-gcp/pull/421 -[this commit as an example on how to add the type]: https://github.com/crossplane/provider-gcp/pull/421/commits/65c630b008e174336ac6b9dfbb319f0909514039 +[this PR instead]: https://github.com/crossplane-contrib/provider-jet-template/pull/23/commits +[this commit as an example on how to add the type]: https://github.com/crossplane/provider-aws/pull/1242/commits/d8a2df323fa2489d82bf1843d2fe338de033c61d [this commit as an example for adding the feature flag]: https://github.com/crossplane/provider-gcp/pull/421/commits/b5898c62dc6668d9918496de8aa9bc365c371f82 [this commit as an example for changes in Setup functions]: https://github.com/crossplane/provider-gcp/pull/421/commits/9700d0c4fdb7e1fba8805afa309c1b1c7aa167a6 \ No newline at end of file diff --git a/docs/v1.7/guides/vault-as-secret-store.md b/docs/v1.7/guides/vault-as-secret-store.md new file mode 100644 index 00000000..99fd2cb9 --- /dev/null +++ b/docs/v1.7/guides/vault-as-secret-store.md @@ -0,0 +1,482 @@ +--- +title: Vault as an External Secret Store +toc: true +weight: 230 +indent: true +--- + +# Using Vault as an External Secret Store + +This guide walks through the steps required to configure Crossplane and +its Providers to use [Vault] as an [External Secret Store]. For the sake of +completeness, we will also include steps for Vault installation and setup, +however, you can skip those and use your existing Vault. + +> External Secret Stores are an alpha feature. They are not yet recommended for +> production use, and are disabled by default. + +Crossplane consumes and also produces sensitive information to operate which +could be categorized as follows: + +1. **Provider credentials:** These are the credentials required for Providers +to authenticate against external APIs. For example, AWS Access/Secret keys, GCP +service account json, etc. +2. **Connection Details:** Once an infrastructure provisioned, we usually +need some connection data to consume it. Most of the time, this +information includes sensitive information like usernames, passwords or access +keys. +3. **Sensitive Inputs to Managed Resources:** There are some Managed resources +which expect input parameters that could be sensitive. Initial password of a +managed database is a good example of this category. + +It is already possible to use Vault for the 1st category (i.e. Provider +Credentials) as described in [the previous guide]. 3rd use case is a relatively +rare and being tracked with [this issue]. + +In this guide we will focus on the 2nd category, which is storing Connection +Details for managed resources in Vault. + +## Steps + +> Some steps in this guide duplicates [the previous guide] on Vault injection. +> However, for convenience, we put them here as well with minor +> changes/improvements. + +At a high level we will run the following steps: + +- Install and Unseal Vault. +- Configure Vault with Kubernetes Auth. +- Install and Configure Crossplane by enabling the feature. +- Install and Configure Provider GCP by enabling the feature. +- Deploy a Composition and CompositeResourceDefinition. +- Create a Claim. +- Verify all secrets land in Vault as expected. + +For simplicity, we will deploy Vault into the same cluster as Crossplane, +however, this is not a requirement as long as Vault has Kubernetes auth enabled +for the cluster where Crossplane is running. + +### Prepare Vault + +1. Install Vault Helm Chart + +```shell +kubectl create ns vault-system + +helm repo add hashicorp https://helm.releases.hashicorp.com --force-update +helm -n vault-system upgrade --install vault hashicorp/vault +``` + +2. [Unseal] Vault + +```shell +kubectl -n vault-system exec vault-0 -- vault operator init -key-shares=1 -key-threshold=1 -format=json > cluster-keys.json +VAULT_UNSEAL_KEY=$(cat cluster-keys.json | jq -r ".unseal_keys_b64[]") +kubectl -n vault-system exec vault-0 -- vault operator unseal $VAULT_UNSEAL_KEY +``` + +3. Configure Vault with Kubernetes Auth. + +In order for Vault to be able to authenticate requests based on Kubernetes +service accounts, the [Kubernetes auth method] must be enabled. +This requires logging in to Vault and configuring it with a service account +token, API server address, and certificate. Because we are running Vault in +Kubernetes, these values are already available via the container filesystem and +environment variables. + +Get Vault Root Token: + +```shell +cat cluster-keys.json | jq -r ".root_token" +``` + +Login as root and enable/configure Kubernetes Auth: + +```shell +kubectl -n vault-system exec -it vault-0 -- /bin/sh + +vault login # use root token from above + +vault auth enable kubernetes +vault write auth/kubernetes/config \ + token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ + kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \ + kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt + +exit # exit vault container +``` + +4. Enable Vault Key Value Secret Engine + +There are two different versions of [Vault KV Secrets Engine], `v1` and `v2`, +which you can find more details in the linked documentation page. +We will use `v2` in this guide as an example, however, both versions are +supported as an external secret store. + +```shell +kubectl -n vault-system exec -it vault-0 -- vault secrets enable -path=secret kv-v2 +``` + +5. Create a Vault Policy and Role for Crossplane + +```shell +kubectl -n vault-system exec -i vault-0 -- vault policy write crossplane - < Prerequisite: You should have a working **default** `ProviderConfig` for +> GCP available. + +1. Create a `Composition` and a `CompositeResourceDefinition`: + +```shell +echo "apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: compositeessinstances.ess.example.org + annotations: + feature: ess +spec: + group: ess.example.org + names: + kind: CompositeESSInstance + plural: compositeessinstances + claimNames: + kind: ESSInstance + plural: essinstances + connectionSecretKeys: + - publicKey + - publicKeyType + versions: + - name: v1alpha1 + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + parameters: + type: object + properties: + serviceAccount: + type: string + required: + - serviceAccount + required: + - parameters" | kubectl apply -f - + +echo "apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: essinstances.ess.example.org + labels: + feature: ess +spec: + publishConnectionDetailsWithStoreConfigRef: + name: vault + compositeTypeRef: + apiVersion: ess.example.org/v1alpha1 + kind: CompositeESSInstance + resources: + - name: serviceaccount + base: + apiVersion: iam.gcp.crossplane.io/v1alpha1 + kind: ServiceAccount + metadata: + name: ess-test-sa + spec: + forProvider: + displayName: a service account to test ess + - name: serviceaccountkey + base: + apiVersion: iam.gcp.crossplane.io/v1alpha1 + kind: ServiceAccountKey + spec: + forProvider: + serviceAccountSelector: + matchControllerRef: true + publishConnectionDetailsTo: + name: ess-mr-conn + metadata: + labels: + environment: development + team: backend + configRef: + name: vault + connectionDetails: + - fromConnectionSecretKey: publicKey + - fromConnectionSecretKey: publicKeyType" | kubectl apply -f - +``` + +2. Create a `Claim`: + +```shell +echo "apiVersion: ess.example.org/v1alpha1 +kind: ESSInstance +metadata: + name: my-ess + namespace: default +spec: + parameters: + serviceAccount: ess-test-sa + compositionSelector: + matchLabels: + feature: ess + publishConnectionDetailsTo: + name: ess-claim-conn + metadata: + labels: + environment: development + team: backend + configRef: + name: vault" | kubectl apply -f - +``` + +3. Verify all resources SYNCED and READY: + +```shell +kubectl get managed +# Example output: +# NAME READY SYNCED DISPLAYNAME EMAIL DISABLED +# serviceaccount.iam.gcp.crossplane.io/my-ess-zvmkz-vhklg True True a service account to test ess my-ess-zvmkz-vhklg@testingforbugbounty.iam.gserviceaccount.com + +# NAME READY SYNCED KEY_ID CREATED_AT EXPIRES_AT +# serviceaccountkey.iam.gcp.crossplane.io/my-ess-zvmkz-bq8pz True True 5cda49b7c32393254b5abb121b4adc07e140502c 2022-03-23T10:54:50Z + +kubectl -n default get claim +# Example output: +# NAME READY CONNECTION-SECRET AGE +# my-ess True 19s + +kubectl get composite +# Example output: +# NAME READY COMPOSITION AGE +# my-ess-zvmkz True essinstances.ess.example.org 32s +``` + +### Verify the Connection Secrets landed to Vault + +```shell +# Check connection secrets in the "default" scope (namespace). +kubectl -n vault-system exec -i vault-0 -- vault kv list /secret/default +# Example output: +# Keys +# ---- +# ess-claim-conn + +# Check connection secrets in the "crossplane-system" scope (namespace). +kubectl -n vault-system exec -i vault-0 -- vault kv list /secret/crossplane-system +# Example output: +# Keys +# ---- +# d2408335-eb88-4146-927b-8025f405da86 +# ess-mr-conn + +# Check contents of claim connection secret +kubectl -n vault-system exec -i vault-0 -- vault kv get /secret/default/ess-claim-conn +# Example output: +# ======= Metadata ======= +# Key Value +# --- ----- +# created_time 2022-03-18T21:24:07.2085726Z +# custom_metadata map[environment:development secret.crossplane.io/owner-uid:881cd9a0-6cc6-418f-8e1d-b36062c1e108 team:backend] +# deletion_time n/a +# destroyed false +# version 1 +# +# ======== Data ======== +# Key Value +# --- ----- +# publicKey -----BEGIN PUBLIC KEY----- +# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzsEYCokmYEsZJCc9QN/8 +# Fm1M/kTPp7Gat/MXLTP3zFyCTBFVNLN79MbAKdinWi6ePXEb75vzB79IdZcWj8lo +# 8trnS64QjNB9Vs4Xk5UvDALwleFN/bZeperxivDPwVPvT9Aqy/U9kohoS/LHyE8w +# uWQb5AuMeVQ1gtCTnCqQZ4d2MSVhQXYVvAWax1spJ9LT7mHub5j95xDdYIcOV3VJ +# l9CIo4VrWIT8THFN2NnjTrGq9+0TzXY0bV674bjJkfBC6v6yXs5HTetG+Uekq/xf +# FCjrrDi1+2UR9Mu2WTuvl8qn50be+mbwdJO5wE32jewxdYrVVmj19+PkaEeAwGTc +# vwIDAQAB +# -----END PUBLIC KEY----- +# publicKeyType TYPE_RAW_PUBLIC_KEY + +# Check contents of managed resource connection secret +kubectl -n vault-system exec -i vault-0 -- vault kv get /secret/crossplane-system/ess-mr-conn +# Example output: +# ======= Metadata ======= +# Key Value +# --- ----- +# created_time 2022-03-18T21:21:07.9298076Z +# custom_metadata map[environment:development secret.crossplane.io/owner-uid:4cd973f8-76fc-45d6-ad45-0b27b5e9252a team:backend] +# deletion_time n/a +# destroyed false +# version 2 +# +# ========= Data ========= +# Key Value +# --- ----- +# privateKey { +# "type": "service_account", +# "project_id": "REDACTED", +# "private_key_id": "REDACTED", +# "private_key": "-----BEGIN PRIVATE KEY-----\nREDACTED\n-----END PRIVATE KEY-----\n", +# "client_email": "ess-test-sa@REDACTED.iam.gserviceaccount.com", +# "client_id": "REDACTED", +# "auth_uri": "https://accounts.google.com/o/oauth2/auth", +# "token_uri": "https://oauth2.googleapis.com/token", +# "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", +# "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/ess-test-sa%40REDACTED.iam.gserviceaccount.com" +# } +# privateKeyType TYPE_GOOGLE_CREDENTIALS_FILE +# publicKey -----BEGIN PUBLIC KEY----- +# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzsEYCokmYEsZJCc9QN/8 +# Fm1M/kTPp7Gat/MXLTP3zFyCTBFVNLN79MbAKdinWi6ePXEb75vzB79IdZcWj8lo +# 8trnS64QjNB9Vs4Xk5UvDALwleFN/bZeperxivDPwVPvT9Aqy/U9kohoS/LHyE8w +# uWQb5AuMeVQ1gtCTnCqQZ4d2MSVhQXYVvAWax1spJ9LT7mHub5j95xDdYIcOV3VJ +# l9CIo4VrWIT8THFN2NnjTrGq9+0TzXY0bV674bjJkfBC6v6yXs5HTetG+Uekq/xf +# FCjrrDi1+2UR9Mu2WTuvl8qn50be+mbwdJO5wE32jewxdYrVVmj19+PkaEeAwGTc +# vwIDAQAB +# -----END PUBLIC KEY----- +# publicKeyType TYPE_RAW_PUBLIC_KEY +``` + +The commands above verifies using the cli, however, you can also connect to the +Vault UI and check secrets there. + +```shell +kubectl -n vault-system port-forward vault-0 8200:8200 +``` + +Now, you can open http://127.0.0.1:8200/ui in browser and login with the root token. + +### Cleanup + +Delete the claim which should clean up all the resources created. + +``` +kubectl -n default delete claim my-ess +``` + + + +[Vault]: https://www.vaultproject.io/ +[External Secret Store]: https://github.com/crossplane/crossplane/blob/master/design/design-doc-external-secret-stores.md +[the previous guide]: vault-injection.md +[this issue]: https://github.com/crossplane/crossplane/issues/2985 +[Kubernetes Auth Method]: https://www.vaultproject.io/docs/auth/kubernetes +[Unseal]: https://www.vaultproject.io/docs/concepts/seal +[Vault KV Secrets Engine]: https://www.vaultproject.io/docs/secrets/kv +[Vault Agent Sidecar Injection]: https://www.vaultproject.io/docs/platform/k8s/injector