mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-24 23:19:39 -05:00
Attestation logging (#275)
* Add section for checking joinservice logs * Add logging for attestation validation Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
parent
e7118223fe
commit
f068e50dee
@ -177,6 +177,9 @@ type warnLogger struct {
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
// Infof is a no-op since we don't want extra info messages when using the CLI.
|
||||
func (wl warnLogger) Infof(format string, args ...interface{}) {}
|
||||
|
||||
// Warnf prints a formatted warning from the validator.
|
||||
func (wl warnLogger) Warnf(fmtStr string, args ...interface{}) {
|
||||
wl.cmd.PrintErrf("Warning: %s\n", fmt.Sprintf(fmtStr, args...))
|
||||
|
@ -79,7 +79,49 @@ Configure `kubectl` to connect to your local Constellation cluster:
|
||||
export KUBECONFIG="$PWD/constellation-admin.conf"
|
||||
```
|
||||
|
||||
It may take a couple of minutes for all cluster resources to be available.
|
||||
Your cluster initially consists of a single control-plane node:
|
||||
|
||||
```shell-session
|
||||
$ kubectl get nodes
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
control-plane-0 Ready control-plane 66s v1.24.6
|
||||
```
|
||||
|
||||
A worker node will request to join the cluster shortly.
|
||||
|
||||
Before the new worker node is allowed to join the cluster, its state is verified using remote attestation by the [JoinService](../architecture/components.md#joinservice).
|
||||
If verification passes successfully, the new node receives keys and certificates to join the cluster.
|
||||
|
||||
You can follow this process by viewing the logs of the JoinService:
|
||||
|
||||
```shell-session
|
||||
$ kubectl logs -n kube-system daemonsets/join-service -f
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:20Z","caller":"cmd/main.go:48","msg":"Constellation Node Join Service","version":"2.1.0","cloudProvider":"qemu"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:20Z","logger":"validator","caller":"watcher/validator.go:96","msg":"Updating expected measurements"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:20Z","logger":"server","caller":"server/server.go:73","msg":"Starting join service on [::]:9090"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:20Z","caller":"cmd/main.go:103","msg":"starting file watcher for measurements file /var/config/measurements"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"server","caller":"server/server.go:86","msg":"IssueJoinTicket called","peerAddress":"10.42.2.100:59988"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"server","caller":"server/server.go:88","msg":"Requesting measurement secret","peerAddress":"10.42.2.100:59988"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"kms","caller":"kms/kms.go:41","msg":"Connecting to KMS at kms.kube-system:9000","keyID":"measurementSecret","endpoint":"kms.kube-system:9000"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"kms","caller":"kms/kms.go:48","msg":"Requesting data key","keyID":"measurementSecret","endpoint":"kms.kube-system:9000"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"kms","caller":"kms/kms.go:61","msg":"Data key request successful","keyID":"measurementSecret","endpoint":"kms.kube-system:9000"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"server","caller":"server/server.go:95","msg":"Requesting disk encryption key","peerAddress":"10.42.2.100:59988"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"kms","caller":"kms/kms.go:41","msg":"Connecting to KMS at kms.kube-system:9000","keyID":"0f87c61f-31e7-466d-be22-e7300e7d9e76","endpoint":"kms.kube-system:9000"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"kms","caller":"kms/kms.go:48","msg":"Requesting data key","keyID":"0f87c61f-31e7-466d-be22-e7300e7d9e76","endpoint":"kms.kube-system:9000"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"kms","caller":"kms/kms.go:61","msg":"Data key request successful","keyID":"0f87c61f-31e7-466d-be22-e7300e7d9e76","endpoint":"kms.kube-system:9000"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"server","caller":"server/server.go:102","msg":"Creating Kubernetes join token","peerAddress":"10.42.2.100:59988"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"kubeadm","caller":"kubeadm/kubeadm.go:63","msg":"Generating new random bootstrap token"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"kubeadm","caller":"kubeadm/kubeadm.go:81","msg":"Creating bootstrap token in Kubernetes"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"kubeadm","caller":"kubeadm/kubeadm.go:87","msg":"Preparing join token for new node"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"kubeadm","caller":"kubeadm/kubeadm.go:109","msg":"Join token creation successful"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"server","caller":"server/server.go:109","msg":"Querying K8sVersion ConfigMap","peerAddress":"10.42.2.100:59988"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"server","caller":"server/server.go:115","msg":"Creating signed kubelet certificate","peerAddress":"10.42.2.100:59988"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"certificateAuthority","caller":"kubernetesca/kubernetesca.go:84","msg":"Creating kubelet certificate"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"server","caller":"server/server.go:138","msg":"IssueJoinTicket successful","peerAddress":"10.42.2.100:59988"}
|
||||
{"level":"INFO","ts":"2022-10-14T09:32:21Z","logger":"server.gRPC","caller":"zap/server_interceptors.go:39","msg":"finished unary call with code OK","grpc.start_time":"2022-10-14T09:32:21Z","grpc.request.deadline":"2022-10-14T09:32:51Z","system":"grpc","span.kind":"server","grpc.service":"join.API","grpc.method":"IssueJoinTicket","peer.address":"10.42.2.100:59988","grpc.code":"OK","grpc.time_ms":27.715}
|
||||
```
|
||||
|
||||
Once the worker node has joined your cluster, it may take a couple of minutes for all resources to be available.
|
||||
You can check on the state of your cluster by running the following:
|
||||
|
||||
```bash
|
||||
@ -90,9 +132,9 @@ If your cluster is running as expected the output should look like the following
|
||||
|
||||
```shell-session
|
||||
$ kubectl get nodes
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
control-plane-0 Ready control-plane,master 2m59s v1.23.9
|
||||
worker-0 Ready <none> 32s v1.23.9
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
control-plane-0 Ready control-plane 2m59s v1.24.6
|
||||
worker-0 Ready <none> 32s v1.24.6
|
||||
```
|
||||
|
||||
## Deploy a sample application
|
||||
|
@ -41,7 +41,7 @@ type Validator struct {
|
||||
}
|
||||
|
||||
// NewValidator initializes a new Azure validator with the provided PCR values.
|
||||
func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32, idKeyDigest []byte, enforceIDKeyDigest bool, log vtpm.WarnLogger) *Validator {
|
||||
func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32, idKeyDigest []byte, enforceIDKeyDigest bool, log vtpm.AttestationLogger) *Validator {
|
||||
return &Validator{
|
||||
Validator: vtpm.NewValidator(
|
||||
pcrs,
|
||||
@ -76,7 +76,9 @@ func reverseEndian(b []byte) {
|
||||
|
||||
// getTrustedKey establishes trust in the given public key.
|
||||
// It does so by verifying the SNP attestation statement in instanceInfo.
|
||||
func getTrustedKey(hclAk HCLAkValidator, idKeyDigest []byte, enforceIDKeyDigest bool, log vtpm.WarnLogger) func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) {
|
||||
func getTrustedKey(
|
||||
hclAk HCLAkValidator, idKeyDigest []byte, enforceIDKeyDigest bool, log vtpm.AttestationLogger,
|
||||
) func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) {
|
||||
return func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) {
|
||||
var instanceInfo azureInstanceInfo
|
||||
if err := json.Unmarshal(instanceInfoRaw, &instanceInfo); err != nil {
|
||||
@ -140,7 +142,10 @@ func validateVCEK(vcekRaw []byte, certChain []byte) (*x509.Certificate, error) {
|
||||
return vcek, nil
|
||||
}
|
||||
|
||||
func validateSNPReport(cert *x509.Certificate, expectedIDKeyDigest []byte, enforceIDKeyDigest bool, report snpAttestationReport, log vtpm.WarnLogger) error {
|
||||
func validateSNPReport(
|
||||
cert *x509.Certificate, expectedIDKeyDigest []byte, enforceIDKeyDigest bool,
|
||||
report snpAttestationReport, log vtpm.AttestationLogger,
|
||||
) error {
|
||||
if report.Policy.Debug() {
|
||||
return errDebugEnabled
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ type Validator struct {
|
||||
}
|
||||
|
||||
// NewValidator initializes a new Azure validator with the provided PCR values.
|
||||
func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32, log vtpm.WarnLogger) *Validator {
|
||||
func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32, log vtpm.AttestationLogger) *Validator {
|
||||
rootPool := x509.NewCertPool()
|
||||
rootPool.AddCert(ameRoot)
|
||||
v := &Validator{roots: rootPool}
|
||||
|
@ -34,7 +34,7 @@ type Validator struct {
|
||||
}
|
||||
|
||||
// NewValidator initializes a new GCP validator with the provided PCR values.
|
||||
func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32, log vtpm.WarnLogger) *Validator {
|
||||
func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32, log vtpm.AttestationLogger) *Validator {
|
||||
return &Validator{
|
||||
Validator: vtpm.NewValidator(
|
||||
pcrs,
|
||||
|
@ -21,7 +21,7 @@ type Validator struct {
|
||||
}
|
||||
|
||||
// NewValidator initializes a new qemu validator with the provided PCR values.
|
||||
func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32, log vtpm.WarnLogger) *Validator {
|
||||
func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32, log vtpm.AttestationLogger) *Validator {
|
||||
return &Validator{
|
||||
Validator: vtpm.NewValidator(
|
||||
pcrs,
|
||||
|
@ -57,8 +57,9 @@ type (
|
||||
VerifyUserData func(pub crypto.PublicKey, hash crypto.Hash, hashed, sig []byte) error
|
||||
)
|
||||
|
||||
// WarnLogger is a logger used to print warnings during attestation validation.
|
||||
type WarnLogger interface {
|
||||
// AttestationLogger is a logger used to print warnings and infos during attestation validation.
|
||||
type AttestationLogger interface {
|
||||
Infof(format string, args ...interface{})
|
||||
Warnf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
@ -139,12 +140,12 @@ type Validator struct {
|
||||
validateCVM ValidateCVM
|
||||
verifyUserData VerifyUserData
|
||||
|
||||
log WarnLogger
|
||||
log AttestationLogger
|
||||
}
|
||||
|
||||
// NewValidator returns a new Validator.
|
||||
func NewValidator(expectedPCRs map[uint32][]byte, enforcedPCRs []uint32,
|
||||
getTrustedKey GetTPMTrustedAttestationPublicKey, validateCVM ValidateCVM, verifyUserData VerifyUserData, log WarnLogger,
|
||||
func NewValidator(expectedPCRs map[uint32][]byte, enforcedPCRs []uint32, getTrustedKey GetTPMTrustedAttestationPublicKey,
|
||||
validateCVM ValidateCVM, verifyUserData VerifyUserData, log AttestationLogger,
|
||||
) *Validator {
|
||||
// Convert the enforced PCR list to a map for convenient and fast lookup
|
||||
enforcedMap := make(map[uint32]struct{})
|
||||
@ -164,6 +165,10 @@ func NewValidator(expectedPCRs map[uint32][]byte, enforcedPCRs []uint32,
|
||||
|
||||
// Validate a TPM based attestation.
|
||||
func (v *Validator) Validate(attDocRaw []byte, nonce []byte) ([]byte, error) {
|
||||
if v.log != nil {
|
||||
v.log.Infof("Validating attestation document")
|
||||
}
|
||||
|
||||
var attDoc AttestationDocument
|
||||
if err := json.Unmarshal(attDocRaw, &attDoc); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling TPM attestation document: %w", err)
|
||||
@ -213,6 +218,10 @@ func (v *Validator) Validate(attDocRaw []byte, nonce []byte) ([]byte, error) {
|
||||
if err = v.verifyUserData(aKP, crypto.SHA256, digest[:], attDoc.UserDataSignature); err != nil {
|
||||
return nil, fmt.Errorf("verifying signed user data: %w", err)
|
||||
}
|
||||
|
||||
if v.log != nil {
|
||||
v.log.Infof("Successfully validated attestation document")
|
||||
}
|
||||
return attDoc.UserData, nil
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ func TestValidate(t *testing.T) {
|
||||
0: {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
1: {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
}
|
||||
warnLog := &testWarnLog{}
|
||||
warnLog := &testAttestationLogger{}
|
||||
|
||||
issuer := NewIssuer(newSimTPMWithEventLog, tpmclient.AttestationKeyRSA, fakeGetInstanceInfo)
|
||||
validator := NewValidator(testExpectedPCRs, []uint32{0, 1}, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog)
|
||||
@ -401,10 +401,15 @@ func TestGetSelectedPCRs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type testWarnLog struct {
|
||||
type testAttestationLogger struct {
|
||||
infos []string
|
||||
warnings []string
|
||||
}
|
||||
|
||||
func (w *testWarnLog) Warnf(format string, args ...interface{}) {
|
||||
func (w *testAttestationLogger) Infof(format string, args ...interface{}) {
|
||||
w.infos = append(w.infos, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (w *testAttestationLogger) Warnf(format string, args ...interface{}) {
|
||||
w.warnings = append(w.warnings, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user