Skip to content

Commit

Permalink
add docs for csi-driver-spiffe v0.6.0
Browse files Browse the repository at this point in the history
Also adds security documentation and other improvements for
csi-driver-spiffe docs in general

Signed-off-by: Ashley Davis <[email protected]>
  • Loading branch information
SgtCoDFish committed May 16, 2024
1 parent fb36a15 commit 107bb9c
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 31 deletions.
102 changes: 80 additions & 22 deletions content/docs/usage/csi-driver-spiffe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ These documents in turn have the following properties:
- private key never leaves the node's virtual memory ✔️
- each Pod's document is unique ✔️
- the document shares the same life cycle as the Pod and is destroyed on Pod termination ✔️
- enable mTLS within a trust domain ✔️

```yaml
...
Expand All @@ -31,40 +32,38 @@ These documents in turn have the following properties:
readOnly: true
```
SPIFFE documents can then be used by Pods for mutual TLS (mTLS) or other authentication within their Trust Domain.
### Components
The project is split into two components.
The project is split into two components: the driver and the approver.
#### CSI Driver
The CSI driver runs as DaemonSet on the cluster which is responsible for
generating, requesting, and mounting the certificate key pair to Pods on the
The CSI driver runs as DaemonSet on the cluster and is responsible for
generating, requesting, and mounting a certificate and private key to Pods on the
node it manages. The CSI driver creates and manages a
[tmpfs](https://www.kernel.org/doc/html/latest/filesystems/tmpfs.html) directory
which is used to create and mount Pod volumes from.
[tmpfs](https://www.kernel.org/doc/html/latest/filesystems/tmpfs.html) directory.
When a Pod is created with the CSI volume configured, the
driver will locally generate a private key, and create a cert-manager
[CertificateRequest](../../usage/certificaterequest.md)
in the same Namespace as the Pod.
The driver uses [CSI Token Request](https://kubernetes-csi.github.io/docs/token-requests.html) to both
discover the Pod's identity to form the SPIFFE identity contained in the X.509
certificate signing request, as well as securely impersonate its ServiceAccount
when creating the CertificateRequest.
The driver uses [CSI Token Requests](https://kubernetes-csi.github.io/docs/token-requests.html).
This means that the token of the pod being created is passed to the driver.
Once signed by the pre-configured target signer, the driver will mount the
private key and signed certificate into the Pod's Volume to be made available as
a Volume Mount. This certificate key pair is regularly renewed based on the
expiry of the signed certificate.
This token's details are used for creating the SPIFFE ID which represents the pod's identity,
and the token is used for creating the actual CertificateRequest for the SVID.
Once the certificate is signed by the configured cert-manager issuer, the driver
mounts the private key and certificate into the Pod's Volume, and watches the
certificate to renew it and the private key based on the certificate's
expiry date.
#### Approver
A distinct [cert-manager approver](../../usage/certificaterequest.md#approval)
Deployment is responsible for managing the approval and denial condition of
created CertificateRequests that target the configured SPIFFE Trust Domain
signer.
Deployment is responsible for managing approval and denial of csi-driver-spiffe
CertificateRequests.
The approver ensures that requests have:
Expand All @@ -76,16 +75,48 @@ The approver ensures that requests have:
the CertificateRequest;
5. a SPIFFE ID Trust Domain matching the one that was configured at startup.
If any of these checks do not pass, the CertificateRequest will be marked as
Denied, else it will be marked as Approved. The approver will only manage
CertificateRequests who request from the same [IssuerRef](../../usage/certificaterequest.md)
that has been configured.
The approver only considers CertificateRequests which have the
`spiffe.csi.cert-manager.io/identity` annotation, which is added by csi-driver-spiffe
to all requests it creates.

## Installation

See the [installation guide](./installation.md) for instructions on how to
install csi-driver-spiffe.

## Security Considerations

csi-driver-spiffe deals with highly valuable credentials which should be kept secret. The design
of using a Kubernetes CSI volume makes it simple to restrict access to only the pod which mounts
the CSI volume, but care should still be taken not to expose the private key created by csi-driver-spiffe.

csi-driver-spiffe _always_ uses the token of the pod it's issuing for to create the CertificateRequest
resource for the SVID it creates. That means that:

1. During issuance, csi-driver-spiffe has the ability to do whatever the Pod can do; bear in mind that a
compromised csi-driver-spiffe pod could abuse these permissions, although in normal operation only the
CertificateRequest permission is used.
2. Importantly: all pods using csi-driver-spiffe must have permission to create CertificateRequest resources.

The requirement for a pod to have permission to create CertificateRequests has important security
implications. If a Pod with these permissions is compromised it can create arbitrary CertificateRequest
resources, which could reference arbitrary issuers in the cluster.

Since csi-driver-spiffe requires that approval is used in-cluster, this risk is mitigated by approval.

It's important, though, that any other forms of approval in the cluster (such as approver-policy) are
carefully configured not to overlap with the csi-driver-spiffe approver and to restrict access to other
issuers.

For example, an approver-policy `CertificateRequestPolicy` resource which allowed any pod to issue
using an ACME (Let's Encrypt) issuer might allow a compromised Pod to issue a publicly trusted
certificate, if that Pod used csi-driver-spiffe and thus had permissions to create CertificateRequest
resources.

Using csi-driver-spiffe safely means considering which approval methods are available in your cluster
and carefully configuring those approvers to ensure that pods cannot target issuers they're not meant
to be able to use.

## Usage

Once the driver is successfully installed, Pods can begin to request and mount
Expand Down Expand Up @@ -115,6 +146,30 @@ kubectl exec -n sandbox \
# expected output: URI:spiffe://foo.bar/ns/sandbox/sa/example-app
```

### Runtime Configuration

If csi-driver-spiffe was installed with runtime configuration enabled, it will watch
a named ConfigMap for issuer configuration. If that ConfigMap exists and contains a valid issuer
reference, that issuer will be used for all created CertificateRequest resources.

If the ConfigMap is deleted or does not contain a valid issuer reference, it will be
ignored. If a default issuer was specified at install time, that default will be used
as a fallback. If no valid runtime configuration is provided and no default issuer was
specified, issuance (and therefore pod creation) will fail until a valid issuer is configured.

The name of the runtime configuration ConfigMap is set with the `app.runtimeIssuanceConfigMap`
Helm value at install time. A valid ConfigMap must contain the `issuer-name`, `issuer-kind`
and `issuer-group` keys.

An example of creating a ConfigMap for a ClusterIssuer named `my-issuer-name` is below:

```console
kubectl create configmap spiffe-issuer -n cert-manager \
--from-literal=issuer-name=my-issuer-name \
--from-literal=issuer-kind=ClusterIssuer \
--from-literal=issuer-group=cert-manager.io
```

### FS-Group

When running Pods with a specified user or group, the volume will not be
Expand Down Expand Up @@ -144,6 +199,9 @@ kubectl exec -n sandbox $(kubectl get pod -n sandbox -l app=my-csi-app-fs-group

### Root CA Bundle

> ⚠️ This feature is significantly less powerful than [trust-manager](../../trust/trust-manager/README.md),
> and is much harder to use and update. It's recommended to use trust-manager instead.

By default, the CSI driver will only mount the Pod's private key and signed
certificate. csi-driver-spiffe can be optionally configured to also mount a
statically defined CA bundle from a volume that will be written to all Pod
Expand All @@ -159,8 +217,8 @@ ClusterIssuer.
helm upgrade -i -n cert-manager cert-manager-csi-driver-spiffe jetstack/cert-manager-csi-driver-spiffe --wait \
--set "app.logLevel=1" \
--set "app.trustDomain=my.trust.domain" \
--set "app.approver.signerName=clusterissuers.cert-manager.io/csi-driver-spiffe-ca" \
\
--set "app.runtimeIssuanceConfigMap=spiffe-issuer"
--set "app.issuer.name=csi-driver-spiffe-ca" \
--set "app.issuer.kind=ClusterIssuer" \
--set "app.issuer.group=cert-manager.io" \
Expand Down
59 changes: 50 additions & 9 deletions content/docs/usage/csi-driver-spiffe/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ a default installation of cert-manager **will not work**.
If the default approver is not disabled, the csi-driver-spiffe approver will
race with cert-manager and policy enforcement will become useless.

Policy enforcement is absolutely critical for using csi-driver-spiffe safely. See
the [security considerations](./README.md#security-considerations) section for more details.

```bash
helm repo add jetstack https://charts.jetstack.io --force-update

Expand All @@ -30,20 +33,25 @@ helm upgrade -i -n cert-manager cert-manager jetstack/cert-manager \

### 2. Configure an Issuer / ClusterIssuer

Install or configure a [ClusterIssuer](../../configuration/README.md) to give
cert-manager the ability to sign against your Trust Domain.
> This step can be deferred if you use [runtime configuration](./README.md#runtime-configuration),
> but a valid issuer must be configured before a pod can successfully use
> csi-driver-spiffe to obtain an SVID.
Install or configure some kind of issuer which will be used for signing
CertificateRequest resources in your Trust Domain.

If you want a namespace-scoped Issuer, then it must be created in every namespace
If you wish to use a namespace-scoped issuer it must be created in every namespace
that Pods will mount volumes from.

You must use an Issuer type which is compatible with signing URI SAN certificates;
ACME issuers won't generally work, and the SelfSigned issuer is not appropriate.
You must use an Issuer type which is compatible with signing certificates with a custom
URI SAN. ACME issuers won't generally work, and the SelfSigned issuer is not appropriate.

An example demo [ClusterIssuer](../../concepts/issuer.md#namespaces) can
be found [in the csi-driver-spiffe repo](https://github.com/cert-manager/csi-driver-spiffe/blob/ed646ccf28b1ecdf63f628bf16f1d350a9b850c1/deploy/example/clusterissuer.yaml).

> ⚠️ This Trust Domain's root CA is generated by cert-manager and **the private key is stored in the cluster**
> This might not be appropriate for production deployments!
> ⚠️ This Trust Domain's root CA is generated by cert-manager and **the private key is stored in the cluster**!
>
> This might not be appropriate for production deployments, depending on your threat model.
We'll also use [cmctl](../../reference/cmctl.md) to approve the CertificateRequest,
since the default approver was disabled above.
Expand All @@ -63,15 +71,48 @@ Install csi-driver-spiffe into the cluster using the issuer we configured. We
must also configure the issuer resource type and name of the issuer we
configured so that the approver has [permissions to approve referencing CertificateRequests](../../usage/certificaterequest.md#rbac-syntax).

Installation varies slightly depending on whether you want to use runtime configuration, which is recommended.

#### With Runtime Configuration

First, create a ConfigMap in the installation namespace with details of the issuer to want to use.

The issuer doesn't have to exist until a pod tries to mount csi-driver-spiffe.

The name of the ConfigMap is passed into csi-driver-spiffe at install time.

```bash
kubectl create configmap -n cert-manager spiffe-issuer \
--from-literal=issuer-name=csi-driver-spiffe-ca \
--from-literal=issuer-kind=ClusterIssuer \
--from-literal=issuer-group=cert-manager.io
```

```bash
helm upgrade -i -n cert-manager cert-manager-csi-driver-spiffe jetstack/cert-manager-csi-driver-spiffe --wait \
--set "app.logLevel=1" \
--set "app.trustDomain=my.trust.domain" \
--set "app.issuer.name=" \
--set "app.issuer.kind=" \
--set "app.issuer.group=" \
--set "app.runtimeIssuanceConfigMap=spiffe-issuer"
```

In the example above the default issuer values (i.e. `app.issuer.name`, `app.issuer.kind` and `app.issuer.group`)
are explicitly set to be empty, meaning that only runtime configuration will be used.

You can set the default issuer values if you want to fall back to that issuer in the event that the runtime
configuration ConfigMap is invalid or deleted.

#### Without Runtime Configuration

Note that the `issuer.name`, `issuer.kind` and `issuer.group` will need to be changed to match
the issuer you're actually using!

```bash
helm upgrade -i -n cert-manager cert-manager-csi-driver-spiffe jetstack/cert-manager-csi-driver-spiffe --wait \
--set "app.logLevel=1" \
--set "app.trustDomain=my.trust.domain" \
--set "app.approver.signerName=clusterissuers.cert-manager.io/csi-driver-spiffe-ca" \
\
--set "app.issuer.name=csi-driver-spiffe-ca" \
--set "app.issuer.kind=ClusterIssuer" \
--set "app.issuer.group=cert-manager.io"
Expand Down

0 comments on commit 107bb9c

Please sign in to comment.