diff --git a/docs/architecture/concepts/external-key-management.md b/docs/architecture/concepts/external-key-management.md index 850ea50a..82b7f47f 100644 --- a/docs/architecture/concepts/external-key-management.md +++ b/docs/architecture/concepts/external-key-management.md @@ -1,6 +1,6 @@ --- title: "External Key Management" -description: "Data-at-rest encryption with external key management systems, enabling separation of duty, rotation, and audit." +description: "How simplyblock encrypts data at rest against an external key management system, and what separation of duty, key rotation, and audit that buys." weight: 30220 --- @@ -41,4 +41,13 @@ must be configured on the control plane before an external KMS can be wired up. Operationally, this means the KMS team and the storage team share only the CA bundle and an agreed-upon DNS-name for the simplyblock client. No static passwords or long-lived tokens must be exchanged. +## Where the KMS Runs + +A volume of the cluster is only usable once its DEK has been unwrapped, which makes the KMS a dependency of the data +path. The KMS must therefore not store its own state on the cluster it holds the keys for. Such a deployment +deadlocks on a cold start: the KMS waits for its data volume, and that volume waits for the KMS to unwrap its key. +The state cannot be recovered from inside the cluster, so the KMS is placed on storage that is available before +simplyblock is. + For the setup steps, see [Securing the Control Plane: External KMS](../../kubernetes/installation/security.md#external-key-management-kms). +A worked deployment of an instance is in [Deploying OpenBao as a KMS](../../tutorials/openbao-kms.md). diff --git a/docs/kubernetes/installation/security.md b/docs/kubernetes/installation/security.md index 01ab98b3..126010f5 100644 --- a/docs/kubernetes/installation/security.md +++ b/docs/kubernetes/installation/security.md @@ -93,99 +93,26 @@ either of them. ### Prerequisites -- [mTLS configured](#transport-layer-security-mutual-tls-mtls) is required, because the vault is authenticated to the cluster via - a certificate issued by the operator's `simplyblock-certificate-authority-issuer`. -- A Vault or OpenBao instance reachable from the simplyblock namespace. The instance must be initialized and unsealed - before configuring authentication. - -### Issue a TLS Certificate via Vault - -Create a Cert-Manager `Certificate` resource that uses the operator-managed issuer. The resulting secret holds the -TLS material that Vault serves to clients and is trusted by the simplyblock components because it chains to the same -CA. - -```yaml title="vault-tls.yaml" -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: vault-tls - namespace: vault -spec: - secretName: vault-tls - issuerRef: - name: simplyblock-certificate-authority-issuer - kind: ClusterIssuer - commonName: vault - dnsNames: - - vault - - vault.vault - - vault.vault.svc - - vault.vault.svc.cluster.local -``` - -Mount the resulting `vault-tls` secret into the Vault deployment as its serving certificate. Mount the issuer's CA -bundle (typically `ca.crt`) at a path the Vault can read. The example below assumes `/vault/tls/ca.crt` for Vault -and `/bao/tls/ca.crt` for OpenBao. - -### Deploy the Vault - -Install Vault or OpenBao using their upstream Helm chart and expose it inside the cluster. For the rest of this guide -the in-cluster service is assumed to be `vault.vault:8200`. Adjust the URL to match the actual deployment. - -### Configure Auth, Policy, and Secret Engines - -Configure the vault with a policy that grants simplyblock access to the `transit` and `kv` backends, enable the -certificate authentication bound to the simplyblock CA, and enable the required secret engines. The script below -works for both Vault (`vault`) and OpenBao (`bao`). Assign the appropriate CLI to the `$CLI` variable. - -```bash title="Configure the vault for simplyblock" -CLI=vault # or: CLI=bao - -# Policy granting access to the transit and kv backends -$CLI policy write webappapi-policy - < +``` + +```bash title="Checking the seal state" +kubectl -n vault exec openbao-0 -- \ + env BAO_ADDR=https://openbao.vault:8200/ bao status +``` + +The instance is ready once `Sealed` reads `false` and the pod turns ready. Note that a restart seals it again, which +makes unsealing an operational task rather than a one-off. Anything beyond a test instance is worth configuring for +auto-unseal against a cloud KMS. + +## Step 5: Open a Configuration Shell + +The remaining configuration runs inside the pod, with the root token from step 3. The shell carries the address, the +token, the name of the command line interface, and the path of the certificate authority, so that the commands below +are copied unchanged. + +```bash title="Opening a configuration shell on the OpenBao instance" +kubectl -n vault exec -it openbao-0 -- \ + env BAO_ADDR=https://openbao.vault:8200/ BAO_TOKEN= CLI=bao \ + CA=/openbao/tls/ca.crt sh +``` + +## Step 6: Write the Policy + +The policy is the boundary of what simplyblock may do with the instance. It may manage its own keys, wrap and unwrap +data keys, and store key material per volume. Nothing else. + +```bash title="Writing the policy for the simplyblock control plane" +$CLI policy write simplyblock-webappapi-policy - < +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: kms-check + namespace: simplyblock +spec: + accessModes: + - ReadWriteOnce + storageClassName: simplyblock-encrypted + resources: + requests: + storage: 1Gi +``` + +```bash title="Creating the encrypted volume" +kubectl apply -f kms-check.yaml +kubectl -n simplyblock get pvc kms-check +``` + +Once the claim is bound, the wrapped keys of the volume sit under `cluster//lvol/` in the key-value +engine, and the key that wraps them is a transit key named after the storage pool. + +```bash title="Listing the wrapped volume keys in OpenBao" +kubectl -n vault exec openbao-0 -- \ + env BAO_ADDR=https://openbao.vault:8200/ BAO_TOKEN= \ + bao kv list -mount=simplyblock/kv cluster//lvol +``` + +```bash title="Listing the wrapping keys in OpenBao" +kubectl -n vault exec openbao-0 -- \ + env BAO_ADDR=https://openbao.vault:8200/ BAO_TOKEN= \ + bao list simplyblock/transit/keys +``` + +The cluster UUID comes from `kubectl get storagecluster simplyblock-cluster -n simplyblock -o jsonpath='{.status.uuid}'`. +An entry per encrypted volume and a `pool-` key mean the path works end to end. An empty listing means the +control plane never reached the instance, and the reason is in its log: + +```bash title="Reading the control plane log after a failed key operation" +kubectl logs -n simplyblock deploy/simplyblock-operator +``` + +Delete the claim and the storage class once the check is done. + +```bash title="Removing the verification volume" +kubectl delete -f kms-check.yaml +``` + +## Using HashiCorp Vault Instead + +Vault is configured identically and differs in four places: + +- **The chart and the certificate.** The `hashicorp/vault` chart has no `extraObjects`, so the `Certificate` is + applied as its own resource, into the `vault` namespace and with `vault` DNS names, before the chart is installed. +- **The values.** Every `/openbao` path becomes `/vault`, `BAO_CACERT` becomes `VAULT_CACERT`, and the secret is named + `vault-server-tls`. +- **The commands.** The binary is `vault` rather than `bao`, the address variable is `VAULT_ADDR`, the token variable + is `VAULT_TOKEN`, and the pod is `vault-0`. +- **The endpoint.** The service is `https://vault.vault:8200/`, which is what `spec.hashicorpVaultSettings.baseURL` + then carries. + +```bash title="Installing Vault into the vault namespace" +helm repo add hashicorp https://helm.releases.hashicorp.com +kubectl create namespace vault +kubectl apply -f ./vault-certificate.yaml +helm install vault hashicorp/vault \ + -n vault \ + -f ./vault-values.yaml +``` + +## Where to Go Next + +- [External Key Management](../architecture/concepts/external-key-management.md) explains the two key layers and what + separation of duty the setup buys. +- [Securing the Control Plane](../kubernetes/installation/security.md#external-key-management-kms) is the reference + for the `StorageCluster` field and the behavior of existing volumes. +- [Volume Encryption](../kubernetes/usage/volume-encryption.md) covers encrypting volumes through a `StorageClass`.