From 690b50b29de54cbdf3c6aab5b7b7ed3ee81992df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Wei=C3=9Fe?= <66256922+daniel-weisse@users.noreply.github.com> Date: Thu, 19 Jan 2023 15:57:50 +0100 Subject: [PATCH] dev-docs: Go package docs (#958) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove unused package * Add Go package docs to most packages Signed-off-by: Daniel Weiße Signed-off-by: Fabian Kammel Signed-off-by: Paul Meyer <49727155+katexochen@users.noreply.github.com> Co-authored-by: Paul Meyer <49727155+katexochen@users.noreply.github.com> Co-authored-by: Fabian Kammel --- bootstrapper/README.md | 2 +- bootstrapper/cmd/bootstrapper/main.go | 2 +- .../internal/certificate/certificate.go | 24 ++ bootstrapper/internal/clean/clean.go | 1 + .../internal/diskencryption/diskencryption.go | 6 + .../internal/helm/{client.go => helm.go} | 1 + .../helm/{client_test.go => helm_test.go} | 0 .../internal/initserver/initserver.go | 11 + .../joinclient/{client.go => joinclient.go} | 19 +- .../{client_test.go => joinclient_test.go} | 0 bootstrapper/internal/kubelet/kubelet.go | 35 --- .../internal/kubernetes/k8sapi/k8sapi.go | 8 + .../internal/kubernetes/k8sapi/k8sutil.go | 8 +- .../kubernetes/k8sapi/kubeadm_config.go | 6 +- .../kubernetes/k8sapi/resources/resources.go | 8 + .../internal/kubernetes/kubernetes.go | 3 +- .../internal/kubernetes/kubernetes_test.go | 2 +- .../waiter.go => kubewaiter/kubewaiter.go} | 1 + .../kubewaiter_test.go} | 0 bootstrapper/internal/logging/logger.go | 1 + bootstrapper/internal/nodelock/nodelock.go | 1 + cli/cmd/root.go | 5 + cli/internal/cloudcmd/cloudcmd.go | 22 ++ cli/internal/cmd/cmd.go | 12 + cli/internal/image/image.go | 5 + cli/internal/libvirt/libvirt.go | 1 + cli/internal/terraform/terraform.go | 8 + csi/cryptmapper/cryptmapper.go | 1 + dev-docs/conventions.md | 20 ++ disk-mapper/README.md | 11 +- disk-mapper/internal/mapper/mapper.go | 11 +- .../{server.go => recoveryserver.go} | 9 + ...{server_test.go => recoveryserver_test.go} | 0 .../{client.go => rejoinclient.go} | 5 + .../{client_test.go => rejoinclient_test.go} | 0 disk-mapper/internal/setup/setup.go | 5 + disk-mapper/internal/systemd/systemd.go | 1 + disk-mapper/recoverproto/recover.proto | 2 +- e2e/e2e.go | 8 + e2e/internal/kubectl/kubectl.go | 2 + e2e/internal/lb/lb_test.go | 1 + internal/atls/atls.go | 1 + internal/attestation/attestation.go | 22 ++ internal/attestation/aws/README.md | 1 - internal/attestation/aws/aws.go | 30 +++ .../attestation/azure/{issuer.go => azure.go} | 13 ++ internal/attestation/azure/snp/README.md | 19 -- internal/attestation/azure/snp/snp.go | 43 ++++ .../azure/trustedlaunch/trustedlaunch.go | 22 ++ internal/attestation/gcp/README.md | 75 ------ internal/attestation/gcp/gcp.go | 45 ++++ .../attestation/measurements/measurements.go | 7 + internal/attestation/qemu/qemu.go | 13 ++ internal/attestation/simulator/simulator.go | 1 + internal/attestation/vtpm/vtpm.go | 47 ++++ internal/cloud/aws/{cloud.go => aws.go} | 9 + .../cloud/aws/{cloud_test.go => aws_test.go} | 0 internal/cloud/azure/{cloud.go => azure.go} | 9 + .../azure/{cloud_test.go => azure_test.go} | 0 .../azureshared/{doc.go => azureshared.go} | 0 internal/cloud/cloud.go | 20 ++ internal/cloud/gcp/{cloud.go => gcp.go} | 9 + .../cloud/gcp/{cloud_test.go => gcp_test.go} | 0 .../cloud/gcpshared/{doc.go => gcpshared.go} | 0 internal/cloud/qemu/{cloud.go => qemu.go} | 3 + internal/config/config.go | 8 + internal/config/config_doc.go | 2 +- internal/deploy/helm/helm.go | 3 +- internal/deploy/user/create_user.go | 57 ----- internal/deploy/user/linux_user.go | 215 ------------------ internal/deploy/user/linux_user_test.go | 134 ----------- internal/deploy/user/passwd.go | 68 ------ internal/deploy/user/passwd_test.go | 95 -------- .../grpc/atlscredentials/atlscredentials.go | 1 + internal/grpc/dialer/dialer.go | 1 + internal/grpc/retry/retry.go | 1 + .../{bufconndialer.go => testdialer.go} | 1 + .../installer/{install.go => installer.go} | 1 + .../{install_test.go => installer_test.go} | 0 internal/kms/config/config.go | 1 + .../internal => internal/kms/kms}/README.md | 0 internal/kms/kms/aws/aws.go | 1 + internal/kms/kms/azure/azure.go | 1 + internal/kms/kms/cluster/cluster.go | 9 + internal/kms/kms/gcp/gcp.go | 19 ++ internal/kms/kms/kms.go | 1 + internal/kms/kms/util/crypto.go | 1 + internal/kms/setup/setup.go | 9 + internal/kms/storage/storage.go | 5 + internal/kubernetes/kubectl/kubectl.go | 2 + internal/kubernetes/kubernetes.go | 12 + internal/license/license.go | 1 + internal/logger/log.go | 10 +- internal/nodestate/nodestate.go | 1 + internal/oid/oid.go | 2 + internal/retry/retry.go | 1 + internal/sigstore/sigstore.go | 8 + internal/versions/versions.go | 5 + internal/versionsapi/cli/main.go | 11 + internal/versionsapi/client/client.go | 32 +-- internal/versionsapi/fetcher/fetcher.go | 13 +- internal/versionsapi/versionsapi.go | 20 ++ internal/watcher/watcher.go | 1 + joinservice/README.md | 1 - joinservice/internal/kms/kms.go | 1 + joinservice/internal/kubeadm/kubeadm.go | 1 + joinservice/internal/kubernetes/kubernetes.go | 1 + .../internal/kubernetesca/kubernetesca.go | 1 + joinservice/internal/server/server.go | 1 + joinservice/joinproto/join.proto | 27 +++ keyservice/README.md | 18 ++ keyservice/internal/test/integration_test.go | 1 + upgrade-agent/cmd/main.go | 4 +- upgrade-agent/{ => internal/server}/server.go | 8 +- .../{ => internal/server}/server_test.go | 2 +- verify/README.md | 5 + verify/server/server.go | 1 + verify/verifyproto/verify.proto | 3 + 118 files changed, 735 insertions(+), 750 deletions(-) rename bootstrapper/internal/helm/{client.go => helm.go} (98%) rename bootstrapper/internal/helm/{client_test.go => helm_test.go} (100%) rename bootstrapper/internal/joinclient/{client.go => joinclient.go} (93%) rename bootstrapper/internal/joinclient/{client_test.go => joinclient_test.go} (100%) delete mode 100644 bootstrapper/internal/kubelet/kubelet.go create mode 100644 bootstrapper/internal/kubernetes/k8sapi/k8sapi.go create mode 100644 bootstrapper/internal/kubernetes/k8sapi/resources/resources.go rename bootstrapper/internal/kubernetes/{kubeWaiter/waiter.go => kubewaiter/kubewaiter.go} (94%) rename bootstrapper/internal/kubernetes/{kubeWaiter/waiter_test.go => kubewaiter/kubewaiter_test.go} (100%) create mode 100644 cli/internal/cloudcmd/cloudcmd.go create mode 100644 cli/internal/cmd/cmd.go rename disk-mapper/internal/recoveryserver/{server.go => recoveryserver.go} (92%) rename disk-mapper/internal/recoveryserver/{server_test.go => recoveryserver_test.go} (100%) rename disk-mapper/internal/rejoinclient/{client.go => rejoinclient.go} (96%) rename disk-mapper/internal/rejoinclient/{client_test.go => rejoinclient_test.go} (100%) create mode 100644 e2e/e2e.go delete mode 100644 internal/attestation/aws/README.md create mode 100644 internal/attestation/aws/aws.go rename internal/attestation/azure/{issuer.go => azure.go} (73%) delete mode 100644 internal/attestation/azure/snp/README.md create mode 100644 internal/attestation/azure/snp/snp.go create mode 100644 internal/attestation/azure/trustedlaunch/trustedlaunch.go delete mode 100644 internal/attestation/gcp/README.md create mode 100644 internal/attestation/gcp/gcp.go create mode 100644 internal/attestation/qemu/qemu.go rename internal/cloud/aws/{cloud.go => aws.go} (96%) rename internal/cloud/aws/{cloud_test.go => aws_test.go} (100%) rename internal/cloud/azure/{cloud.go => azure.go} (98%) rename internal/cloud/azure/{cloud_test.go => azure_test.go} (100%) rename internal/cloud/azureshared/{doc.go => azureshared.go} (100%) rename internal/cloud/gcp/{cloud.go => gcp.go} (97%) rename internal/cloud/gcp/{cloud_test.go => gcp_test.go} (100%) rename internal/cloud/gcpshared/{doc.go => gcpshared.go} (100%) rename internal/cloud/qemu/{cloud.go => qemu.go} (97%) delete mode 100644 internal/deploy/user/create_user.go delete mode 100644 internal/deploy/user/linux_user.go delete mode 100644 internal/deploy/user/linux_user_test.go delete mode 100644 internal/deploy/user/passwd.go delete mode 100644 internal/deploy/user/passwd_test.go rename internal/grpc/testdialer/{bufconndialer.go => testdialer.go} (95%) rename internal/installer/{install.go => installer.go} (98%) rename internal/installer/{install_test.go => installer_test.go} (100%) rename {keyservice/internal => internal/kms/kms}/README.md (100%) create mode 100644 internal/kubernetes/kubernetes.go create mode 100644 internal/sigstore/sigstore.go create mode 100644 internal/versionsapi/versionsapi.go create mode 100644 keyservice/README.md rename upgrade-agent/{ => internal/server}/server.go (96%) rename upgrade-agent/{ => internal/server}/server_test.go (99%) create mode 100644 verify/README.md diff --git a/bootstrapper/README.md b/bootstrapper/README.md index 96577526e..a54d7470d 100644 --- a/bootstrapper/README.md +++ b/bootstrapper/README.md @@ -9,7 +9,7 @@ The bootstrapper has two active components: ## Init Flow -The InitServer is a gRPC server that is listining for initialization requests. +The InitServer is a gRPC server that is listening for initialization requests. The first instance needs to be initialized by the user, see the [initproto](./initproto) for a description of the initialization protocol. The client that talks to this server is part of Constellation's CLI. diff --git a/bootstrapper/cmd/bootstrapper/main.go b/bootstrapper/cmd/bootstrapper/main.go index 439736fef..2894579d9 100644 --- a/bootstrapper/cmd/bootstrapper/main.go +++ b/bootstrapper/cmd/bootstrapper/main.go @@ -16,7 +16,7 @@ import ( "github.com/edgelesssys/constellation/v2/bootstrapper/internal/helm" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/k8sapi" - kubewaiter "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/kubeWaiter" + "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/kubewaiter" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/logging" "github.com/edgelesssys/constellation/v2/internal/atls" "github.com/edgelesssys/constellation/v2/internal/attestation/aws" diff --git a/bootstrapper/internal/certificate/certificate.go b/bootstrapper/internal/certificate/certificate.go index b5f90c973..e27836bcd 100644 --- a/bootstrapper/internal/certificate/certificate.go +++ b/bootstrapper/internal/certificate/certificate.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package certificate provides functions to create a certificate request and matching private key. package certificate import ( @@ -11,9 +12,32 @@ import ( "crypto/elliptic" "crypto/rand" "crypto/x509" + "crypto/x509/pkix" "encoding/pem" + "net" + + "k8s.io/kubernetes/cmd/kubeadm/app/constants" ) +const ( + // CertificateFilename is the path to the kubelets certificate. + CertificateFilename = "/run/state/kubelet/pki/kubelet-client-crt.pem" + // KeyFilename is the path to the kubelets private key. + KeyFilename = "/run/state/kubelet/pki/kubelet-client-key.pem" +) + +// GetKubeletCertificateRequest returns a certificate request and matching private key for the kubelet. +func GetKubeletCertificateRequest(nodeName string, ips []net.IP) (certificateRequest []byte, privateKey []byte, err error) { + csrTemplate := &x509.CertificateRequest{ + Subject: pkix.Name{ + Organization: []string{constants.NodesGroup}, + CommonName: constants.NodesUserPrefix + nodeName, + }, + IPAddresses: ips, + } + return GetCertificateRequest(csrTemplate) +} + // GetCertificateRequest returns a certificate request and matching private key. func GetCertificateRequest(csrTemplate *x509.CertificateRequest) (certificateRequest []byte, privateKey []byte, err error) { privK, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) diff --git a/bootstrapper/internal/clean/clean.go b/bootstrapper/internal/clean/clean.go index a82dbca4d..71d46b4ec 100644 --- a/bootstrapper/internal/clean/clean.go +++ b/bootstrapper/internal/clean/clean.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package clean provides functionality to stop a list of services gracefully and synchronously. package clean import ( diff --git a/bootstrapper/internal/diskencryption/diskencryption.go b/bootstrapper/internal/diskencryption/diskencryption.go index 7d56c81a0..277f6740b 100644 --- a/bootstrapper/internal/diskencryption/diskencryption.go +++ b/bootstrapper/internal/diskencryption/diskencryption.go @@ -4,6 +4,12 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package diskencryption handles interaction with a node's state disk. + +This package is not thread safe, since libcryptsetup is not thread safe. +There should only be one instance using this package per process. +*/ package diskencryption import ( diff --git a/bootstrapper/internal/helm/client.go b/bootstrapper/internal/helm/helm.go similarity index 98% rename from bootstrapper/internal/helm/client.go rename to bootstrapper/internal/helm/helm.go index c5dc0652b..d0000870b 100644 --- a/bootstrapper/internal/helm/client.go +++ b/bootstrapper/internal/helm/helm.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package helm is used to install Constellation microservices and other services during cluster initialization. package helm import ( diff --git a/bootstrapper/internal/helm/client_test.go b/bootstrapper/internal/helm/helm_test.go similarity index 100% rename from bootstrapper/internal/helm/client_test.go rename to bootstrapper/internal/helm/helm_test.go diff --git a/bootstrapper/internal/initserver/initserver.go b/bootstrapper/internal/initserver/initserver.go index 8c9a78ac6..ecbc6b786 100644 --- a/bootstrapper/internal/initserver/initserver.go +++ b/bootstrapper/internal/initserver/initserver.go @@ -4,6 +4,17 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +# InitServer + +The InitServer is one of the two main components of the bootstrapper. +It is responsible for the initial setup of a node, and the initialization of the Kubernetes cluster. + +The InitServer is started on each node, and waits for either a call from the CLI, +or for the JoinClient to connect to an existing cluster. + +If a call from the CLI is received, the InitServer bootstraps the Kubernetes cluster, and stops the JoinClient. +*/ package initserver import ( diff --git a/bootstrapper/internal/joinclient/client.go b/bootstrapper/internal/joinclient/joinclient.go similarity index 93% rename from bootstrapper/internal/joinclient/client.go rename to bootstrapper/internal/joinclient/joinclient.go index 8c96dfaa2..21e46dba1 100644 --- a/bootstrapper/internal/joinclient/client.go +++ b/bootstrapper/internal/joinclient/joinclient.go @@ -4,6 +4,17 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +# JoinClient + +The JoinClient is one of the two main components of the bootstrapper. +It is responsible for for the initial setup of a node, and joining an existing Kubernetes cluster. + +The JoinClient is started on each node, it then continuously checks for an existing cluster to join, +or for the InitServer to bootstrap a new cluster. + +If the JoinClient finds an existing cluster, it will attempt to join it as either a control-plane or a worker node. +*/ package joinclient import ( @@ -16,8 +27,8 @@ import ( "sync" "time" + "github.com/edgelesssys/constellation/v2/bootstrapper/internal/certificate" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/diskencryption" - "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubelet" "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/cloud/metadata" "github.com/edgelesssys/constellation/v2/internal/constants" @@ -201,7 +212,7 @@ func (c *JoinClient) join(serviceEndpoint string) error { ctx, cancel := c.timeoutCtx() defer cancel() - certificateRequest, kubeletKey, err := kubelet.GetCertificateRequest(c.nodeName, c.validIPs) + certificateRequest, kubeletKey, err := certificate.GetKubeletCertificateRequest(c.nodeName, c.validIPs) if err != nil { return err } @@ -267,10 +278,10 @@ func (c *JoinClient) startNodeAndJoin(ticket *joinproto.IssueJoinTicketResponse, return fmt.Errorf("writing control plane files: %w", err) } } - if err := c.fileHandler.Write(kubelet.CertificateFilename, ticket.KubeletCert, file.OptMkdirAll); err != nil { + if err := c.fileHandler.Write(certificate.CertificateFilename, ticket.KubeletCert, file.OptMkdirAll); err != nil { return fmt.Errorf("writing kubelet certificate: %w", err) } - if err := c.fileHandler.Write(kubelet.KeyFilename, kubeletKey, file.OptMkdirAll); err != nil { + if err := c.fileHandler.Write(certificate.KeyFilename, kubeletKey, file.OptMkdirAll); err != nil { return fmt.Errorf("writing kubelet key: %w", err) } diff --git a/bootstrapper/internal/joinclient/client_test.go b/bootstrapper/internal/joinclient/joinclient_test.go similarity index 100% rename from bootstrapper/internal/joinclient/client_test.go rename to bootstrapper/internal/joinclient/joinclient_test.go diff --git a/bootstrapper/internal/kubelet/kubelet.go b/bootstrapper/internal/kubelet/kubelet.go deleted file mode 100644 index 8c8209b88..000000000 --- a/bootstrapper/internal/kubelet/kubelet.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright (c) Edgeless Systems GmbH - -SPDX-License-Identifier: AGPL-3.0-only -*/ - -package kubelet - -import ( - "crypto/x509" - "crypto/x509/pkix" - "net" - - "github.com/edgelesssys/constellation/v2/bootstrapper/internal/certificate" - "k8s.io/kubernetes/cmd/kubeadm/app/constants" -) - -const ( - // CertificateFilename is the path to the kubelets certificate. - CertificateFilename = "/run/state/kubelet/pki/kubelet-client-crt.pem" - // KeyFilename is the path to the kubelets private key. - KeyFilename = "/run/state/kubelet/pki/kubelet-client-key.pem" -) - -// GetCertificateRequest returns a certificate request and macthing private key for the kubelet. -func GetCertificateRequest(nodeName string, ips []net.IP) (certificateRequest []byte, privateKey []byte, err error) { - csrTemplate := &x509.CertificateRequest{ - Subject: pkix.Name{ - Organization: []string{constants.NodesGroup}, - CommonName: constants.NodesUserPrefix + nodeName, - }, - IPAddresses: ips, - } - return certificate.GetCertificateRequest(csrTemplate) -} diff --git a/bootstrapper/internal/kubernetes/k8sapi/k8sapi.go b/bootstrapper/internal/kubernetes/k8sapi/k8sapi.go new file mode 100644 index 000000000..72565b30a --- /dev/null +++ b/bootstrapper/internal/kubernetes/k8sapi/k8sapi.go @@ -0,0 +1,8 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +// Package k8sapi is used to interact with the Kubernetes API to create or update required resources. +package k8sapi diff --git a/bootstrapper/internal/kubernetes/k8sapi/k8sutil.go b/bootstrapper/internal/kubernetes/k8sapi/k8sutil.go index 916dc7366..48b305c0c 100644 --- a/bootstrapper/internal/kubernetes/k8sapi/k8sutil.go +++ b/bootstrapper/internal/kubernetes/k8sapi/k8sutil.go @@ -22,7 +22,7 @@ import ( "strings" "time" - "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubelet" + "github.com/edgelesssys/constellation/v2/bootstrapper/internal/certificate" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/k8sapi/resources" "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/role" @@ -345,11 +345,11 @@ func (k *KubernetesUtil) StartKubelet() error { // This is necessary because this node does not request a certificate from the join service. func (k *KubernetesUtil) createSignedKubeletCert(nodeName string, ips []net.IP) error { // Create CSR - certRequestRaw, kubeletKey, err := kubelet.GetCertificateRequest(nodeName, ips) + certRequestRaw, kubeletKey, err := certificate.GetKubeletCertificateRequest(nodeName, ips) if err != nil { return err } - if err := k.file.Write(kubelet.KeyFilename, kubeletKey, file.OptMkdirAll); err != nil { + if err := k.file.Write(certificate.KeyFilename, kubeletKey, file.OptMkdirAll); err != nil { return err } @@ -399,7 +399,7 @@ func (k *KubernetesUtil) createSignedKubeletCert(nodeName string, ips []net.IP) Bytes: certRaw, }) - return k.file.Write(kubelet.CertificateFilename, kubeletCert, file.OptMkdirAll) + return k.file.Write(certificate.CertificateFilename, kubeletCert, file.OptMkdirAll) } // createSignedKonnectivityCert manually creates a Kubernetes CA signed certificate for the Konnectivity server. diff --git a/bootstrapper/internal/kubernetes/k8sapi/kubeadm_config.go b/bootstrapper/internal/kubernetes/k8sapi/kubeadm_config.go index 033a1931d..ade3582af 100644 --- a/bootstrapper/internal/kubernetes/k8sapi/kubeadm_config.go +++ b/bootstrapper/internal/kubernetes/k8sapi/kubeadm_config.go @@ -9,7 +9,7 @@ package k8sapi import ( "path/filepath" - "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubelet" + "github.com/edgelesssys/constellation/v2/bootstrapper/internal/certificate" "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/kubernetes" corev1 "k8s.io/api/core/v1" @@ -166,8 +166,8 @@ func (c *KubdeadmConfiguration) InitConfiguration(externalCloudProvider bool, cl Effect: corev1.TaintEffectPreferNoSchedule, }, }, - TLSCertFile: kubelet.CertificateFilename, - TLSPrivateKeyFile: kubelet.KeyFilename, + TLSCertFile: certificate.CertificateFilename, + TLSPrivateKeyFile: certificate.KeyFilename, }, } } diff --git a/bootstrapper/internal/kubernetes/k8sapi/resources/resources.go b/bootstrapper/internal/kubernetes/k8sapi/resources/resources.go new file mode 100644 index 000000000..d7dc58c49 --- /dev/null +++ b/bootstrapper/internal/kubernetes/k8sapi/resources/resources.go @@ -0,0 +1,8 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +// Package resources contains Kubernetes configs and policies for Constellation. +package resources diff --git a/bootstrapper/internal/kubernetes/kubernetes.go b/bootstrapper/internal/kubernetes/kubernetes.go index 850c4cf90..8112d15cf 100644 --- a/bootstrapper/internal/kubernetes/kubernetes.go +++ b/bootstrapper/internal/kubernetes/kubernetes.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package kubernetes provides functionality to bootstrap a Kubernetes cluster, or join an exiting one. package kubernetes import ( @@ -19,7 +20,7 @@ import ( "time" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/k8sapi" - kubewaiter "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/kubeWaiter" + "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/kubewaiter" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/cloud/azureshared" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" diff --git a/bootstrapper/internal/kubernetes/kubernetes_test.go b/bootstrapper/internal/kubernetes/kubernetes_test.go index 66595c259..10e6243c4 100644 --- a/bootstrapper/internal/kubernetes/kubernetes_test.go +++ b/bootstrapper/internal/kubernetes/kubernetes_test.go @@ -14,7 +14,7 @@ import ( "testing" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/k8sapi" - kubewaiter "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/kubeWaiter" + "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/kubewaiter" "github.com/edgelesssys/constellation/v2/internal/cloud/metadata" "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/deploy/helm" diff --git a/bootstrapper/internal/kubernetes/kubeWaiter/waiter.go b/bootstrapper/internal/kubernetes/kubewaiter/kubewaiter.go similarity index 94% rename from bootstrapper/internal/kubernetes/kubeWaiter/waiter.go rename to bootstrapper/internal/kubernetes/kubewaiter/kubewaiter.go index 5237fbe60..8229b1e4a 100644 --- a/bootstrapper/internal/kubernetes/kubeWaiter/waiter.go +++ b/bootstrapper/internal/kubernetes/kubewaiter/kubewaiter.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package kubewaiter is used to wait for the Kubernetes API to be available. package kubewaiter import ( diff --git a/bootstrapper/internal/kubernetes/kubeWaiter/waiter_test.go b/bootstrapper/internal/kubernetes/kubewaiter/kubewaiter_test.go similarity index 100% rename from bootstrapper/internal/kubernetes/kubeWaiter/waiter_test.go rename to bootstrapper/internal/kubernetes/kubewaiter/kubewaiter_test.go diff --git a/bootstrapper/internal/logging/logger.go b/bootstrapper/internal/logging/logger.go index 626976918..b91da8965 100644 --- a/bootstrapper/internal/logging/logger.go +++ b/bootstrapper/internal/logging/logger.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package logging provides an interface for logging information to a non-confidential destination package logging import "io" diff --git a/bootstrapper/internal/nodelock/nodelock.go b/bootstrapper/internal/nodelock/nodelock.go index 2d5f1b0ae..cf4805d6d 100644 --- a/bootstrapper/internal/nodelock/nodelock.go +++ b/bootstrapper/internal/nodelock/nodelock.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package nodelock handles locking operations on the node. package nodelock import ( diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 76914240d..37c419497 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -4,6 +4,11 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package cmd is the entrypoint of the Constellation CLI. + +Business logic of the CLI shall be implemented in the internal/cmd package. +*/ package cmd import ( diff --git a/cli/internal/cloudcmd/cloudcmd.go b/cli/internal/cloudcmd/cloudcmd.go new file mode 100644 index 000000000..221482f47 --- /dev/null +++ b/cli/internal/cloudcmd/cloudcmd.go @@ -0,0 +1,22 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +/* +Package cloudcmd provides executable command for the CLI. + +This package focuses on the interaction with the cloud provider. +It separates the cloud provider specific code from the rest of the CLI, and +provides a common interface for all cloud providers. + +Exported functions must not be cloud provider specific, but rather take a +cloudprovider.Provider as an argument. + +User interaction happens in the cmd package, and should not happen or pass through +this package. + +The backend to this package is currently provided by the terraform package. +*/ +package cloudcmd diff --git a/cli/internal/cmd/cmd.go b/cli/internal/cmd/cmd.go new file mode 100644 index 000000000..d483c04b5 --- /dev/null +++ b/cli/internal/cmd/cmd.go @@ -0,0 +1,12 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +/* +Package cmd provides the Constellation CLI. + +It is responsible for the interaction with the user. +*/ +package cmd diff --git a/cli/internal/image/image.go b/cli/internal/image/image.go index 44715836f..a0d624865 100644 --- a/cli/internal/image/image.go +++ b/cli/internal/image/image.go @@ -4,6 +4,11 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package image provides helping wrappers around a versionsapi fetcher. + +It also enables local image overrides and download of raw images. +*/ package image import ( diff --git a/cli/internal/libvirt/libvirt.go b/cli/internal/libvirt/libvirt.go index 0ab4f545b..ec41069a5 100644 --- a/cli/internal/libvirt/libvirt.go +++ b/cli/internal/libvirt/libvirt.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package libvirt is used to start and stop containerized libvirt instances. package libvirt import ( diff --git a/cli/internal/terraform/terraform.go b/cli/internal/terraform/terraform.go index b862467ae..d523986e8 100644 --- a/cli/internal/terraform/terraform.go +++ b/cli/internal/terraform/terraform.go @@ -4,6 +4,14 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package terraform handles creation/destruction of a Constellation cluster using Terraform. + +Since Terraform does not provide a stable Go API, we use the `terraform-exec` package to interact with Terraform. + +The Terraform templates are located in the "terraform" subdirectory. The templates are embedded into the CLI binary using `go:embed`. +On use the relevant template is extracted to the working directory and the user customized variables are written to a `terraform.tfvars` file. +*/ package terraform import ( diff --git a/csi/cryptmapper/cryptmapper.go b/csi/cryptmapper/cryptmapper.go index c7beed058..9c90c692b 100644 --- a/csi/cryptmapper/cryptmapper.go +++ b/csi/cryptmapper/cryptmapper.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package cryptmapper provides a wrapper around libcryptsetup to manage dm-crypt volumes for CSI drivers. package cryptmapper import ( diff --git a/dev-docs/conventions.md b/dev-docs/conventions.md index e17e44576..f47ac5071 100644 --- a/dev-docs/conventions.md +++ b/dev-docs/conventions.md @@ -141,6 +141,26 @@ the `/.vscode/settings.json` repo, so the settings will only affect Additionally, we use the [Redhat YAML formatter](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) to have uniform formatting in our `.yaml` files. +## Code documentation + +Documentation of the latest release are available on [pkg.go.dev](https://pkg.go.dev/github.com/edgelesssys/constellation/v2). + +Alternatively use `pkgsite` to host your own documentation server and view documentation for the local version of your code. + +
+View installation instructions + +```shell +CONSTELLATION_DIR= +git clone https://github.com/golang/pkgsite && cd pkgsite +go install ./cmd/pkgsite +cd "${CONSTELLATION_DIR} +pkgsite +``` + +You can now view the documentation on http://localhost:8080/github.com/edgelesssys/constellation/v2 +
+ ## PR conventions Our changelog is generated from PR titles. Which PR is listed in which category is determined by labels, see the [release.yml](/.github/release.yml). diff --git a/disk-mapper/README.md b/disk-mapper/README.md index f94c91e50..9d537d5a1 100644 --- a/disk-mapper/README.md +++ b/disk-mapper/README.md @@ -1,6 +1,11 @@ -# State +# disk-mapper -Files and source code for mounting persistent state disks +The disk-mapper is a binary that runs during the initramfs of a Constellation node. + +If running on a new node, it handles setting up the node's state disk by creating an integrity protected encrypted partition. + +On a rebooting node, the disk-mapper handles recovery of the node by requesting a decryption key for its state disk. +Once the disk is decrypted, the measurement salt is read from disk and used to extend a PCR to mark the node as initialized. ## Testing @@ -9,6 +14,6 @@ The integration test requires root privileges since it uses dm-crypt. Build and run the test: ```bash -go test -c -tags=integration ./disk-mapper/test/ +go test -c -tags=integration ./disk-mapper/internal/test/ sudo ./test.test ``` diff --git a/disk-mapper/internal/mapper/mapper.go b/disk-mapper/internal/mapper/mapper.go index 96225f34a..4455e296c 100644 --- a/disk-mapper/internal/mapper/mapper.go +++ b/disk-mapper/internal/mapper/mapper.go @@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package mapper uses libcryptsetup to format and map crypt devices. + +This is used by the disk-mapper to set up a node's state disk. + +All interaction with libcryptsetup should be done here. + +Warning: This package is not thread safe, since libcryptsetup is not thread safe. +*/ package mapper import ( @@ -24,7 +33,7 @@ import ( // https://stackoverflow.com/questions/30553386/cryptsetup-backend-safe-with-multithreading var packageLock = sync.Mutex{} -// Mapper handles actions for formating and mapping crypt devices. +// Mapper handles actions for formatting and mapping crypt devices. type Mapper struct { device cryptDevice log *logger.Logger diff --git a/disk-mapper/internal/recoveryserver/server.go b/disk-mapper/internal/recoveryserver/recoveryserver.go similarity index 92% rename from disk-mapper/internal/recoveryserver/server.go rename to disk-mapper/internal/recoveryserver/recoveryserver.go index b5d87e59f..d4db3b787 100644 --- a/disk-mapper/internal/recoveryserver/server.go +++ b/disk-mapper/internal/recoveryserver/recoveryserver.go @@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package recoveryserver implements the gRPC endpoints for recovering a restarting node. + +The endpoint is only available for control-plane nodes, +worker nodes should only rejoin the cluster using Constellation's JoinService. + +This endpoint can be used by an admin in case of a complete cluster shutdown, +in which case a node is unable to rejoin the cluster automatically. +*/ package recoveryserver import ( diff --git a/disk-mapper/internal/recoveryserver/server_test.go b/disk-mapper/internal/recoveryserver/recoveryserver_test.go similarity index 100% rename from disk-mapper/internal/recoveryserver/server_test.go rename to disk-mapper/internal/recoveryserver/recoveryserver_test.go diff --git a/disk-mapper/internal/rejoinclient/client.go b/disk-mapper/internal/rejoinclient/rejoinclient.go similarity index 96% rename from disk-mapper/internal/rejoinclient/client.go rename to disk-mapper/internal/rejoinclient/rejoinclient.go index 4ff2678cc..d1824c293 100644 --- a/disk-mapper/internal/rejoinclient/client.go +++ b/disk-mapper/internal/rejoinclient/rejoinclient.go @@ -4,6 +4,11 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package rejoinclient handles the automatic rejoining of a restarting node. + +It does so by continuously sending rejoin requests to the JoinService of available control-plane endpoints. +*/ package rejoinclient import ( diff --git a/disk-mapper/internal/rejoinclient/client_test.go b/disk-mapper/internal/rejoinclient/rejoinclient_test.go similarity index 100% rename from disk-mapper/internal/rejoinclient/client_test.go rename to disk-mapper/internal/rejoinclient/rejoinclient_test.go diff --git a/disk-mapper/internal/setup/setup.go b/disk-mapper/internal/setup/setup.go index a48ce75ca..d2acff14c 100644 --- a/disk-mapper/internal/setup/setup.go +++ b/disk-mapper/internal/setup/setup.go @@ -4,6 +4,11 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package setup handles setting up rejoinclient and recoveryserver for the disk-mapper. + +On success of either of these services, the state disk is decrypted and the node is tainted as initialized by updating it's PCRs. +*/ package setup import ( diff --git a/disk-mapper/internal/systemd/systemd.go b/disk-mapper/internal/systemd/systemd.go index 093a5cc6d..843f26c7b 100644 --- a/disk-mapper/internal/systemd/systemd.go +++ b/disk-mapper/internal/systemd/systemd.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package systemd configures systemd units for encrypted volumes. package systemd import ( diff --git a/disk-mapper/recoverproto/recover.proto b/disk-mapper/recoverproto/recover.proto index e5be224f0..b84dbc4e5 100644 --- a/disk-mapper/recoverproto/recover.proto +++ b/disk-mapper/recoverproto/recover.proto @@ -5,7 +5,7 @@ package recoverproto; option go_package = "github.com/edgelesssys/constellation/v2/disk-mapper/recoverproto"; service API { - // Recover sends the necessary information to the recoveryserver to start recovering the cluster. + // Recover sends the necessary information to the recoveryserver to initiate recovery of a node. rpc Recover(RecoverMessage) returns (RecoverResponse) {} } diff --git a/e2e/e2e.go b/e2e/e2e.go new file mode 100644 index 000000000..43a0044e7 --- /dev/null +++ b/e2e/e2e.go @@ -0,0 +1,8 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +// End-to-end tests which are executed from our GitHub action pipelines. +package e2e diff --git a/e2e/internal/kubectl/kubectl.go b/e2e/internal/kubectl/kubectl.go index 1f3e78f6b..2fb191b30 100644 --- a/e2e/internal/kubectl/kubectl.go +++ b/e2e/internal/kubectl/kubectl.go @@ -4,6 +4,8 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Provides functionality to easily interact with the K8s API, which can be used +// from any e2e test. package kubectl import ( diff --git a/e2e/internal/lb/lb_test.go b/e2e/internal/lb/lb_test.go index f78c6ccf0..27c7f42f5 100644 --- a/e2e/internal/lb/lb_test.go +++ b/e2e/internal/lb/lb_test.go @@ -6,6 +6,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// End-to-end tests for our cloud load balancer functionality. package test import ( diff --git a/internal/atls/atls.go b/internal/atls/atls.go index 999598ce0..a1a5d589d 100644 --- a/internal/atls/atls.go +++ b/internal/atls/atls.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// aTLS provides config generation functions to bootstrap attested TLS connections. package atls import ( diff --git a/internal/attestation/attestation.go b/internal/attestation/attestation.go index 51bdddebb..52475623d 100644 --- a/internal/attestation/attestation.go +++ b/internal/attestation/attestation.go @@ -4,6 +4,28 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +This package deals with the low level attestation and verification logic of Constellation nodes. + +General tpm attestation code that is not subjective to a single platform should go into the vtpm package. +Since attestation capabilities can differ between platforms, the attestation code should go into a subpackage for that respective platform. + +We commonly implement the following two interfaces for a platform: + + // Issuer issues an attestation document. + type Issuer interface { + oid.Getter + Issue(userData []byte, nonce []byte) (quote []byte, err error) + } + + // Validator is able to validate an attestation document. + type Validator interface { + oid.Getter + Validate(attDoc []byte, nonce []byte) ([]byte, error) + } + +Attestation code for new platforms needs to implement these two interfaces. +*/ package attestation import ( diff --git a/internal/attestation/aws/README.md b/internal/attestation/aws/README.md deleted file mode 100644 index 66e28af33..000000000 --- a/internal/attestation/aws/README.md +++ /dev/null @@ -1 +0,0 @@ -# Amazon Web Services attestation diff --git a/internal/attestation/aws/aws.go b/internal/attestation/aws/aws.go new file mode 100644 index 000000000..de73ed99c --- /dev/null +++ b/internal/attestation/aws/aws.go @@ -0,0 +1,30 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +/* +# Amazon Web Services attestation + +Attestation for AWS using [NitroTPM]. + +AWS currently does not support confidential VMs, but offers a TPM 2.0 compliant vTPM integration. +We use this to enable a TPM based measured boot Constellation deployment. + +# Issuer + +The TPM attestation is signed by the NitroTPM's RSA attestation key. +Additionally to the TPM attestation, we attach a node's [instance identity document] to the attestation document. + +# Validator + +Currently, the NitroTPM provides no endorsement certificate for its attestation key, nor does AWS offer a secondary of of verifying it. +For now we have to blindly trust the key. + +Additionally to verifying the TPM attestation, we also check the instance identity document for consistency. + +[NitroTPM]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nitrotpm.html +[instance identity document]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html +*/ +package aws diff --git a/internal/attestation/azure/issuer.go b/internal/attestation/azure/azure.go similarity index 73% rename from internal/attestation/azure/issuer.go rename to internal/attestation/azure/azure.go index 0912b9854..21cf5981c 100644 --- a/internal/attestation/azure/issuer.go +++ b/internal/attestation/azure/azure.go @@ -4,6 +4,19 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +# Azure attestation + +Constellation supports multiple attestation technologies on Azure. + + - SEV - Secure Nested Paging (SEV-SNP) + + TPM attestation verified using an SEV-SNP attestation statement. + + - Trusted Launch + + Basic TPM attestation. +*/ package azure import ( diff --git a/internal/attestation/azure/snp/README.md b/internal/attestation/azure/snp/README.md deleted file mode 100644 index 61479e52b..000000000 --- a/internal/attestation/azure/snp/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# SNP - -## Glosssary - -This section explains abbreviations used in SNP implementation. - -### Attestation Key (AK) - -### AMD Root Key (ARK) - -### AMD Signing Key (ASK) - -### Versioned Chip Endorsement Key (VCEK) - -For more information see [SNP WhitePaper](https://www.amd.com/system/files/TechDocs/SEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf) - -### Host (Hardware?) Compatibility Layer (HCL) - -No public information. Azure compute API has a field `isHostCompatibilityLayerVm`, with only a [single sentence of documentation](https://learn.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=windows). diff --git a/internal/attestation/azure/snp/snp.go b/internal/attestation/azure/snp/snp.go new file mode 100644 index 000000000..8c109d25a --- /dev/null +++ b/internal/attestation/azure/snp/snp.go @@ -0,0 +1,43 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +/* +# SNP + +Attestation based on TPM and SEV-SNP attestation. +The TPM is used to generate runtime measurements and signed by an attestation key that can be verified using the SEV-SNP attestation report. + +# Issuer + +Generates a TPM attestation using an attestation key saved in the TPM. +Additionally loads the SEV-SNP attestation report and AMD VCEK certificate chain, and adds them to the attestation document. + +# Validator + +Verifies the attestation key used by first verifying the VCEK certificate chain and the SNP attestation report. + +# Glossary + +This section explains abbreviations used in SNP implementation. + + - Attestation Key (AK) + + - AMD Root Key (ARK) + + - AMD Signing Key (ASK) + + - Versioned Chip Endorsement Key (VCEK) + + For more information see [SNP WhitePaper] + + - Host (Hardware?) Compatibility Layer (HCL) + + No public information. Azure compute API has a field `isHostCompatibilityLayerVm`, with only a [single sentence of documentation]. + +[SNP WhitePaper]: https://www.amd.com/system/files/TechDocs/SEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf +[single sentence of documentation]: https://learn.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=windows +*/ +package snp diff --git a/internal/attestation/azure/trustedlaunch/trustedlaunch.go b/internal/attestation/azure/trustedlaunch/trustedlaunch.go new file mode 100644 index 000000000..8959bc32f --- /dev/null +++ b/internal/attestation/azure/trustedlaunch/trustedlaunch.go @@ -0,0 +1,22 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +/* +# Trusted Launch + +Use Azure's trusted launch vTPM to enable a TPM based measure boot Constellation. + +# Issuer + +Generates a TPM attestation using an attestation key saved in the TPM. +Additionally an endorsement certificate of the key, and corresponding CA certificate chain are added to the attestation document. + +# Validator + +Verifies the TPM attestation statement using the public key of the endorsement certificate. +The certificate is verified by first verifying its CA certificate chain. +*/ +package trustedlaunch diff --git a/internal/attestation/gcp/README.md b/internal/attestation/gcp/README.md deleted file mode 100644 index 0e05ac222..000000000 --- a/internal/attestation/gcp/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# Google Cloud Platform attestation - -Google offers [confidential VMs](https://cloud.google.com/compute/confidential-vm/docs/about-cvm), utilizing AMD SEV-ES to provide memory encryption. - -AMD SEV-ES doesn't offer much in terms of remote attestation, and following that the VMs don't offer much either, see [their docs](https://cloud.google.com/compute/confidential-vm/docs/monitoring) on how to validate a confidential VM for some insights. -However, each VM comes with a [virtual Trusted Platform Module (vTPM)](https://cloud.google.com/security/shielded-cloud/shielded-vm#vtpm). -This module can be used to generate VM unique encryption keys or to attest the platform's chain of boot. We can use the vTPM to verify the VM is running on AMD SEV-ES enabled hardware, allowing us to bootstrap a constellation cluster. - -## vTPM components - -For attestation we make use of multiple vTPM features: - -* Endorsement Key - - Asymemetric key used to establish trust in other keys issued by the TPM or used directly for attestation. The private part never leaves the TPM, while the public part, referred to as Endorsement Public Key (EPK), is available to remote parties. The TPM can issue new keys, signed by its endorsement key, which can then be verified by a remote party using the EPK. - -* Endorsement Publc Key Certificate (EPKC) - - A Certificate signed by the TPM manufacturer verifying the authenticity of the EPK. The public key of the Certificate is the EPK. - -* Event Log - - A log of events over the boot process. - -* [Platform Control Register (PCR)](https://link.springer.com/chapter/10.1007/978-1-4302-6584-9_12) - - Registers holding measurements of software and configuration data. PCR values are not directly written, but updated: a new value is the digest of the old value concatenated with the to be added data. - Contents of the PCRs can be signed for attestation. Providing proof to a remote party about software running on the system. - -## Attestation flow - -1. The VM boots and writes its measured software state to the PCRs. - -2. The PCRs are hashed and signed by the EPK. - -3. An attestation statement is created, containing the EPK, the original PCR values, the hashed PCRs, the signature, and the event log. - -4. A remote party establishes trust in the TPMs EPK by verifying its EPKC with the TPM manufactures CA certificate chain. - - Google's vTPMs have no EPKC, instead we querie the GCE API to retrieve a VMs public signing key. This is the public part of the endorsment key used to sign the attestation document. - The downside to this is the verifying party requiring permissions to access the GCE API. - -5. The remote party verifies the signature was created by the TPM, and the hash matches the PCRs. - -6. The remote party reads the event log and verifies measuring the event log results in the given PCR values - -7. The software state is now verified, the only thing left to do is to decide if the state is good or not. This is done by comparing the given PCR values to a set of expected PCR values. - - -## Problems - -* SEV-ES is somewhat limited when compared to the newer version SEV-SNP - - Comparison of SEV, SEV-ES, and SEV-SNP can be seen on page seven of [AMD's SNP whitepaper](https://www.amd.com/system/files/TechDocs/SEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf#page=7) - -* We have to trust Google - - Since the vTPM is provided by Google, and they could do whatever they want with it, we have no save proof of the VMs actually being confidential. - -* The provided vTPM has no EPKC - - Without a certificate signing the authenticity of any endorsement keys we have no way of establishing a chain of trust. - - -## Code testing - -Running tests for GCP attestation requires root access on a confidential VM. -The VM needs to have read access for the Compute Engine API. This is not an IAM role, but has to be set on the VM itself. - -Build and run the tests: - -```shell -go test -c -tags gcp -sudo ./gcp.test -``` \ No newline at end of file diff --git a/internal/attestation/gcp/gcp.go b/internal/attestation/gcp/gcp.go new file mode 100644 index 000000000..893b002a6 --- /dev/null +++ b/internal/attestation/gcp/gcp.go @@ -0,0 +1,45 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +/* +# Google Cloud Platform attestation + +Google offers [confidential VMs], utilizing AMD SEV-ES to provide memory encryption. + +AMD SEV-ES doesn't offer much in terms of remote attestation, and following that the VMs don't offer much either, see [their docs] on how to validate a confidential VM for some insights. +However, each VM comes with a [virtual Trusted Platform Module (vTPM)]. +This module can be used to generate VM unique encryption keys or to attest the platform's chain of boot. We can use the vTPM to verify the VM is running on AMD SEV-ES enabled hardware, allowing us to bootstrap a constellation cluster. + +# Issuer + +Generates a TPM attestation key using a Google provided attestation key. +Additionally project ID, zone, and instance name are fetched from the metadata server and attached to the attestation document. + +# Validator + +Verifies the TPM attestation by using a public key provided by Google's API corresponding to the project ID, zone, instance name tuple attached to the attestation document. + +# Problems + + - SEV-ES is somewhat limited when compared to the newer version SEV-SNP + + Comparison of SEV, SEV-ES, and SEV-SNP can be seen on page seven of [AMD's SNP whitepaper] + + - We have to trust Google + + Since the vTPM is provided by Google, and they could do whatever they want with it, we have no save proof of the VMs actually being confidential. + + - The provided vTPM has no endorsement certificate for its attestation key + + Without a certificate signing the authenticity of any endorsement keys we have no way of establishing a chain of trust. + Instead, we have to rely on Google's API to provide us with the public key of the vTPM's endorsement key. + +[confidential VMs]: https://cloud.google.com/compute/confidential-vm/docs/about-cvm +[their docs]: https://cloud.google.com/compute/confidential-vm/docs/monitoring +[virtual Trusted Platform Module (vTPM)]: https://cloud.google.com/security/shielded-cloud/shielded-vm#vtpm +[AMD's SNP whitepaper]: https://www.amd.com/system/files/TechDocs/SEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf#page=7 +*/ +package gcp diff --git a/internal/attestation/measurements/measurements.go b/internal/attestation/measurements/measurements.go index 6fbcbf4d6..34c498535 100644 --- a/internal/attestation/measurements/measurements.go +++ b/internal/attestation/measurements/measurements.go @@ -4,6 +4,13 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +# Measurements + +Defines default expected measurements for the current release, as well as functions for comparing, updating and marshalling measurements. + +This package should not include TPM specific code. +*/ package measurements import ( diff --git a/internal/attestation/qemu/qemu.go b/internal/attestation/qemu/qemu.go new file mode 100644 index 000000000..424215a6e --- /dev/null +++ b/internal/attestation/qemu/qemu.go @@ -0,0 +1,13 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +/* +# QEMU attestation + +Basic TPM attestation for QEMU platforms. +This is used for miniConstellation, or QEMU testing clusters. +*/ +package qemu diff --git a/internal/attestation/simulator/simulator.go b/internal/attestation/simulator/simulator.go index 9d5ed248e..a2cdc88cc 100644 --- a/internal/attestation/simulator/simulator.go +++ b/internal/attestation/simulator/simulator.go @@ -6,6 +6,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// TPM2 simulator used for unit tests. package simulator import ( diff --git a/internal/attestation/vtpm/vtpm.go b/internal/attestation/vtpm/vtpm.go index 6b24652bb..e03be8b6c 100644 --- a/internal/attestation/vtpm/vtpm.go +++ b/internal/attestation/vtpm/vtpm.go @@ -4,6 +4,53 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +# Virtual Trusted Platform Module (vTPM) + +This package provides functions to interact with a vTPM. +It also implements the low level TPM attestation and verification logic of Constellation's TPM attestation workflow. + +Code that directly interacts with the TPM goes here. + +# vTPM components + +For attestation we make use of multiple vTPM features: + + - Endorsement Key + + Asymmetric key used to establish trust in other keys issued by the TPM or used directly for attestation. The private part never leaves the TPM, while the public part, referred to as Endorsement Public Key (EPK), is available to remote parties. The TPM can issue new keys, signed by its endorsement key, which can then be verified by a remote party using the EPK. + + - Endorsement Public Key Certificate (EPKC) + + A Certificate signed by the TPM manufacturer verifying the authenticity of the EPK. The public key of the Certificate is the EPK. + + - Event Log + + A log of events over the boot process. + + - [Platform Control Register (PCR)] + + Registers holding measurements of software and configuration data. PCR values are not directly written, but updated: a new value is the digest of the old value concatenated with the to be added data. + Contents of the PCRs can be signed for attestation. Providing proof to a remote party about software running on the system. + +# Attestation flow + +1. The VM boots and writes its measured software state to the PCRs. + +2. The PCRs are hashed and signed by the EPK. + +3. An attestation statement is created, containing the EPK, the original PCR values, the hashed PCRs, the signature, and the event log. + +4. A remote party establishes trust in the TPMs EPK by verifying its EPKC with the TPM manufactures CA certificate chain. + +5. The remote party verifies the signature was created by the TPM, and the hash matches the PCRs. + +6. The remote party reads the event log and verifies measuring the event log results in the given PCR values + +7. The software state is now verified, the only thing left to do is to decide if the state is good or not. This is done by comparing the given PCR values to a set of expected PCR values. + +[Platform Control Register (PCR)]: https://link.springer.com/chapter/10.1007/978-1-4302-6584-9_12 +*/ package vtpm import ( diff --git a/internal/cloud/aws/cloud.go b/internal/cloud/aws/aws.go similarity index 96% rename from internal/cloud/aws/cloud.go rename to internal/cloud/aws/aws.go index 95008a148..a9552bc3a 100644 --- a/internal/cloud/aws/cloud.go +++ b/internal/cloud/aws/aws.go @@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Implements interaction with the AWS API. + +Instance metadata is retrieved from the [AWS IMDS API]. + +Retrieving metadata of other instances is done by using the AWS compute API, and requires AWS credentials. + +[AWS IMDS API]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html +*/ package aws import ( diff --git a/internal/cloud/aws/cloud_test.go b/internal/cloud/aws/aws_test.go similarity index 100% rename from internal/cloud/aws/cloud_test.go rename to internal/cloud/aws/aws_test.go diff --git a/internal/cloud/azure/cloud.go b/internal/cloud/azure/azure.go similarity index 98% rename from internal/cloud/azure/cloud.go rename to internal/cloud/azure/azure.go index 1d654fbb2..943d44888 100644 --- a/internal/cloud/azure/cloud.go +++ b/internal/cloud/azure/azure.go @@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Implements interaction with the Azure API. + +Instance metadata is retrieved from the [Azure IMDS API]. + +Retrieving metadata of other instances is done by using the Azure API, and requires Azure credentials. + +[Azure IMDS API]: https://docs.microsoft.com/en-us/azure/virtual-machines/linux/instance-metadata-service +*/ package azure import ( diff --git a/internal/cloud/azure/cloud_test.go b/internal/cloud/azure/azure_test.go similarity index 100% rename from internal/cloud/azure/cloud_test.go rename to internal/cloud/azure/azure_test.go diff --git a/internal/cloud/azureshared/doc.go b/internal/cloud/azureshared/azureshared.go similarity index 100% rename from internal/cloud/azureshared/doc.go rename to internal/cloud/azureshared/azureshared.go diff --git a/internal/cloud/cloud.go b/internal/cloud/cloud.go index 1b2c172fd..a63a251d5 100644 --- a/internal/cloud/cloud.go +++ b/internal/cloud/cloud.go @@ -4,6 +4,26 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +# Cloud + +This package provides functions to interact with cloud providers. +This is mainly used to fetch information about the current instance, or other instances of the Constellation cluster. + +Implementation of the cloud provider specific code is done in subpackages named after the CSP. +Code that is commonly used by other packages that do not require actual interaction with the CSP API, +such as CSP URI string parsing or data types, should go in a shared package instead. + +A cloud package should implement the following interface: + + type Cloud interface { + List(ctx context.Context) ([]metadata.InstanceMetadata, error) + Self(ctx context.Context) (metadata.InstanceMetadata, error) + GetLoadBalancerEndpoint(ctx context.Context) (string, error) + InitSecretHash(ctx context.Context) ([]byte, error) + UID(ctx context.Context) (string, error) + } +*/ package cloud const ( diff --git a/internal/cloud/gcp/cloud.go b/internal/cloud/gcp/gcp.go similarity index 97% rename from internal/cloud/gcp/cloud.go rename to internal/cloud/gcp/gcp.go index cff90a237..ca8362b87 100644 --- a/internal/cloud/gcp/cloud.go +++ b/internal/cloud/gcp/gcp.go @@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Implements interaction with the GCP API. + +Instance metadata is retrieved from the [GCP metadata API]. + +Retrieving metadata of other instances is done by using the GCP compute API, and requires GCP credentials. + +[GCP metadata API]: https://cloud.google.com/compute/docs/storing-retrieving-metadata +*/ package gcp import ( diff --git a/internal/cloud/gcp/cloud_test.go b/internal/cloud/gcp/gcp_test.go similarity index 100% rename from internal/cloud/gcp/cloud_test.go rename to internal/cloud/gcp/gcp_test.go diff --git a/internal/cloud/gcpshared/doc.go b/internal/cloud/gcpshared/gcpshared.go similarity index 100% rename from internal/cloud/gcpshared/doc.go rename to internal/cloud/gcpshared/gcpshared.go diff --git a/internal/cloud/qemu/cloud.go b/internal/cloud/qemu/qemu.go similarity index 97% rename from internal/cloud/qemu/cloud.go rename to internal/cloud/qemu/qemu.go index 55d707653..f9a4bdfc3 100644 --- a/internal/cloud/qemu/cloud.go +++ b/internal/cloud/qemu/qemu.go @@ -4,6 +4,9 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +This package provides an interface to fake a CSP API for QEMU instances. +*/ package qemu import ( diff --git a/internal/config/config.go b/internal/config/config.go index e9302be85..d6b42a910 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -8,6 +8,14 @@ SPDX-License-Identifier: AGPL-3.0-only // https://github.com/siderolabs/talos/tree/master/hack/docgen // //go:generate docgen ./config.go ./config_doc.go Configuration + +/* +Definitions for Constellation's user config file. + +The config file is used by the CLI to create and manage a Constellation cluster. + +All config relevant definitions, parsing and validation functions should go here. +*/ package config import ( diff --git a/internal/config/config_doc.go b/internal/config/config_doc.go index 7129f5f67..50be1b94e 100644 --- a/internal/config/config_doc.go +++ b/internal/config/config_doc.go @@ -394,7 +394,7 @@ func (_ QEMUConfig) Doc() *encoder.Doc { func GetConfigurationDoc() *encoder.FileDoc { return &encoder.FileDoc{ Name: "Configuration", - Description: "This binary can be build from siderolabs/talos projects. Located at:\nhttps://github.com/siderolabs/talos/tree/master/hack/docgen\n", + Description: "Definitions for Constellation's user config file.\n\nThe config file is used by the CLI to create and manage a Constellation cluster.\n\nAll config relevant definitions, parsing and validation functions should go here.\n", Structs: []*encoder.Doc{ &ConfigDoc, &UpgradeConfigDoc, diff --git a/internal/deploy/helm/helm.go b/internal/deploy/helm/helm.go index 8355bd658..38f304155 100644 --- a/internal/deploy/helm/helm.go +++ b/internal/deploy/helm/helm.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package helm provides types and functions shared across services. package helm // Release bundles all information necessary to create a helm release. @@ -23,7 +24,7 @@ type Releases struct { } // MergeMaps returns a new map that is the merger of it's inputs. -// Key colissions are resolved by taking the value of the second argument (map b). +// Key collisions are resolved by taking the value of the second argument (map b). // Taken from: https://github.com/helm/helm/blob/dbc6d8e20fe1d58d50e6ed30f09a04a77e4c68db/pkg/cli/values/options.go#L91-L108. func MergeMaps(a, b map[string]any) map[string]any { out := make(map[string]any, len(a)) diff --git a/internal/deploy/user/create_user.go b/internal/deploy/user/create_user.go deleted file mode 100644 index 050eba86a..000000000 --- a/internal/deploy/user/create_user.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright (c) Edgeless Systems GmbH - -SPDX-License-Identifier: AGPL-3.0-only -*/ - -package user - -import ( - "context" - "fmt" - "os/exec" - "strconv" -) - -// Unix defines an user creation interface for UNIX systems. -type Unix struct{} - -// reference: https://man7.org/linux/man-pages/man8/useradd.8.html#EXIT_VALUES -const exitCodeAlreadyInUse = 9 - -// CreateUser creates a new user with sudo access. -func (u Unix) CreateUser(ctx context.Context, username string) error { - cmd := exec.CommandContext(ctx, "useradd", "-m", "-G", "wheel,sudo", username) - if err := cmd.Run(); err != nil { - // do not fail if user already exists - if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == exitCodeAlreadyInUse { - return ErrUserOrGroupAlreadyExists - } - return fmt.Errorf("creating a new user failed: %w", err) - } - return nil -} - -// CreateUserWithSpecificUIDAndGID creates a new user with sudo access and a specific UID and GID. -func (u Unix) CreateUserWithSpecificUIDAndGID(ctx context.Context, username string, uid int, gid int) error { - // Add group first with the targeted gid - cmd := exec.CommandContext(ctx, "groupadd", "-g", strconv.Itoa(gid), username) - if err := cmd.Run(); err != nil { - // do not fail if group already exists - if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == exitCodeAlreadyInUse { - return ErrUserOrGroupAlreadyExists - } - return fmt.Errorf("creating a new group failed: %w", err) - } - - // Then, create the user with both the UID and GID assigned. - cmd = exec.CommandContext(ctx, "useradd", "-m", "-G", "wheel,sudo", "-u", strconv.Itoa(uid), "-g", strconv.Itoa(gid), username) - if err := cmd.Run(); err != nil { - // do not fail if user already exists - if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == exitCodeAlreadyInUse { - return ErrUserOrGroupAlreadyExists - } - return fmt.Errorf("creating a new user: %w", err) - } - return nil -} diff --git a/internal/deploy/user/linux_user.go b/internal/deploy/user/linux_user.go deleted file mode 100644 index 895efcf66..000000000 --- a/internal/deploy/user/linux_user.go +++ /dev/null @@ -1,215 +0,0 @@ -/* -Copyright (c) Edgeless Systems GmbH - -SPDX-License-Identifier: AGPL-3.0-only -*/ - -package user - -import ( - "context" - "errors" - "fmt" - "os" - "strconv" - - "github.com/spf13/afero" -) - -// ErrUserDoesNotExist is returned by GetLinuxUser if a linux user does not exist yet. -var ErrUserDoesNotExist = errors.New("user does not exist") - -// ErrUserOrGroupAlreadyExists is the Go error converted from the result of useradd or groupadd when an user or group already exists. -var ErrUserOrGroupAlreadyExists = errors.New("user or group already exists") - -type passwdParser interface { - Parse(fs afero.Fs) (Entries, error) -} - -type userCreator interface { - CreateUser(ctx context.Context, username string) error - CreateUserWithSpecificUIDAndGID(ctx context.Context, username string, uid int, gid int) error -} - -// LinuxUser holds relevant information about a linux user (subset of /etc/passwd). -type LinuxUser struct { - Username string - Home string - UID int - GID int -} - -// LinuxUserManager can retrieve information on linux users and create new users. -type LinuxUserManager struct { - Fs afero.Fs - Passwd passwdParser - Creator userCreator -} - -// NewLinuxUserManager creates a new LinuxUserManager. -func NewLinuxUserManager(fs afero.Fs) LinuxUserManager { - return LinuxUserManager{ - Fs: fs, - Passwd: Passwd{}, - Creator: Unix{}, - } -} - -// NewLinuxUserManagerFake creates a new LinuxUserManager that is used for unit tests. -func NewLinuxUserManagerFake(fs afero.Fs) LinuxUserManager { - return LinuxUserManager{ - Fs: fs, - Passwd: Passwd{}, - Creator: &StubUserCreator{fs: fs}, - } -} - -// StubUserCreator is used for unit tests. -type StubUserCreator struct { - fs afero.Fs - usernames []string - uids []int - createUserErr error - currentUID int -} - -// CreateUser for StubUserCreator creates an user for an unit test environment. -func (s *StubUserCreator) CreateUser(ctx context.Context, username string) error { - if stringInSlice(username, s.usernames) { - // do not fail if user already exists - return nil - } - - // We want created users to start at UID 1000 - if s.currentUID == 0 { - s.currentUID = 1000 - } - - if s.createUserErr != nil { - return s.createUserErr - } - - // If no predefined error is supposed to happen, increase the UID (unless the file system code fails) - if s.fs != nil { - lineToWrite := fmt.Sprintf("%s:x:%d:%d:%s:/var/home/%s:/bin/bash\n", username, s.currentUID, s.currentUID, username, username) - file, err := s.fs.OpenFile("/etc/passwd", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o644) - if err != nil { - return err - } - defer file.Close() - - n, err := file.WriteString(lineToWrite) - - if err != nil { - return err - } else if n != len(lineToWrite) { - return errors.New("written text too short") - } - } - - s.currentUID++ - s.usernames = append(s.usernames, username) - - return nil -} - -// CreateUserWithSpecificUIDAndGID for StubUserCreator creates an user with a specific UID and GID for an unit test environment. -func (s *StubUserCreator) CreateUserWithSpecificUIDAndGID(ctx context.Context, username string, uid int, gid int) error { - if stringInSlice(username, s.usernames) { - // do not fail if user already exists - return nil - } - if intInSlice(uid, s.uids) { - return errors.New("uid is already used by another user") - } - - if s.createUserErr != nil { - return s.createUserErr - } - - // If no predefined error is supposed to happen, increase the UID (unless the file system code fails) - if s.fs != nil { - lineToWrite := fmt.Sprintf("%s:x:%d:%d:%s:/var/home/%s:/bin/bash\n", username, uid, gid, username, username) - file, err := s.fs.OpenFile("/etc/passwd", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o644) - if err != nil { - return err - } - defer file.Close() - - n, err := file.WriteString(lineToWrite) - - if err != nil { - return err - } else if n != len(lineToWrite) { - return errors.New("written text too short") - } - } - - // Mark UID as used (we don't track GIDs though, as multiple users can belong to one GID) - s.uids = append(s.uids, uid) - - // Avoid potential collisions - if s.currentUID == uid { - s.currentUID++ - } - - s.usernames = append(s.usernames, username) - - return nil -} - -// GetLinuxUser tries to find an existing linux user in /etc/passwd. -func (l *LinuxUserManager) GetLinuxUser(username string) (LinuxUser, error) { - entries, err := l.Passwd.Parse(l.Fs) - if err != nil { - return LinuxUser{}, err - } - if _, ok := entries[username]; !ok { - return LinuxUser{}, ErrUserDoesNotExist - } - entry := entries[username] - uid, err := strconv.Atoi(entry.UID) - if err != nil { - return LinuxUser{}, fmt.Errorf("parsing users uid: %w", err) - } - gid, err := strconv.Atoi(entry.GID) - if err != nil { - return LinuxUser{}, fmt.Errorf("parsing users gid: %w", err) - } - return LinuxUser{ - Username: username, - Home: entry.Directory, - UID: uid, - GID: gid, - }, nil -} - -// EnsureLinuxUserExists will try to create the user specified by username and call GetLinuxUser to retrieve user information. -func (l *LinuxUserManager) EnsureLinuxUserExists(ctx context.Context, username string) (LinuxUser, error) { - // try to create user (even if it already exists) - if err := l.Creator.CreateUser(ctx, username); err != nil && !errors.Is(err, ErrUserOrGroupAlreadyExists) { - return LinuxUser{}, err - } - - return l.GetLinuxUser(username) -} - -// stringInSlice checks if a given string exists in a slice of strings. -func stringInSlice(a string, list []string) bool { - for _, b := range list { - if b == a { - return true - } - } - return false -} - -// intInSlice checks if a given string exists in a slice of strings. -func intInSlice(a int, list []int) bool { - for _, b := range list { - if b == a { - return true - } - } - return false -} diff --git a/internal/deploy/user/linux_user_test.go b/internal/deploy/user/linux_user_test.go deleted file mode 100644 index 37f1a6288..000000000 --- a/internal/deploy/user/linux_user_test.go +++ /dev/null @@ -1,134 +0,0 @@ -/* -Copyright (c) Edgeless Systems GmbH - -SPDX-License-Identifier: AGPL-3.0-only -*/ - -package user - -import ( - "context" - "errors" - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - goleak.VerifyTestMain(m) -} - -func TestGetLinuxUser(t *testing.T) { - username := "user" - - testCases := map[string]struct { - passwdContents string - wantErr bool - wantUser LinuxUser - }{ - "get works": { - passwdContents: "user:x:1000:1000:user:/var/home/user:/bin/bash\n", - wantErr: false, - wantUser: LinuxUser{ - Username: "user", - Home: "/var/home/user", - UID: 1000, - GID: 1000, - }, - }, - "user does not exist": { - passwdContents: "", - wantErr: true, - }, - "parse fails": { - passwdContents: "invalid contents\n", - wantErr: true, - }, - "invalid uid": { - passwdContents: "user:x:invalid:1000:user:/var/home/user:/bin/bash\n", - wantErr: true, - }, - "invalid gid": { - passwdContents: "user:x:1000:invalid:user:/var/home/user:/bin/bash\n", - wantErr: true, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - assert := assert.New(t) - require := require.New(t) - - fs := afero.NewMemMapFs() - assert.NoError(afero.WriteFile(fs, "/etc/passwd", []byte(tc.passwdContents), 0o755)) - manager := NewLinuxUserManagerFake(fs) - user, err := manager.GetLinuxUser(username) - - if tc.wantErr { - assert.Error(err) - return - } - require.NoError(err) - assert.Equal(tc.wantUser, user) - }) - } -} - -func TestEnsureLinuxUserExists(t *testing.T) { - username := "user" - - testCases := map[string]struct { - userCreator *StubUserCreator - wantErr bool - wantUser LinuxUser - }{ - "create works": { - userCreator: &StubUserCreator{}, - wantErr: false, - wantUser: LinuxUser{ - Username: "user", - Home: "/var/home/user", - UID: 1000, - GID: 1000, - }, - }, - "create fails": { - userCreator: &StubUserCreator{ - createUserErr: errors.New("create fails"), - }, - wantErr: true, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - assert := assert.New(t) - require := require.New(t) - - fs := afero.NewMemMapFs() - manager := NewLinuxUserManagerFake(fs) - tc.userCreator.fs = fs - manager.Creator = tc.userCreator - user, err := manager.EnsureLinuxUserExists(context.Background(), username) - - if tc.wantErr { - assert.Error(err) - return - } - require.NoError(err) - assert.Equal(tc.wantUser, user) - assert.ElementsMatch([]string{username}, tc.userCreator.usernames) - }) - } -} - -func TestStringInSlice(t *testing.T) { - assert := assert.New(t) - testSlice := []string{"abc", "efg", "xyz"} - - assert.True(stringInSlice("efg", testSlice)) - assert.False(stringInSlice("hij", testSlice)) -} diff --git a/internal/deploy/user/passwd.go b/internal/deploy/user/passwd.go deleted file mode 100644 index eb07c09b1..000000000 --- a/internal/deploy/user/passwd.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright (c) Edgeless Systems GmbH - -SPDX-License-Identifier: AGPL-3.0-only -*/ - -package user - -import ( - "bufio" - "errors" - "strings" - - "github.com/spf13/afero" -) - -// Entry is an entry of a '/etc/passwd' file. -type Entry struct { - Password string - UID string - GID string - GECOS string - Directory string - Shell string -} - -// Entries contains the information for each user defined in '/etc/passwd'. -type Entries map[string]Entry - -// Passwd allows to parse users from '/etc/passwd' on the local system. -type Passwd struct{} - -// Parse opens the '/etc/passwd' file and parses it into a map from usernames to Entries. -func (p Passwd) Parse(fs afero.Fs) (Entries, error) { - return p.parseFile(fs, "/etc/passwd") -} - -// parseFile opens the file and parses it into a map from usernames to Entries. -func (p Passwd) parseFile(fs afero.Fs, path string) (Entries, error) { - file, err := fs.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - entries := Entries{} - scanner := bufio.NewScanner(file) - - for scanner.Scan() { - // File format: https://man7.org/linux/man-pages/man5/passwd.5.html - - fields := strings.Split(scanner.Text(), ":") - if len(fields) != 7 { - return nil, errors.New("invalid number of fields") - } - - entries[fields[0]] = Entry{ - Password: fields[1], - UID: fields[2], - GID: fields[3], - GECOS: fields[4], - Directory: fields[5], - Shell: fields[6], - } - } - - return entries, scanner.Err() -} diff --git a/internal/deploy/user/passwd_test.go b/internal/deploy/user/passwd_test.go deleted file mode 100644 index 8a78a3589..000000000 --- a/internal/deploy/user/passwd_test.go +++ /dev/null @@ -1,95 +0,0 @@ -/* -Copyright (c) Edgeless Systems GmbH - -SPDX-License-Identifier: AGPL-3.0-only -*/ - -package user - -import ( - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestParse(t *testing.T) { - filename := "/etc/passwd" - - testCases := map[string]struct { - passwdContents string - createFile bool - wantEntries Entries - wantErr bool - }{ - "parse works": { - passwdContents: "root:x:0:0:root:/root:/bin/bash\n", - createFile: true, - wantEntries: Entries{ - "root": { - Password: "x", - UID: "0", - GID: "0", - GECOS: "root", - Directory: "/root", - Shell: "/bin/bash", - }, - }, - wantErr: false, - }, - "multiple lines": { - passwdContents: "root:x:0:0:root:/root:/bin/bash\nfoo:y:1:2:bar:baz:sh", - createFile: true, - wantEntries: Entries{ - "root": { - Password: "x", - UID: "0", - GID: "0", - GECOS: "root", - Directory: "/root", - Shell: "/bin/bash", - }, - "foo": { - Password: "y", - UID: "1", - GID: "2", - GECOS: "bar", - Directory: "baz", - Shell: "sh", - }, - }, - wantErr: false, - }, - "passwd is corrupt": { - passwdContents: "too:few:fields\n", - createFile: true, - wantErr: true, - }, - "file does not exist": { - createFile: false, - wantErr: true, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - assert := assert.New(t) - require := require.New(t) - - fs := afero.NewMemMapFs() - if tc.createFile { - assert.NoError(afero.WriteFile(fs, filename, []byte(tc.passwdContents), 0o644)) - } - passwd := Passwd{} - entries, err := passwd.Parse(fs) - - if tc.wantErr { - assert.Error(err) - return - } - require.NoError(err) - assert.Equal(tc.wantEntries, entries) - }) - } -} diff --git a/internal/grpc/atlscredentials/atlscredentials.go b/internal/grpc/atlscredentials/atlscredentials.go index 512c026fd..69d875aca 100644 --- a/internal/grpc/atlscredentials/atlscredentials.go +++ b/internal/grpc/atlscredentials/atlscredentials.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package atlscredentials handles creation of TLS credentials for attested TLS (ATLS). package atlscredentials import ( diff --git a/internal/grpc/dialer/dialer.go b/internal/grpc/dialer/dialer.go index 69834bc34..8c42f4041 100644 --- a/internal/grpc/dialer/dialer.go +++ b/internal/grpc/dialer/dialer.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package dialer provides a grpc dialer that can be used to create grpc client connections with different levels of ATLS encryption / verification. package dialer import ( diff --git a/internal/grpc/retry/retry.go b/internal/grpc/retry/retry.go index 48da4b1dd..9d03279a4 100644 --- a/internal/grpc/retry/retry.go +++ b/internal/grpc/retry/retry.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package retry provides functions to check if a gRPC error is retryable. package retry import ( diff --git a/internal/grpc/testdialer/bufconndialer.go b/internal/grpc/testdialer/testdialer.go similarity index 95% rename from internal/grpc/testdialer/bufconndialer.go rename to internal/grpc/testdialer/testdialer.go index 6cf8bd38b..6e885e4d7 100644 --- a/internal/grpc/testdialer/bufconndialer.go +++ b/internal/grpc/testdialer/testdialer.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package testdialer provides a fake dialer for testing. package testdialer import ( diff --git a/internal/installer/install.go b/internal/installer/installer.go similarity index 98% rename from internal/installer/install.go rename to internal/installer/installer.go index 6fb5f3c73..9ea5f6673 100644 --- a/internal/installer/install.go +++ b/internal/installer/installer.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package installer provides functionality to install binary components of supported kubernetes versions. package installer import ( diff --git a/internal/installer/install_test.go b/internal/installer/installer_test.go similarity index 100% rename from internal/installer/install_test.go rename to internal/installer/installer_test.go diff --git a/internal/kms/config/config.go b/internal/kms/config/config.go index 8083ddb5f..9dcaa5918 100644 --- a/internal/kms/config/config.go +++ b/internal/kms/config/config.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package config provides configuration constants for the KeyService. package config const ( diff --git a/keyservice/internal/README.md b/internal/kms/kms/README.md similarity index 100% rename from keyservice/internal/README.md rename to internal/kms/kms/README.md diff --git a/internal/kms/kms/aws/aws.go b/internal/kms/kms/aws/aws.go index 94400ce06..30eec44a9 100644 --- a/internal/kms/kms/aws/aws.go +++ b/internal/kms/kms/aws/aws.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package aws implements a KMS backend for AWS KMS. package aws import ( diff --git a/internal/kms/kms/azure/azure.go b/internal/kms/kms/azure/azure.go index bdaeb95cb..d122b4b84 100644 --- a/internal/kms/kms/azure/azure.go +++ b/internal/kms/kms/azure/azure.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package azure implements KMS backends for Azure Key Vault and Azure managed HSM. package azure import ( diff --git a/internal/kms/kms/cluster/cluster.go b/internal/kms/kms/cluster/cluster.go index cc9e39767..f9c0f30e9 100644 --- a/internal/kms/kms/cluster/cluster.go +++ b/internal/kms/kms/cluster/cluster.go @@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package cluster implements a KMS backend for in cluster key management. + +The cluster backend holds a master key, and corresponding salt. +Data Encryption Keys (DEK) are derived from master key and salt using HKDF. + +This backend does not require a storage backend, as keys are derived on demand and not stored anywhere. +For that purpose the special NoStoreURI can be used during KMS initialization. +*/ package cluster import ( diff --git a/internal/kms/kms/gcp/gcp.go b/internal/kms/kms/gcp/gcp.go index 3e59f3e04..49b132f69 100644 --- a/internal/kms/kms/gcp/gcp.go +++ b/internal/kms/kms/gcp/gcp.go @@ -4,6 +4,25 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package gcp implements a KMS backend for Google Cloud KMS. + +The following permissions are required for the service account used to authenticate with GCP: + + - cloudkms.cryptoKeyVersions.create + + - cloudkms.cryptoKeyVersions.update + + - cloudkms.cryptoKeyVersions.useToDecrypt + + - cloudkms.cryptoKeyVersions.useToEncrypt + + - cloudkms.importJobs.create + + - cloudkms.importJobs.get + + - cloudkms.importJobs.useToImport +*/ package gcp import ( diff --git a/internal/kms/kms/kms.go b/internal/kms/kms/kms.go index 553ff2acf..fe6ce5cc2 100644 --- a/internal/kms/kms/kms.go +++ b/internal/kms/kms/kms.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package kms provides an abstract interface for Key Management Services. package kms import ( diff --git a/internal/kms/kms/util/crypto.go b/internal/kms/kms/util/crypto.go index af176d017..563d57125 100644 --- a/internal/kms/kms/util/crypto.go +++ b/internal/kms/kms/util/crypto.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package util provides utility functions for the KMS backends. package util import ( diff --git a/internal/kms/setup/setup.go b/internal/kms/setup/setup.go index 53f827346..95832480e 100644 --- a/internal/kms/setup/setup.go +++ b/internal/kms/setup/setup.go @@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package setup provides functions to create a KMS and key store from a given URI. + +This package does not provide any functionality to interact with the KMS or key store, +but only to create them. + +Adding support for a new KMS or storage backend requires adding a new URI for that backend, +and implementing the corresponding get*Config function. +*/ package setup import ( diff --git a/internal/kms/storage/storage.go b/internal/kms/storage/storage.go index 1e9b09bc1..d8ec42c1e 100644 --- a/internal/kms/storage/storage.go +++ b/internal/kms/storage/storage.go @@ -4,6 +4,11 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package storage implements storage backends for DEKs. + +If an unset DEK is requested, the backend MUST return [ErrDEKUnset]. +*/ package storage import ( diff --git a/internal/kubernetes/kubectl/kubectl.go b/internal/kubernetes/kubectl/kubectl.go index 393f30a58..3ad175ca8 100644 --- a/internal/kubernetes/kubectl/kubectl.go +++ b/internal/kubernetes/kubectl/kubectl.go @@ -4,6 +4,8 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package kubectl provides a kubectl-like interface for Kubernetes. +// Functions defined here should not make use of [os/exec]. package kubectl import ( diff --git a/internal/kubernetes/kubernetes.go b/internal/kubernetes/kubernetes.go new file mode 100644 index 000000000..cf8c478da --- /dev/null +++ b/internal/kubernetes/kubernetes.go @@ -0,0 +1,12 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +/* +Package kubernetes provides data types and custom marshalers for Kubernetes API objects. + +Interaction with the Kubernetes API should be handled by the kubectl subpackage. +*/ +package kubernetes diff --git a/internal/license/license.go b/internal/license/license.go index 64bc27052..1e9525361 100644 --- a/internal/license/license.go +++ b/internal/license/license.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package license provides functions to check a user's Constellation license. package license import ( diff --git a/internal/logger/log.go b/internal/logger/log.go index 3c31e5366..0f5d2a16a 100644 --- a/internal/logger/log.go +++ b/internal/logger/log.go @@ -26,15 +26,15 @@ This can also be used to add context to a single log message: # Log Levels -Use Debugf() to log low level and detailed information that is useful for debugging. +Use [Logger.Debugf] to log low level and detailed information that is useful for debugging. -Use Infof() to log general information. This method is correct for most logging purposes. +Use [Logger.Infof] to log general information. This method is correct for most logging purposes. -Use Warnf() to log information that may indicate unwanted behavior, but is not an error. +Use [Logger.Warnf] to log information that may indicate unwanted behavior, but is not an error. -Use Errorf() to log information about any errors that occurred. +Use [Logger.Errorf] to log information about any errors that occurred. -Use Fatalf() to log information about any errors that occurred and then exit the program. +Use [Logger.Fatalf] to log information about any errors that occurred and then exit the program. */ package logger diff --git a/internal/nodestate/nodestate.go b/internal/nodestate/nodestate.go index d72e7c56f..40e8113c7 100644 --- a/internal/nodestate/nodestate.go +++ b/internal/nodestate/nodestate.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package nodestate is used to persist the state of a Constellation node to disk. package nodestate import ( diff --git a/internal/oid/oid.go b/internal/oid/oid.go index 4d1b31bca..b95081c04 100644 --- a/internal/oid/oid.go +++ b/internal/oid/oid.go @@ -2,7 +2,9 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only +*/ +/* Package oid defines OIDs for different CSPs. Currently this is used in attested TLS to distinguish the attestation documents. OIDs beginning with 1.3.9900 are reserved and can be used without registration. diff --git a/internal/retry/retry.go b/internal/retry/retry.go index c0d65aa01..ab8bbdfdc 100644 --- a/internal/retry/retry.go +++ b/internal/retry/retry.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package retry provides a simple interface for retrying operations. package retry import ( diff --git a/internal/sigstore/sigstore.go b/internal/sigstore/sigstore.go new file mode 100644 index 000000000..0c90bd898 --- /dev/null +++ b/internal/sigstore/sigstore.go @@ -0,0 +1,8 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +// Package sigstore is used to verify Constellation components using sigstore, cosign and rekor. +package sigstore diff --git a/internal/versions/versions.go b/internal/versions/versions.go index 9d3f7d63a..af9929057 100644 --- a/internal/versions/versions.go +++ b/internal/versions/versions.go @@ -4,6 +4,11 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package versions defines the supported versions of Constellation components. + +Binaries and container image versions are pinned by their hashes, the generate tool can be found in the hash-generator subpackage. +*/ package versions import ( diff --git a/internal/versionsapi/cli/main.go b/internal/versionsapi/cli/main.go index 9c1c07343..a59803f60 100644 --- a/internal/versionsapi/cli/main.go +++ b/internal/versionsapi/cli/main.go @@ -4,6 +4,17 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +This package provides a CLI tool to interact with the Constellation versions API. + +The tool can be used to request information from the API, but also for admin tasks. +All actions require an authentication against AWS with the common permissions. +Andministrative tasks like adding or removing versions require further AWS permissions +as well as permissions to GCP and Azure. + +The CLI is commonly used in the CI pipeline. Most actions shouldn't be executed manually +by a developer. Notice that there is no synchronization on API operations. +*/ package main import ( diff --git a/internal/versionsapi/client/client.go b/internal/versionsapi/client/client.go index 7b64a6535..351e8e3d1 100644 --- a/internal/versionsapi/client/client.go +++ b/internal/versionsapi/client/client.go @@ -4,6 +4,24 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package client provides a client for the versions API. + +The client needs to be authenticated with AWS. It should be used in internal +development and CI tools for administrative tasks. For just fetching information +from the API, use the fetcher package instead. + +Needed IAM permissions for read mode: +- "s3:GetObject" +- "s3:ListBucket" + +Additional needed IAM permissions for write mode: +- "s3:PutObject" +- "s3:DeleteObject" +- "cloudfront:CreateInvalidation" + +Thread-safety of the bucket is not guaranteed. The client is not thread-safe. +*/ package client import ( @@ -30,20 +48,6 @@ import ( ) // Client is the client for the versions API. -// -// The client needs to be authenticated with AWS. It is the interface that should -// be used in internal development and CI tools. -// -// Needed IAM permissions for read mode: -// - "s3:GetObject" -// - "s3:ListBucket" -// -// Additional needed IAM permissions for write mode: -// - "s3:PutObject" -// - "s3:DeleteObject" -// - "cloudfront:CreateInvalidation" -// -// Thread-safety of the bucket is not guaranteed. The client is not thread-safe. type Client struct { config aws.Config cloudfrontClient *cloudfront.Client diff --git a/internal/versionsapi/fetcher/fetcher.go b/internal/versionsapi/fetcher/fetcher.go index 9c6357d25..fe4ea5668 100644 --- a/internal/versionsapi/fetcher/fetcher.go +++ b/internal/versionsapi/fetcher/fetcher.go @@ -4,6 +4,13 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +/* +Package fetcher implements a client for the versions API. + +The fetcher is used to get information from the versions API without having to +authenticate with AWS, where the API is currently hosted. This package should be +used in user-facing application code most of the time, like the CLI. +*/ package fetcher import ( @@ -15,11 +22,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/versionsapi" ) -// Fetcher fetches versions API resources. -// -// The fetcher is used to get information from the versions API without having to -// authenticate with AWS. It is the interface that should be used in user-facing -// application code most of the time. +// Fetcher fetches versions API resources without authentication. type Fetcher struct { httpc httpc } diff --git a/internal/versionsapi/versionsapi.go b/internal/versionsapi/versionsapi.go new file mode 100644 index 000000000..20ce5a67e --- /dev/null +++ b/internal/versionsapi/versionsapi.go @@ -0,0 +1,20 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +/* +# Versions API + +The Versions API is a provides information about versions of Constellation components. + +This package defines API types that represents objects of the versions API. +The types provide helper methods for validation and commonly used operations on the +information contained in the objects. Especially the paths used for the API are defined +in these helper methods. + +The package also provides helper functions that can be used in context of the versions API, +e.g. to validate versions. +*/ +package versionsapi diff --git a/internal/watcher/watcher.go b/internal/watcher/watcher.go index 2ab39583d..07768f6f8 100644 --- a/internal/watcher/watcher.go +++ b/internal/watcher/watcher.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package watcher implements a file watcher to update an object on file changes. package watcher import ( diff --git a/joinservice/README.md b/joinservice/README.md index bc5dbc87f..2c50eca2c 100644 --- a/joinservice/README.md +++ b/joinservice/README.md @@ -7,7 +7,6 @@ New nodes (at cluster start, or later through autoscaling) send an IssueJoinTick The join service verifies the new nodes certificate and attestation statement. If attestation is successful, the new node is supplied with a disk encryption key for its state disk, and a Kubernetes bootstrap token, so it may join the cluster. - ## Packages ### [joinproto](./joinproto/) diff --git a/joinservice/internal/kms/kms.go b/joinservice/internal/kms/kms.go index e63a00cc9..f8bc3f87b 100644 --- a/joinservice/internal/kms/kms.go +++ b/joinservice/internal/kms/kms.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package kms handles communication with Constellation's key service to request data encryption keys for new or rejoining nodes. package kms import ( diff --git a/joinservice/internal/kubeadm/kubeadm.go b/joinservice/internal/kubeadm/kubeadm.go index f5faa93c6..44f028617 100644 --- a/joinservice/internal/kubeadm/kubeadm.go +++ b/joinservice/internal/kubeadm/kubeadm.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package kubeadm handles joining of new nodes by creating Kubernetes Join Tokens. package kubeadm import ( diff --git a/joinservice/internal/kubernetes/kubernetes.go b/joinservice/internal/kubernetes/kubernetes.go index 5b00762f0..6e2913eb7 100644 --- a/joinservice/internal/kubernetes/kubernetes.go +++ b/joinservice/internal/kubernetes/kubernetes.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package kubernetes interacts with the Kubernetes API to update an fetch objects related to joining nodes. package kubernetes import ( diff --git a/joinservice/internal/kubernetesca/kubernetesca.go b/joinservice/internal/kubernetesca/kubernetesca.go index e8405a881..5a12e3f3b 100644 --- a/joinservice/internal/kubernetesca/kubernetesca.go +++ b/joinservice/internal/kubernetesca/kubernetesca.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// kubernetesca implements a certificate authority that uses the Kubernetes root CA to sign certificates. package kubernetesca import ( diff --git a/joinservice/internal/server/server.go b/joinservice/internal/server/server.go index bbc3fc3b6..fa7097b82 100644 --- a/joinservice/internal/server/server.go +++ b/joinservice/internal/server/server.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package server implements the gRPC endpoint of Constellation's node join service. package server import ( diff --git a/joinservice/joinproto/join.proto b/joinservice/joinproto/join.proto index 686bf8c1d..bb6c8609c 100644 --- a/joinservice/joinproto/join.proto +++ b/joinservice/joinproto/join.proto @@ -5,48 +5,75 @@ package join; option go_package = "github.com/edgelesssys/constellation/v2/joinservice/joinproto"; service API { + // IssueJoinTicket issues a join ticket for a new node. rpc IssueJoinTicket(IssueJoinTicketRequest) returns (IssueJoinTicketResponse); + // IssueRejoinTicket issues a join ticket for a node that has previously joined the cluster. rpc IssueRejoinTicket(IssueRejoinTicketRequest) returns (IssueRejoinTicketResponse); } message IssueJoinTicketRequest { + // disk_uuid is the UUID of a node's state disk. string disk_uuid = 1; + // certificate_request is a certificate request for the node's kubelet certificate. bytes certificate_request = 2; + // is_control_plane indicates whether the node is a control-plane node. bool is_control_plane = 3; } message IssueJoinTicketResponse { + // state_disk_key is the key used to encrypt the state disk. bytes state_disk_key = 1; + // measurement_salt is a salt used to derive the node's ClusterID. + // This value is persisted on the state disk. bytes measurement_salt = 2; + // measurement_secret is a secret used to derive the node's ClusterID. + // This value is NOT persisted on the state disk. bytes measurement_secret = 3; + // kubelet_cert is the certificate to be used by the kubelet. bytes kubelet_cert = 4; + // api_server_endpoint is the endpoint of Constellation's API server. string api_server_endpoint = 5; + // token is the Kubernetes Join Token to be used by the node to join the cluster. string token = 6; + // discovery_token_ca_cert_hash is a hash of the root certificate authority presented by the Kubernetes control-plane. string discovery_token_ca_cert_hash = 7; + // control_plane_files is a list of control-plane certificates and keys. repeated control_plane_cert_or_key control_plane_files = 8; + // kubernetes_version is the Kubernetes version to install on the node. string kubernetes_version = 9; + // kubernetes_components is a list of components to install on the node. repeated KubernetesComponent kubernetes_components = 10; } message control_plane_cert_or_key { + // name of the certificate or key. string name = 1; + // data of the certificate or key. bytes data = 2; } message IssueRejoinTicketRequest { + // disk_uuid is the UUID of a node's state disk. string disk_uuid = 1; } message IssueRejoinTicketResponse { + // state_disk_key is the key to decrypt the state disk. bytes state_disk_key = 1; + // measurement_secret is a secret used to derive the node's ClusterID. + // This value is NOT persisted on the state disk. bytes measurement_secret = 2; } // Discuss if we want to import the init proto instead of duplicating it message KubernetesComponent { + // url to download the component from. string url = 1; + // hash of the component. string hash = 2; + // install_path is the path to install the component to. string install_path = 3; + // extract indicates whether the component is an archive and needs to be extracted. bool extract = 4; } diff --git a/keyservice/README.md b/keyservice/README.md new file mode 100644 index 000000000..ba86a5aa2 --- /dev/null +++ b/keyservice/README.md @@ -0,0 +1,18 @@ +# KeyService + +The KeyService is one of Constellation's Kubernetes components, responsible for distributing keys and secrets to other services. +This includes the JoinService, which contacts the KeyService to derive state disk keys and measurement secrets for newly-joining, and rejoining nodes, +and Constellation's CSI drivers, which contact the KeyService for disk encryption keys. + +The service is not exposed outside the cluster, and should be kept for internal usage only. + +## gRPC API + +Keys can be requested through simple gRPC API based on an ID and key length. + +## Backends + +The KeyService supports multiple backends to store keys and manage crypto operations. +The default option holds a master secret in memory. Keys are derived on demand from this secret, and not stored anywhere. +Other backends make use of external Key Management Systems (KMS) for key derivation and securing a master secret. +When using an external KMS backend, encrypted keys are stored in cloud buckets. diff --git a/keyservice/internal/test/integration_test.go b/keyservice/internal/test/integration_test.go index 65603b8ab..4d8bdd4cc 100644 --- a/keyservice/internal/test/integration_test.go +++ b/keyservice/internal/test/integration_test.go @@ -6,6 +6,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package test provides integration tests for KMS and storage backends. package test import ( diff --git a/upgrade-agent/cmd/main.go b/upgrade-agent/cmd/main.go index e2d49ea19..55df367f6 100644 --- a/upgrade-agent/cmd/main.go +++ b/upgrade-agent/cmd/main.go @@ -12,7 +12,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/logger" - upgradeagent "github.com/edgelesssys/constellation/v2/upgrade-agent" + "github.com/edgelesssys/constellation/v2/upgrade-agent/internal/server" "github.com/spf13/afero" "go.uber.org/zap" ) @@ -36,7 +36,7 @@ func main() { } handler := file.NewHandler(afero.NewOsFs()) - server, err := upgradeagent.New(log, handler) + server, err := server.New(log, handler) if err != nil { log.With(zap.Error(err)).Fatalf("Failed to create update server") } diff --git a/upgrade-agent/server.go b/upgrade-agent/internal/server/server.go similarity index 96% rename from upgrade-agent/server.go rename to upgrade-agent/internal/server/server.go index 79943b163..c59a634f3 100644 --- a/upgrade-agent/server.go +++ b/upgrade-agent/internal/server/server.go @@ -4,7 +4,13 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ -package upgradeagent +/* +Package server implements the gRPC server for the upgrade agent. + +The server is responsible for using kubeadm to upgrade the Kubernetes +release of a Constellation node. +*/ +package server import ( "context" diff --git a/upgrade-agent/server_test.go b/upgrade-agent/internal/server/server_test.go similarity index 99% rename from upgrade-agent/server_test.go rename to upgrade-agent/internal/server/server_test.go index db15d6c6d..6f1a73f2c 100644 --- a/upgrade-agent/server_test.go +++ b/upgrade-agent/internal/server/server_test.go @@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ -package upgradeagent +package server import ( "context" diff --git a/verify/README.md b/verify/README.md new file mode 100644 index 000000000..73e76e779 --- /dev/null +++ b/verify/README.md @@ -0,0 +1,5 @@ +# Verification service + +Constellation's verification service allows a user to request an attestation statement from the cluster. + +The service offers a gRPC and a REST API to retrieve the attestation from. diff --git a/verify/server/server.go b/verify/server/server.go index e1c23d9fe..85c26ff69 100644 --- a/verify/server/server.go +++ b/verify/server/server.go @@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package server implements the gRPC and REST endpoints for retrieving attestation statements. package server import ( diff --git a/verify/verifyproto/verify.proto b/verify/verifyproto/verify.proto index 794f3809d..bafb0c298 100644 --- a/verify/verifyproto/verify.proto +++ b/verify/verifyproto/verify.proto @@ -5,14 +5,17 @@ package verify; option go_package = "github.com/edgelesssys/constellation/v2/verify/verifyproto"; service API { + // GetAttestation returns an attestation for the given user data and nonce. rpc GetAttestation(GetAttestationRequest) returns (GetAttestationResponse); } message GetAttestationRequest { // bytes user_data = 1; removed + // nonce is a random nonce to prevent replay attacks. bytes nonce = 2; } message GetAttestationResponse { + // attestation is the attestation for the given user data and nonce. bytes attestation = 1; }