dev-docs: Go package docs (#958)

* Remove unused package

* Add Go package docs to most packages

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
Signed-off-by: Fabian Kammel <fk@edgeless.systems>
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 <fk@edgeless.systems>
This commit is contained in:
Daniel Weiße 2023-01-19 15:57:50 +01:00 committed by GitHub
parent b7740723ac
commit 690b50b29d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
118 changed files with 735 additions and 750 deletions

View File

@ -9,7 +9,7 @@ The bootstrapper has two active components:
## Init Flow ## 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) 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 for a description of the initialization protocol. The client that talks to this server
is part of Constellation's CLI. is part of Constellation's CLI.

View File

@ -16,7 +16,7 @@ import (
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/helm" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/helm"
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes"
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/k8sapi" "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/bootstrapper/internal/logging"
"github.com/edgelesssys/constellation/v2/internal/atls" "github.com/edgelesssys/constellation/v2/internal/atls"
"github.com/edgelesssys/constellation/v2/internal/attestation/aws" "github.com/edgelesssys/constellation/v2/internal/attestation/aws"

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package certificate provides functions to create a certificate request and matching private key.
package certificate package certificate
import ( import (
@ -11,9 +12,32 @@ import (
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix"
"encoding/pem" "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. // GetCertificateRequest returns a certificate request and matching private key.
func GetCertificateRequest(csrTemplate *x509.CertificateRequest) (certificateRequest []byte, privateKey []byte, err error) { func GetCertificateRequest(csrTemplate *x509.CertificateRequest) (certificateRequest []byte, privateKey []byte, err error) {
privK, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) privK, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package clean provides functionality to stop a list of services gracefully and synchronously.
package clean package clean
import ( import (

View File

@ -4,6 +4,12 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package diskencryption
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package helm is used to install Constellation microservices and other services during cluster initialization.
package helm package helm
import ( import (

View File

@ -4,6 +4,17 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package initserver
import ( import (

View File

@ -4,6 +4,17 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package joinclient
import ( import (
@ -16,8 +27,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/certificate"
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/diskencryption" "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/attestation"
"github.com/edgelesssys/constellation/v2/internal/cloud/metadata" "github.com/edgelesssys/constellation/v2/internal/cloud/metadata"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
@ -201,7 +212,7 @@ func (c *JoinClient) join(serviceEndpoint string) error {
ctx, cancel := c.timeoutCtx() ctx, cancel := c.timeoutCtx()
defer cancel() defer cancel()
certificateRequest, kubeletKey, err := kubelet.GetCertificateRequest(c.nodeName, c.validIPs) certificateRequest, kubeletKey, err := certificate.GetKubeletCertificateRequest(c.nodeName, c.validIPs)
if err != nil { if err != nil {
return err return err
} }
@ -267,10 +278,10 @@ func (c *JoinClient) startNodeAndJoin(ticket *joinproto.IssueJoinTicketResponse,
return fmt.Errorf("writing control plane files: %w", err) 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) 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) return fmt.Errorf("writing kubelet key: %w", err)
} }

View File

@ -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)
}

View File

@ -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

View File

@ -22,7 +22,7 @@ import (
"strings" "strings"
"time" "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/bootstrapper/internal/kubernetes/k8sapi/resources"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/role" "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. // 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 { func (k *KubernetesUtil) createSignedKubeletCert(nodeName string, ips []net.IP) error {
// Create CSR // Create CSR
certRequestRaw, kubeletKey, err := kubelet.GetCertificateRequest(nodeName, ips) certRequestRaw, kubeletKey, err := certificate.GetKubeletCertificateRequest(nodeName, ips)
if err != nil { if err != nil {
return err 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 return err
} }
@ -399,7 +399,7 @@ func (k *KubernetesUtil) createSignedKubeletCert(nodeName string, ips []net.IP)
Bytes: certRaw, 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. // createSignedKonnectivityCert manually creates a Kubernetes CA signed certificate for the Konnectivity server.

View File

@ -9,7 +9,7 @@ package k8sapi
import ( import (
"path/filepath" "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/constants"
"github.com/edgelesssys/constellation/v2/internal/kubernetes" "github.com/edgelesssys/constellation/v2/internal/kubernetes"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@ -166,8 +166,8 @@ func (c *KubdeadmConfiguration) InitConfiguration(externalCloudProvider bool, cl
Effect: corev1.TaintEffectPreferNoSchedule, Effect: corev1.TaintEffectPreferNoSchedule,
}, },
}, },
TLSCertFile: kubelet.CertificateFilename, TLSCertFile: certificate.CertificateFilename,
TLSPrivateKeyFile: kubelet.KeyFilename, TLSPrivateKeyFile: certificate.KeyFilename,
}, },
} }
} }

View File

@ -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

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package kubernetes provides functionality to bootstrap a Kubernetes cluster, or join an exiting one.
package kubernetes package kubernetes
import ( import (
@ -19,7 +20,7 @@ import (
"time" "time"
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/k8sapi" "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/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/azureshared" "github.com/edgelesssys/constellation/v2/internal/cloud/azureshared"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"

View File

@ -14,7 +14,7 @@ import (
"testing" "testing"
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/k8sapi" "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/cloud/metadata"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/deploy/helm" "github.com/edgelesssys/constellation/v2/internal/deploy/helm"

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package kubewaiter is used to wait for the Kubernetes API to be available.
package kubewaiter package kubewaiter
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package logging provides an interface for logging information to a non-confidential destination
package logging package logging
import "io" import "io"

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package nodelock handles locking operations on the node.
package nodelock package nodelock
import ( import (

View File

@ -4,6 +4,11 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package cmd
import ( import (

View File

@ -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

12
cli/internal/cmd/cmd.go Normal file
View File

@ -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

View File

@ -4,6 +4,11 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package image
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package libvirt is used to start and stop containerized libvirt instances.
package libvirt package libvirt
import ( import (

View File

@ -4,6 +4,14 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package terraform
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package cryptmapper provides a wrapper around libcryptsetup to manage dm-crypt volumes for CSI drivers.
package cryptmapper package cryptmapper
import ( import (

View File

@ -141,6 +141,26 @@ the `<REPOSITORY>/.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. 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.
<details>
<summary>View installation instructions</summary>
```shell
CONSTELLATION_DIR=</path/to/your/local/report>
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
</details>
## PR conventions ## 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). 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).

View File

@ -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 ## Testing
@ -9,6 +14,6 @@ The integration test requires root privileges since it uses dm-crypt.
Build and run the test: Build and run the test:
```bash ```bash
go test -c -tags=integration ./disk-mapper/test/ go test -c -tags=integration ./disk-mapper/internal/test/
sudo ./test.test sudo ./test.test
``` ```

View File

@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package mapper
import ( import (
@ -24,7 +33,7 @@ import (
// https://stackoverflow.com/questions/30553386/cryptsetup-backend-safe-with-multithreading // https://stackoverflow.com/questions/30553386/cryptsetup-backend-safe-with-multithreading
var packageLock = sync.Mutex{} 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 { type Mapper struct {
device cryptDevice device cryptDevice
log *logger.Logger log *logger.Logger

View File

@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package recoveryserver
import ( import (

View File

@ -4,6 +4,11 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package rejoinclient
import ( import (

View File

@ -4,6 +4,11 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package setup
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package systemd configures systemd units for encrypted volumes.
package systemd package systemd
import ( import (

View File

@ -5,7 +5,7 @@ package recoverproto;
option go_package = "github.com/edgelesssys/constellation/v2/disk-mapper/recoverproto"; option go_package = "github.com/edgelesssys/constellation/v2/disk-mapper/recoverproto";
service API { 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) {} rpc Recover(RecoverMessage) returns (RecoverResponse) {}
} }

8
e2e/e2e.go Normal file
View File

@ -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

View File

@ -4,6 +4,8 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package kubectl
import ( import (

View File

@ -6,6 +6,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// End-to-end tests for our cloud load balancer functionality.
package test package test
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// aTLS provides config generation functions to bootstrap attested TLS connections.
package atls package atls
import ( import (

View File

@ -4,6 +4,28 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package attestation
import ( import (

View File

@ -1 +0,0 @@
# Amazon Web Services attestation

View File

@ -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

View File

@ -4,6 +4,19 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package azure
import ( import (

View File

@ -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).

View File

@ -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

View File

@ -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

View File

@ -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
```

View File

@ -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

View File

@ -4,6 +4,13 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package measurements
import ( import (

View File

@ -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

View File

@ -6,6 +6,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// TPM2 simulator used for unit tests.
package simulator package simulator
import ( import (

View File

@ -4,6 +4,53 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package vtpm
import ( import (

View File

@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package aws
import ( import (

View File

@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package azure
import ( import (

View File

@ -4,6 +4,26 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 <CSP>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 package cloud
const ( const (

View File

@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package gcp
import ( import (

View File

@ -4,6 +4,9 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
/*
This package provides an interface to fake a CSP API for QEMU instances.
*/
package qemu package qemu
import ( import (

View File

@ -8,6 +8,14 @@ SPDX-License-Identifier: AGPL-3.0-only
// https://github.com/siderolabs/talos/tree/master/hack/docgen // https://github.com/siderolabs/talos/tree/master/hack/docgen
// //
//go:generate docgen ./config.go ./config_doc.go Configuration //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 package config
import ( import (

View File

@ -394,7 +394,7 @@ func (_ QEMUConfig) Doc() *encoder.Doc {
func GetConfigurationDoc() *encoder.FileDoc { func GetConfigurationDoc() *encoder.FileDoc {
return &encoder.FileDoc{ return &encoder.FileDoc{
Name: "Configuration", 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{ Structs: []*encoder.Doc{
&ConfigDoc, &ConfigDoc,
&UpgradeConfigDoc, &UpgradeConfigDoc,

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package helm provides types and functions shared across services.
package helm package helm
// Release bundles all information necessary to create a helm release. // 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. // 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. // 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 { func MergeMaps(a, b map[string]any) map[string]any {
out := make(map[string]any, len(a)) out := make(map[string]any, len(a))

View File

@ -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
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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()
}

View File

@ -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)
})
}
}

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package atlscredentials handles creation of TLS credentials for attested TLS (ATLS).
package atlscredentials package atlscredentials
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package dialer
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package retry provides functions to check if a gRPC error is retryable.
package retry package retry
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package testdialer provides a fake dialer for testing.
package testdialer package testdialer
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package installer provides functionality to install binary components of supported kubernetes versions.
package installer package installer
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package config provides configuration constants for the KeyService.
package config package config
const ( const (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package aws implements a KMS backend for AWS KMS.
package aws package aws
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package azure implements KMS backends for Azure Key Vault and Azure managed HSM.
package azure package azure
import ( import (

View File

@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package cluster
import ( import (

View File

@ -4,6 +4,25 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package gcp
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package kms provides an abstract interface for Key Management Services.
package kms package kms
import ( import (

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package util provides utility functions for the KMS backends.
package util package util
import ( import (

View File

@ -4,6 +4,15 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package setup
import ( import (

View File

@ -4,6 +4,11 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package storage
import ( import (

View File

@ -4,6 +4,8 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package kubectl
import ( import (

View File

@ -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

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package license provides functions to check a user's Constellation license.
package license package license
import ( import (

View File

@ -26,15 +26,15 @@ This can also be used to add context to a single log message:
# Log Levels # 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 package logger

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package nodestate is used to persist the state of a Constellation node to disk.
package nodestate package nodestate
import ( import (

View File

@ -2,7 +2,9 @@
Copyright (c) Edgeless Systems GmbH Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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. 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. OIDs beginning with 1.3.9900 are reserved and can be used without registration.

View File

@ -4,6 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
// Package retry provides a simple interface for retrying operations.
package retry package retry
import ( import (

View File

@ -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

View File

@ -4,6 +4,11 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package versions
import ( import (

View File

@ -4,6 +4,17 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package main
import ( import (

View File

@ -4,6 +4,24 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package client
import ( import (
@ -30,20 +48,6 @@ import (
) )
// Client is the client for the versions API. // 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 { type Client struct {
config aws.Config config aws.Config
cloudfrontClient *cloudfront.Client cloudfrontClient *cloudfront.Client

Some files were not shown because too many files have changed in this diff Show More