mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-05-02 14:26:23 -04:00
Add docs to repo (#38)
This commit is contained in:
parent
50d3f3ca7f
commit
b95f3dbc91
180 changed files with 13401 additions and 67 deletions
41
rfc/key-derivation.md
Normal file
41
rfc/key-derivation.md
Normal 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
43
rfc/proof-of-ownership.md
Normal 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
|
217
rfc/secure-software-distribution.md
Normal file
217
rfc/secure-software-distribution.md
Normal 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.
|
Loading…
Add table
Add a link
Reference in a new issue