Add docs to repo (#38)

This commit is contained in:
Moritz Eckert 2022-09-02 11:52:42 +02:00 committed by GitHub
parent 50d3f3ca7f
commit b95f3dbc91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
180 changed files with 13401 additions and 67 deletions

41
rfc/key-derivation.md Normal file
View file

@ -0,0 +1,41 @@
# Key derivation
To derive disk-encryption keys and other secret material in a Constellation Kubernetes cluster, we use [HKDF](https://datatracker.ietf.org/doc/html/rfc5869).
To derive a key, HKDF depends on four parameters: *input keying material (IKM)*, *salt*, *info*, and a hash function.
The parameters *salt* and *info* are optional.
Key derivation is performed in two steps:
1. [Extract](https://datatracker.ietf.org/doc/html/rfc5869#section-2.2)
A pseudorandom key is generated from the secret *IKM* and an optional independent *salt*.
2. [Expand](https://datatracker.ietf.org/doc/html/rfc5869#section-2.3)
The pseudorandom key generated by the extract phase is used to derive keys.
Using the optional *info* parameter multiple different keys can be created from the key created in the extract phase.
## Hash
We use SHA-256 as the hash function.
## Initial Key Material
Constellation's master secret is used as *IKM* for HKDF.
The master secret is either provided by the admin, or a randomly generated 32 byte value.
It is supplied to the cluster during `constellation init` and in the following used by [Constellation's KMS](../kms/) and managed by Kubernetes as a [secret](https://kubernetes.io/docs/concepts/configuration/secret/).
## Salt
To salt the extract phase of HKDF, Constellation uses a randomly generated 32 Byte value during `constellation init`.
The *salt* is returned to the user for data recovery, and kept in the cluster as a Kubernetes secret.
## Info
The *info* field is used to add context-specific information to a derived key.
This allows using the same *IKM* to derive multiple different keys.
### Disk-encryption key
For disk-encryption keys, *info* is set to the string `key-` appended with the lower-case LUKS2 UUID of the disk.

43
rfc/proof-of-ownership.md Normal file
View file

@ -0,0 +1,43 @@
# Proof of Ownership
A cluster owner needs a way to prove a cluster belongs to them, while a third-party needs to be able to verify the owner's claims.
For that, the owner generates a private/public key pair.
During `constellation init`, the cluster will generate its own private/public key pair, and send back a signing request for the public key.
The signed public key is measured into a PCR, so the binding of the private/public key to the cluster can be verified through remote attestation.
The cluster is now able to sign data using its own private key.
A third-party can verify a cluster belongs to a specific person in three steps:
1. Verify the signature of data provided by the third-party and signed by the cluster
2. Verify the cluster's public key was signed by the owner
3. Verify the public key is measured into a PCR by validating the cluster's attestation statement
## Workflow
1. Cluster owner generates a private/public key pair
2. The Constellation cluster generates its own private/public key pair and requests the owner to sign the public key during `constellation init`
3. Constellation measures the signed public key into PCR[11] (previously used for ownerID)
4. A third-party requests an attestation from the verification service, providing some data to be signed
5. The verification service signs the data using its private key
6. The verification service returns: attestation document + data signature + signed public key
7. The third-party verifies the public key signature using the owner's public key
8. The third-party calculates the expected PCR[11] using the signed public key and validates the attestation document
9. The data signature is verified, and if successful proving ownership of the cluster
## Encoding
The signed public key measured into PCR[11] is DER encoded.
TODO: Add exact encoding specification

View file

@ -0,0 +1,217 @@
# Secure Distribution of CLI & Measurements
## Goal / Motivation
We need a mechanism to provide trusted measurements, for a specific version,
to users of `constellation` CLI, when verifying a Constellation cluster.
Since `constellation` CLI is responsible for carrying out the verification,
we also enable users to verify the CLI they are using.
## Limitations
+ We only support a single measurement per image.
Updating measurements (e.g. when cloud provider replaced firmware) will be solved later.
+ First implementation only supports GitHub as "KMS" for our cosign key.
Later on, we can support a proper KMS with key rotation, derivation, revocation & TEE.
+ In future, we might support multi-party-signatures for measurements.
This would allow customers to explicitly trust a measurement.
+ This implementation will not protect against downgrade attacks. For a full
implementation we should check how we can integrate with [TUF](https://theupdateframework.io/)
& [in-toto](https://in-toto.io/).
## Alternative Solutions
+ We could enable users to reproduce measurements.
This way users do not need to trust our signed measurements,
but are able to generate this data themselves.
+ We could also enable users to build CLI themselves from source (open source).
We might support some or all alternative solutions in future.
## High Level Solution
We will create a single `cosign` key `Signer` in GitHub:
1. `Signer` signs `constellation` CLI
+ `Signer`'s public key is part of each CLI release
+ Customer verify CLI **before** first use
2. `constellation` CLI contains public key of `Signer`
+ By verifying integrity of CLI, user can trust this public key and do not need to supply it
+ `constellation config fetch-measurements` downloads measurements for current image
version from a known (or configurable) place
+ CLI uses `Signer`'s public key to verify downloaded measurements
+ CLI stores verified measurements in local `constellation-config.yaml`
3. `constellation verify` reads measurements from config & compares to cluster's measurements
+ No changes necessary here.
## Detailed Solution
### Key Generation
#### Generate locally
```sh
COSIGN_PASSWORD=$(openssl rand --hex 32)
cosign generate-key-pair
```
Generates a password protected keypair locally.
This keypair can be configured manually to GitHub.
This keypair could also be backed-up onto USB stick / SD card.
### Sign & Verify CLI
```sh
# Set these beforehand!
# COSIGN_PASSWORD=
# COSIGN_PRIVATE_KEY=
# COSIGN_PUBLIC_KEY=
go build constellation
COSIGN_EXPERIMENTAL=1 cosign sign-blob --key env://COSIGN_PRIVATE_KEY constellation > constellation.sig
# We provide: cosign.pub, constellation.sig, constellation
echo "$COSIGN_PUBLIC_KEY" > cosign.pub
cosign verify-blob --key cosign.pub --signature constellation.sig constellation
# We provide: cosign.pub, constellation (requires access to rekor.dev, but no signature to be distributed)
uuid=$(rekor-cli search --artifact constellation | tail -n 1)
sig=$(rekor-cli get --uuid=$uuid --format=json | jq -r .Body.HashedRekordObj.signature.content)
cosign verify-blob --key cosign.pub --signature <(echo $sig) constellation
```
```mermaid
flowchart LR
cli[Constellation CLI]
clisig[Constellation CLI signature]
signer[Cosign Keypair]
signer -- signs --> cli
signer -- produces --> clisig
clisig -- uploaded to --> rekor
clisig -- uploaded to --> gh
cli -- uploaded to --> gh
subgraph artifacts [Artifact Storage]
rekor[Rekor]
gh[GitHub Releases]
end
```
### Sign Measurements
We regularly spin up a Constellation cluster in our pipeline, store the observed
measurements and keep them in a file (`measurements.yaml`) compatible with our config file.
Comments should be omitted in final file. They show why certain values might be missing.
Those measurements are signed and uploaded to AWS S3. Stored at a path matching the configured image.
```yaml
azure:
image: /subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/constellation-images/providers/Microsoft.Compute/galleries/Constellation/images/constellation-coreos/versions/0.0.1655304334
measurements:
# 0: q27iAZeXGAiCPdu1bqRA2gAoyMO2KrXWY4YkTCQowc4= # Unstable: UEFI on Azure
1: 0GqVBBcu78dlLW03pON6OJbjQTMsKZmN+SV88kWHPss=
2: PUWM/lXMA+ofRD8VYr7sjfUcdeFKn8+acjShPxmOeWk=
3: PUWM/lXMA+ofRD8VYr7sjfUcdeFKn8+acjShPxmOeWk=
4: Mnl6y16fHWpwSGWZsSFiLc4NYXRhQ39UqkClHcDbJ2s=
5: qJ2QqWHIFfV9UILu76d3fGXdZz/RpZ/TcFyw7kPHzj4=
# 6: glvMCHop3keeyU2xBHJTpYmEuqKqXJqCRQuQi8C3n4w= # Unstable: VM Name Encoded on Azure
7: DnCqTk4YKN60heuvyzPoPH9uJ3yn3SgjaK1w59xmvvg=
8: hcLg6uP27lj28A+TExXlsv34EOmOh1jzdCofrBZS5gU=
9: hwxFDmhNROlS7oBh4dG3jzB4OeQAGhcZD9f6bwBtK/k=
# 10: PPiSI1eZHs+S/BgVWqewZAXDhkVgtIW8/PPgpR9Sr2o= # Unstable: Linux IMA records on Azure
# 11: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= # Set by us
# 12: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= # Set by us
# 13..23 are all unused
```
```sh
# Set these beforehand!
# COSIGN_PASSWORD=
# COSIGN_PRIVATE_KEY=
COSIGN_EXPERIMENTAL=1 cosign sign-blob --key cosign.key measurements.yaml > measurements.yaml.sig
```
```mermaid
flowchart LR
measurementssig[measurements.yaml signature]
measurements[measurements.yaml]
signer[Cosign Keypair]
cloud[Cloud Infrastructure<br />GCP/Azure]
pcrreader[pcr-reader]
cloud -- uses --> aig
cloud -- uses --> gmi
pcrreader -- writes --> measurements
pcrreader -- reads --> cloud
signer -- produces --> measurementssig
signer -- signs --> measurements
measurementssig -- uploaded to --> rekor
measurementssig -- uploaded to --> s3
measurements -- uploaded to --> s3
subgraph artifacts [Artifact Storage]
rekor[Rekor]
boots[Bootstrapper]
s3[S3]
aig[Azure Image Gallery]
gmi[Google Machine Images]
gmi -- contains --> boots
aig -- contains --> boots
end
```
### Verify Measurements
`constellation config fetch-measurements`
1. Read `.provider.[azure|gcp].image` from `constellation-conf.yaml`
2. Fetch measurements for this image from S3
+ Alternatively: Use rekor to fetch signature for this artifact
3. Use embedded public key to verify signature.
4. Write measurements to local config
No changes required for `constellation verify`.
## Next Increments
### Key Management
#### Trust Public Key
Upload our public key to public places controlled by us, e.g.,
+ Twitter
+ Website
Users can check that public keys are the same in all those places, and therefore have not been tampered with.
#### Local Build Agent
Instead of using a GitHub managed runner when building and signing our release, we should use the one we have in the office. We also re-generate the key pair and store it locally on the build runner, as well as a back up on a USB stick.
The goal is to remove GitHub from the TCB.
#### Confidential Build Agent
We should investigate if an Enclave could be used as a confidential build agent for building and signing the CLI.
Private signing key can be stored encrypted anywhere and is only ever decrypted in TEE.
## Potential Problems
### How do we handle, if one of our private signing keys is leaked?
We will investigate how [TUF](https://theupdateframework.io/) can be used to improve key life cycle management. A [TUF / cosing integration](https://github.com/sigstore/cosign/issues/86) is still being worked on.
## Roads Not Taken
### Fulcio / OIDC Based Identities
Sigstore supports creating ephemeral keys and trusting in OIDC identities.
We could use our @edgeless.systems Office365 accounts, but in order to trust
these identities we would need to prove our Office365 subscription is secure and
trustworthy.
Additionally our TCB would increase and include Microsoft & Sigstore Root CA.
Therefore we decided to manage our own signing keys.