Bootstrapper

This commit is contained in:
katexochen 2022-06-28 18:33:27 +02:00 committed by Paul Meyer
parent 4f93f8f45c
commit f79674cbb8
36 changed files with 698 additions and 256 deletions

View file

@ -180,7 +180,7 @@ func verifyCompletion(cmd *cobra.Command, args []string, toComplete string) ([]s
} }
type constellationVerifier struct { type constellationVerifier struct {
dialer grpcDialer dialer grpcInsecureDialer
} }
// Verify retrieves an attestation statement from the Constellation and verifies it using the validator. // Verify retrieves an attestation statement from the Constellation and verifies it using the validator.
@ -215,6 +215,6 @@ type verifyClient interface {
Verify(ctx context.Context, endpoint string, req *verifyproto.GetAttestationRequest, validator atls.Validator) error Verify(ctx context.Context, endpoint string, req *verifyproto.GetAttestationRequest, validator atls.Validator) error
} }
type grpcDialer interface { type grpcInsecureDialer interface {
DialInsecure(ctx context.Context, endpoint string) (conn *grpc.ClientConn, err error) DialInsecure(ctx context.Context, endpoint string) (conn *grpc.ClientConn, err error)
} }

View file

@ -1,9 +0,0 @@
package cmd
import wgquick "github.com/nmiculinic/wg-quick-go"
type vpnHandler interface {
Create(coordinatorPubKey string, coordinatorPubIP string, clientPrivKey string, clientVPNIP string, mtu int) (*wgquick.Config, error)
Apply(*wgquick.Config) error
Marshal(*wgquick.Config) ([]byte, error)
}

View file

@ -1,25 +0,0 @@
package cmd
import wgquick "github.com/nmiculinic/wg-quick-go"
type stubVPNHandler struct {
configured bool
marshalRes string
createErr error
applyErr error
marshalErr error
}
func (c *stubVPNHandler) Create(coordinatorPubKey string, coordinatorPubIP string, clientPrivKey string, clientVPNIP string, mtu int) (*wgquick.Config, error) {
return &wgquick.Config{}, c.createErr
}
func (c *stubVPNHandler) Apply(*wgquick.Config) error {
c.configured = true
return c.applyErr
}
func (c *stubVPNHandler) Marshal(*wgquick.Config) ([]byte, error) {
return []byte(c.marshalRes), c.marshalErr
}

View file

@ -242,11 +242,6 @@ func (m *Metadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
return *resp.Properties.IPAddress, nil return *resp.Properties.IPAddress, nil
} }
// SetVPNIP stores the internally used VPN IP in cloud provider metadata (not required on azure).
func (m *Metadata) SetVPNIP(ctx context.Context, vpnIP string) error {
return nil
}
// Supported is used to determine if metadata API is implemented for this cloud provider. // Supported is used to determine if metadata API is implemented for this cloud provider.
func (m *Metadata) Supported() bool { func (m *Metadata) Supported() bool {
return true return true

View file

@ -488,12 +488,6 @@ func TestGetLoadBalancerIP(t *testing.T) {
} }
} }
func TestSetVPNIP(t *testing.T) {
assert := assert.New(t)
metadata := Metadata{}
assert.NoError(metadata.SetVPNIP(context.Background(), "192.0.2.0"))
}
func TestMetadataSupported(t *testing.T) { func TestMetadataSupported(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
metadata := Metadata{} metadata := Metadata{}

View file

@ -2,7 +2,6 @@ package gcp
import ( import (
"github.com/edgelesssys/constellation/coordinator/internal/kubernetes/k8sapi/resources" "github.com/edgelesssys/constellation/coordinator/internal/kubernetes/k8sapi/resources"
"github.com/edgelesssys/constellation/internal/cloud/metadata"
k8s "k8s.io/api/core/v1" k8s "k8s.io/api/core/v1"
) )
@ -15,7 +14,7 @@ func (a *Autoscaler) Name() string {
} }
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler. // Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
func (a *Autoscaler) Secrets(instance metadata.InstanceMetadata, cloudServiceAccountURI string) (resources.Secrets, error) { func (a *Autoscaler) Secrets(instance, cloudServiceAccountURI string) (resources.Secrets, error) {
return resources.Secrets{}, nil return resources.Secrets{}, nil
} }

View file

@ -3,7 +3,6 @@ package gcp
import ( import (
"testing" "testing"
"github.com/edgelesssys/constellation/internal/cloud/metadata"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -12,7 +11,7 @@ func TestTrivialAutoscalerFunctions(t *testing.T) {
autoscaler := Autoscaler{} autoscaler := Autoscaler{}
assert.NotEmpty(autoscaler.Name()) assert.NotEmpty(autoscaler.Name())
assert.Empty(autoscaler.Secrets(metadata.InstanceMetadata{}, "")) assert.Empty(autoscaler.Secrets("", ""))
assert.NotEmpty(autoscaler.Volumes()) assert.NotEmpty(autoscaler.Volumes())
assert.NotEmpty(autoscaler.VolumeMounts()) assert.NotEmpty(autoscaler.VolumeMounts())
assert.NotEmpty(autoscaler.Env()) assert.NotEmpty(autoscaler.Env())

View file

@ -104,6 +104,11 @@ func (m *Metadata) GetSubnetworkCIDR(ctx context.Context) (string, error) {
return m.api.RetrieveSubnetworkAliasCIDR(ctx, project, zone, instanceName) return m.api.RetrieveSubnetworkAliasCIDR(ctx, project, zone, instanceName)
} }
// SupportsLoadBalancer returns true if the cloud provider supports load balancers.
func (m *Metadata) SupportsLoadBalancer() bool {
return true
}
// GetLoadBalancerIP returns the IP of the load balancer. // GetLoadBalancerIP returns the IP of the load balancer.
func (m *Metadata) GetLoadBalancerIP(ctx context.Context) (string, error) { func (m *Metadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
project, err := m.api.RetrieveProjectID() project, err := m.api.RetrieveProjectID()
@ -116,3 +121,8 @@ func (m *Metadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
} }
return m.api.RetrieveLoadBalancerIP(ctx, project, zone) return m.api.RetrieveLoadBalancerIP(ctx, project, zone)
} }
// Supported is used to determine if metadata API is implemented for this cloud provider.
func (m *Metadata) Supported() bool {
return true
}

View file

@ -2,7 +2,6 @@ package qemu
import ( import (
"github.com/edgelesssys/constellation/coordinator/internal/kubernetes/k8sapi/resources" "github.com/edgelesssys/constellation/coordinator/internal/kubernetes/k8sapi/resources"
"github.com/edgelesssys/constellation/internal/cloud/metadata"
k8s "k8s.io/api/core/v1" k8s "k8s.io/api/core/v1"
) )
@ -15,7 +14,7 @@ func (a Autoscaler) Name() string {
} }
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler. // Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
func (a Autoscaler) Secrets(instance metadata.InstanceMetadata, cloudServiceAccountURI string) (resources.Secrets, error) { func (a Autoscaler) Secrets(providerID, cloudServiceAccountURI string) (resources.Secrets, error) {
return resources.Secrets{}, nil return resources.Secrets{}, nil
} }

View file

@ -39,7 +39,7 @@ func (c CloudControllerManager) ConfigMaps(instance metadata.InstanceMetadata) (
// Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager. // Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager.
// Reference: https://kubernetes.io/docs/concepts/configuration/secret/ . // Reference: https://kubernetes.io/docs/concepts/configuration/secret/ .
func (c CloudControllerManager) Secrets(ctx context.Context, instance metadata.InstanceMetadata, cloudServiceAccountURI string) (resources.Secrets, error) { func (c CloudControllerManager) Secrets(ctx context.Context, providerID, cloudServiceAccountURI string) (resources.Secrets, error) {
return resources.Secrets{}, nil return resources.Secrets{}, nil
} }

View file

@ -8,7 +8,6 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/internal/cloud/metadata" "github.com/edgelesssys/constellation/internal/cloud/metadata"
) )
@ -61,16 +60,6 @@ func (m Metadata) GetInstance(ctx context.Context, providerID string) (metadata.
return metadata.InstanceMetadata{}, errors.New("instance not found") return metadata.InstanceMetadata{}, errors.New("instance not found")
} }
// SignalRole signals the constellation role via cloud provider metadata (if supported by the CSP and deployment type, otherwise does nothing).
func (m Metadata) SignalRole(ctx context.Context, role role.Role) error {
return nil
}
// SetVPNIP stores the internally used VPN IP in cloud provider metadata (if supported and required for autoscaling by the CSP, otherwise does nothing).
func (m Metadata) SetVPNIP(ctx context.Context, vpnIP string) error {
return nil
}
// SupportsLoadBalancer returns true if the cloud provider supports load balancers. // SupportsLoadBalancer returns true if the cloud provider supports load balancers.
func (m Metadata) SupportsLoadBalancer() bool { func (m Metadata) SupportsLoadBalancer() bool {
return false return false

View file

@ -12,8 +12,7 @@ import (
azurecloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/azure" azurecloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/azure"
gcpcloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/gcp" gcpcloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/gcp"
qemucloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/qemu" qemucloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/qemu"
"github.com/edgelesssys/constellation/coordinator/config" "github.com/edgelesssys/constellation/coordinator/internal/joinclient"
"github.com/edgelesssys/constellation/coordinator/core"
"github.com/edgelesssys/constellation/coordinator/internal/kubernetes" "github.com/edgelesssys/constellation/coordinator/internal/kubernetes"
"github.com/edgelesssys/constellation/coordinator/internal/kubernetes/k8sapi" "github.com/edgelesssys/constellation/coordinator/internal/kubernetes/k8sapi"
"github.com/edgelesssys/constellation/coordinator/internal/kubernetes/k8sapi/kubectl" "github.com/edgelesssys/constellation/coordinator/internal/kubernetes/k8sapi/kubectl"
@ -34,12 +33,18 @@ import (
const ( const (
defaultIP = "0.0.0.0" defaultIP = "0.0.0.0"
defaultPort = "9000" defaultPort = "9000"
// ConstellationCSP is the Cloud Service Provider Constellation is running on.
constellationCSP = "CONSTEL_CSP"
) )
func main() { func main() {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
var bindIP, bindPort string var bindIP, bindPort string
var clusterInitJoiner ClusterInitJoiner var clusterInitJoiner clusterInitJoiner
var coreMetadata core.ProviderMetadata var metadataAPI joinclient.MetadataAPI
var cloudLogger logging.CloudLogger var cloudLogger logging.CloudLogger
cfg := zap.NewDevelopmentConfig() cfg := zap.NewDevelopmentConfig()
@ -62,7 +67,7 @@ func main() {
var openTPM vtpm.TPMOpenFunc var openTPM vtpm.TPMOpenFunc
var fs afero.Fs var fs afero.Fs
switch strings.ToLower(os.Getenv(config.ConstellationCSP)) { switch strings.ToLower(os.Getenv(constellationCSP)) {
case "aws": case "aws":
panic("AWS cloud provider currently unsupported") panic("AWS cloud provider currently unsupported")
case "gcp": case "gcp":
@ -74,20 +79,20 @@ func main() {
issuer = gcp.NewIssuer() issuer = gcp.NewIssuer()
gcpClient, err := gcpcloud.NewClient(context.Background()) gcpClient, err := gcpcloud.NewClient(ctx)
if err != nil { if err != nil {
log.Fatalf("failed to create GCP client: %v\n", err) log.Fatalf("failed to create GCP client: %v\n", err)
} }
metadata := gcpcloud.New(gcpClient) metadata := gcpcloud.New(gcpClient)
descr, err := metadata.Self(context.Background()) descr, err := metadata.Self(ctx)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
cloudLogger, err = gcpcloud.NewLogger(context.Background(), descr.ProviderID, "constellation-boot-log") cloudLogger, err = gcpcloud.NewLogger(ctx, descr.ProviderID, "constellation-boot-log")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
coreMetadata = metadata metadataAPI = metadata
pcrsJSON, err := json.Marshal(pcrs) pcrsJSON, err := json.Marshal(pcrs)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -108,15 +113,15 @@ func main() {
issuer = azure.NewIssuer() issuer = azure.NewIssuer()
metadata, err := azurecloud.NewMetadata(context.Background()) metadata, err := azurecloud.NewMetadata(ctx)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
cloudLogger, err = azurecloud.NewLogger(context.Background(), metadata) cloudLogger, err = azurecloud.NewLogger(ctx, metadata)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
coreMetadata = metadata metadataAPI = metadata
pcrsJSON, err := json.Marshal(pcrs) pcrsJSON, err := json.Marshal(pcrs)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -148,7 +153,7 @@ func main() {
"qemu", k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New(), &qemucloud.CloudControllerManager{}, "qemu", k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New(), &qemucloud.CloudControllerManager{},
&qemucloud.CloudNodeManager{}, &qemucloud.Autoscaler{}, metadata, pcrsJSON, &qemucloud.CloudNodeManager{}, &qemucloud.Autoscaler{}, metadata, pcrsJSON,
) )
coreMetadata = metadata metadataAPI = metadata
bindIP = defaultIP bindIP = defaultIP
bindPort = defaultPort bindPort = defaultPort
@ -156,8 +161,8 @@ func main() {
fs = afero.NewOsFs() fs = afero.NewOsFs()
default: default:
issuer = atls.NewFakeIssuer(oid.Dummy{}) issuer = atls.NewFakeIssuer(oid.Dummy{})
clusterInitJoiner = &core.ClusterFake{} clusterInitJoiner = &clusterFake{}
coreMetadata = &core.ProviderMetadataFake{} metadataAPI = &providerMetadataFake{}
cloudLogger = &logging.NopLogger{} cloudLogger = &logging.NopLogger{}
bindIP = defaultIP bindIP = defaultIP
bindPort = defaultPort bindPort = defaultPort
@ -170,6 +175,6 @@ func main() {
fileHandler := file.NewHandler(fs) fileHandler := file.NewHandler(fs)
run(issuer, openTPM, fileHandler, clusterInitJoiner, run(issuer, openTPM, fileHandler, clusterInitJoiner,
coreMetadata, bindIP, metadataAPI, bindIP,
bindPort, zapLoggerCore, cloudLogger, fs) bindPort, zapLoggerCore, cloudLogger, fs)
} }

View file

@ -2,23 +2,23 @@ package main
import ( import (
"net" "net"
"sync"
"github.com/edgelesssys/constellation/coordinator/core"
"github.com/edgelesssys/constellation/coordinator/internal/initserver" "github.com/edgelesssys/constellation/coordinator/internal/initserver"
"github.com/edgelesssys/constellation/coordinator/internal/joinclient" "github.com/edgelesssys/constellation/coordinator/internal/joinclient"
"github.com/edgelesssys/constellation/coordinator/internal/logging" "github.com/edgelesssys/constellation/coordinator/internal/logging"
"github.com/edgelesssys/constellation/coordinator/internal/nodelock"
"github.com/edgelesssys/constellation/internal/attestation/vtpm" "github.com/edgelesssys/constellation/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/internal/file" "github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/grpc/dialer" "github.com/edgelesssys/constellation/internal/grpc/dialer"
"github.com/edgelesssys/constellation/internal/oid"
"github.com/spf13/afero" "github.com/spf13/afero"
"go.uber.org/zap" "go.uber.org/zap"
) )
var version = "0.0.0" var version = "0.0.0"
func run(issuer core.QuoteIssuer, tpm vtpm.TPMOpenFunc, fileHandler file.Handler, func run(issuer quoteIssuer, tpm vtpm.TPMOpenFunc, fileHandler file.Handler,
kube ClusterInitJoiner, metadata core.ProviderMetadata, kube clusterInitJoiner, metadata joinclient.MetadataAPI,
bindIP, bindPort string, logger *zap.Logger, bindIP, bindPort string, logger *zap.Logger,
cloudLogger logging.CloudLogger, fs afero.Fs, cloudLogger logging.CloudLogger, fs afero.Fs,
) { ) {
@ -40,7 +40,7 @@ func run(issuer core.QuoteIssuer, tpm vtpm.TPMOpenFunc, fileHandler file.Handler
return return
} }
nodeLock := &sync.Mutex{} nodeLock := nodelock.New()
initServer := initserver.New(nodeLock, kube, logger) initServer := initserver.New(nodeLock, kube, logger)
dialer := dialer.New(issuer, nil, &net.Dialer{}) dialer := dialer.New(issuer, nil, &net.Dialer{})
@ -54,8 +54,14 @@ func run(issuer core.QuoteIssuer, tpm vtpm.TPMOpenFunc, fileHandler file.Handler
} }
} }
type ClusterInitJoiner interface { type clusterInitJoiner interface {
joinclient.ClusterJoiner joinclient.ClusterJoiner
initserver.ClusterInitializer initserver.ClusterInitializer
StartKubelet() error StartKubelet() error
} }
type quoteIssuer interface {
oid.Getter
// Issue issues a quote for remote attestation for a given message
Issue(userData []byte, nonce []byte) (quote []byte, err error)
}

View file

@ -0,0 +1,58 @@
package main
import (
"context"
"github.com/edgelesssys/constellation/coordinator/internal/kubernetes"
"github.com/edgelesssys/constellation/coordinator/role"
attestationtypes "github.com/edgelesssys/constellation/internal/attestation/types"
"github.com/edgelesssys/constellation/internal/cloud/metadata"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
)
// ClusterFake behaves like a real cluster, but does not actually initialize or join Kubernetes.
type clusterFake struct{}
// InitCluster fakes bootstrapping a new cluster with the current node being the master, returning the arguments required to join the cluster.
func (c *clusterFake) InitCluster(context.Context, []string, string, string, attestationtypes.ID, kubernetes.KMSConfig, map[string]string,
) ([]byte, error) {
return []byte{}, nil
}
// JoinCluster will fake joining the current node to an existing cluster.
func (c *clusterFake) JoinCluster(context.Context, *kubeadm.BootstrapTokenDiscovery, string, role.Role) error {
return nil
}
// StartKubelet starts the kubelet service.
func (c *clusterFake) StartKubelet() error {
return nil
}
type providerMetadataFake struct{}
func (f *providerMetadataFake) List(ctx context.Context) ([]metadata.InstanceMetadata, error) {
self, err := f.Self(ctx)
return []metadata.InstanceMetadata{self}, err
}
func (f *providerMetadataFake) Self(ctx context.Context) (metadata.InstanceMetadata, error) {
return metadata.InstanceMetadata{
Name: "instanceName",
ProviderID: "fake://instance-id",
Role: role.Unknown,
PrivateIPs: []string{"192.0.2.1"},
}, nil
}
func (f *providerMetadataFake) SignalRole(ctx context.Context, role role.Role) error {
return nil
}
func (f *providerMetadataFake) SetVPNIP(ctx context.Context, vpnIP string) error {
return nil
}
func (f *providerMetadataFake) Supported() bool {
return true
}

View file

@ -1,7 +0,0 @@
package config
// ConstellationCSP is the Cloud Service Provider Constellation is running on.
const ConstellationCSP = "CONSTEL_CSP"
// RNGLengthDefault is the number of bytes used for generating nonces.
const RNGLengthDefault = 32

View file

@ -5,16 +5,16 @@ import (
"fmt" "fmt"
"net" "net"
"strings" "strings"
"sync"
"github.com/edgelesssys/constellation/coordinator/config"
"github.com/edgelesssys/constellation/coordinator/initproto" "github.com/edgelesssys/constellation/coordinator/initproto"
"github.com/edgelesssys/constellation/coordinator/internal/diskencryption" "github.com/edgelesssys/constellation/coordinator/internal/diskencryption"
"github.com/edgelesssys/constellation/coordinator/internal/kubernetes" "github.com/edgelesssys/constellation/coordinator/internal/kubernetes"
"github.com/edgelesssys/constellation/coordinator/internal/nodelock"
"github.com/edgelesssys/constellation/coordinator/nodestate" "github.com/edgelesssys/constellation/coordinator/nodestate"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/coordinator/util" "github.com/edgelesssys/constellation/coordinator/util"
attestationtypes "github.com/edgelesssys/constellation/internal/attestation/types" attestationtypes "github.com/edgelesssys/constellation/internal/attestation/types"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file" "github.com/edgelesssys/constellation/internal/file"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
@ -25,25 +25,28 @@ import (
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
) )
// Server is the initialization server, which is started on each node.
// The server handles initialization calls from the CLI and initializes the
// Kubernetes cluster.
type Server struct { type Server struct {
nodeLock *sync.Mutex nodeLock *nodelock.Lock
initializer ClusterInitializer
kube ClusterInitializer disk encryptedDisk
disk EncryptedDisk
fileHandler file.Handler fileHandler file.Handler
grpcServer *grpc.Server grpcServer serveStopper
logger *zap.Logger logger *zap.Logger
initproto.UnimplementedAPIServer initproto.UnimplementedAPIServer
} }
func New(nodeLock *sync.Mutex, kube ClusterInitializer, logger *zap.Logger) *Server { // New creates a new initialization server.
func New(lock *nodelock.Lock, kube ClusterInitializer, logger *zap.Logger) *Server {
logger = logger.Named("initServer") logger = logger.Named("initServer")
server := &Server{ server := &Server{
nodeLock: nodeLock, nodeLock: lock,
disk: diskencryption.New(), disk: diskencryption.New(),
kube: kube, initializer: kube,
logger: logger, logger: logger,
} }
@ -74,8 +77,14 @@ func (s *Server) Serve(ip, port string) error {
return s.grpcServer.Serve(lis) return s.grpcServer.Serve(lis)
} }
// Init initializes the cluster.
func (s *Server) Init(ctx context.Context, req *initproto.InitRequest) (*initproto.InitResponse, error) { func (s *Server) Init(ctx context.Context, req *initproto.InitRequest) (*initproto.InitResponse, error) {
if ok := s.nodeLock.TryLock(); !ok { if ok := s.nodeLock.TryLockOnce(); !ok {
// The join client seems to already have a connection to an
// existing join service. At this point, any further call to
// init does not make sense, so we just stop.
//
// The server stops itself after the current call is done.
go s.grpcServer.GracefulStop() go s.grpcServer.GracefulStop()
return nil, status.Error(codes.FailedPrecondition, "node is already being activated") return nil, status.Error(codes.FailedPrecondition, "node is already being activated")
} }
@ -98,7 +107,7 @@ func (s *Server) Init(ctx context.Context, req *initproto.InitRequest) (*initpro
return nil, status.Errorf(codes.Internal, "persisting node state: %s", err) return nil, status.Errorf(codes.Internal, "persisting node state: %s", err)
} }
kubeconfig, err := s.kube.InitCluster(ctx, kubeconfig, err := s.initializer.InitCluster(ctx,
req.AutoscalingNodeGroups, req.AutoscalingNodeGroups,
req.CloudServiceAccountUri, req.CloudServiceAccountUri,
req.KubernetesVersion, req.KubernetesVersion,
@ -145,13 +154,13 @@ func (s *Server) setupDisk(masterSecret []byte) error {
} }
func (s *Server) deriveAttestationID(masterSecret []byte) (attestationtypes.ID, error) { func (s *Server) deriveAttestationID(masterSecret []byte) (attestationtypes.ID, error) {
clusterID, err := util.GenerateRandomBytes(config.RNGLengthDefault) clusterID, err := util.GenerateRandomBytes(constants.RNGLengthDefault)
if err != nil { if err != nil {
return attestationtypes.ID{}, err return attestationtypes.ID{}, err
} }
// TODO: Choose a way to salt the key derivation // TODO: Choose a way to salt the key derivation
ownerID, err := util.DeriveKey(masterSecret, []byte("Constellation"), []byte("id"), config.RNGLengthDefault) ownerID, err := util.DeriveKey(masterSecret, []byte("Constellation"), []byte("id"), constants.RNGLengthDefault)
if err != nil { if err != nil {
return attestationtypes.ID{}, err return attestationtypes.ID{}, err
} }
@ -167,20 +176,21 @@ func sshProtoKeysToMap(keys []*initproto.SSHUserKey) map[string]string {
return keyMap return keyMap
} }
// ClusterInitializer has the ability to initialize a cluster.
type ClusterInitializer interface { type ClusterInitializer interface {
// InitCluster initializes a new Kubernetes cluster.
InitCluster( InitCluster(
ctx context.Context, ctx context.Context,
autoscalingNodeGroups []string, autoscalingNodeGroups []string,
cloudServiceAccountURI string, cloudServiceAccountURI string,
kubernetesVersion string, k8sVersion string,
id attestationtypes.ID, id attestationtypes.ID,
config kubernetes.KMSConfig, kmsConfig kubernetes.KMSConfig,
sshUserKeys map[string]string, sshUserKeys map[string]string,
) ([]byte, error) ) ([]byte, error)
} }
// EncryptedDisk manages the encrypted state disk. type encryptedDisk interface {
type EncryptedDisk interface {
// Open prepares the underlying device for disk operations. // Open prepares the underlying device for disk operations.
Open() error Open() error
// Close closes the underlying device. // Close closes the underlying device.
@ -190,3 +200,10 @@ type EncryptedDisk interface {
// UpdatePassphrase switches the initial random passphrase of the encrypted disk to a permanent passphrase. // UpdatePassphrase switches the initial random passphrase of the encrypted disk to a permanent passphrase.
UpdatePassphrase(passphrase string) error UpdatePassphrase(passphrase string) error
} }
type serveStopper interface {
// Serve starts the server.
Serve(lis net.Listener) error
// GracefulStop stops the server and blocks until all requests are done.
GracefulStop()
}

View file

@ -0,0 +1,238 @@
package initserver
import (
"context"
"errors"
"net"
"testing"
"time"
"github.com/edgelesssys/constellation/coordinator/initproto"
"github.com/edgelesssys/constellation/coordinator/internal/kubernetes"
"github.com/edgelesssys/constellation/coordinator/internal/nodelock"
attestationtypes "github.com/edgelesssys/constellation/internal/attestation/types"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
)
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)
}
func TestNew(t *testing.T) {
assert := assert.New(t)
server := New(nodelock.New(), &stubClusterInitializer{}, zap.NewNop())
assert.NotNil(server)
assert.NotNil(server.logger)
assert.NotNil(server.nodeLock)
assert.NotNil(server.initializer)
assert.NotNil(server.grpcServer)
assert.NotNil(server.fileHandler)
assert.NotNil(server.disk)
}
func TestInit(t *testing.T) {
someErr := errors.New("failed")
lockedNodeLock := nodelock.New()
require.True(t, lockedNodeLock.TryLockOnce())
testCases := map[string]struct {
nodeLock *nodelock.Lock
initializer ClusterInitializer
disk encryptedDisk
fileHandler file.Handler
req *initproto.InitRequest
wantErr bool
wantShutdown bool
}{
"successful init": {
nodeLock: nodelock.New(),
initializer: &stubClusterInitializer{},
disk: &stubDisk{},
fileHandler: file.NewHandler(afero.NewMemMapFs()),
req: &initproto.InitRequest{},
},
"node locked": {
nodeLock: lockedNodeLock,
initializer: &stubClusterInitializer{},
disk: &stubDisk{},
fileHandler: file.NewHandler(afero.NewMemMapFs()),
req: &initproto.InitRequest{},
wantErr: true,
wantShutdown: true,
},
"disk open error": {
nodeLock: nodelock.New(),
initializer: &stubClusterInitializer{},
disk: &stubDisk{openErr: someErr},
fileHandler: file.NewHandler(afero.NewMemMapFs()),
req: &initproto.InitRequest{},
wantErr: true,
},
"disk uuid error": {
nodeLock: nodelock.New(),
initializer: &stubClusterInitializer{},
disk: &stubDisk{uuidErr: someErr},
fileHandler: file.NewHandler(afero.NewMemMapFs()),
req: &initproto.InitRequest{},
wantErr: true,
},
"disk update passphrase error": {
nodeLock: nodelock.New(),
initializer: &stubClusterInitializer{},
disk: &stubDisk{updatePassphraseErr: someErr},
fileHandler: file.NewHandler(afero.NewMemMapFs()),
req: &initproto.InitRequest{},
wantErr: true,
},
"write state file error": {
nodeLock: nodelock.New(),
initializer: &stubClusterInitializer{},
disk: &stubDisk{},
fileHandler: file.NewHandler(afero.NewReadOnlyFs(afero.NewMemMapFs())),
req: &initproto.InitRequest{},
wantErr: true,
},
"initialize cluster error": {
nodeLock: nodelock.New(),
initializer: &stubClusterInitializer{initClusterErr: someErr},
disk: &stubDisk{},
fileHandler: file.NewHandler(afero.NewMemMapFs()),
req: &initproto.InitRequest{},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
serveStopper := newStubServeStopper()
server := &Server{
nodeLock: tc.nodeLock,
initializer: tc.initializer,
disk: tc.disk,
fileHandler: tc.fileHandler,
logger: zaptest.NewLogger(t),
grpcServer: serveStopper,
}
kubeconfig, err := server.Init(context.Background(), tc.req)
if tc.wantErr {
assert.Error(err)
if tc.wantShutdown {
select {
case <-serveStopper.shutdownCalled:
case <-time.After(time.Second):
t.Fatal("grpc server did not shut down")
}
}
return
}
assert.NoError(err)
assert.NotNil(kubeconfig)
assert.False(server.nodeLock.TryLockOnce()) // lock should be locked
})
}
}
func TestSSHProtoKeysToMap(t *testing.T) {
testCases := map[string]struct {
keys []*initproto.SSHUserKey
want map[string]string
}{
"empty": {
keys: []*initproto.SSHUserKey{},
want: map[string]string{},
},
"one key": {
keys: []*initproto.SSHUserKey{
{Username: "key1", PublicKey: "key1-key"},
},
want: map[string]string{
"key1": "key1-key",
},
},
"two keys": {
keys: []*initproto.SSHUserKey{
{Username: "key1", PublicKey: "key1-key"},
{Username: "key2", PublicKey: "key2-key"},
},
want: map[string]string{
"key1": "key1-key",
"key2": "key2-key",
},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
got := sshProtoKeysToMap(tc.keys)
assert.Equal(tc.want, got)
})
}
}
type stubDisk struct {
openErr error
closeErr error
uuid string
uuidErr error
updatePassphraseErr error
updatePassphraseCalled bool
}
func (d *stubDisk) Open() error {
return d.openErr
}
func (d *stubDisk) Close() error {
return d.closeErr
}
func (d *stubDisk) UUID() (string, error) {
return d.uuid, d.uuidErr
}
func (d *stubDisk) UpdatePassphrase(string) error {
d.updatePassphraseCalled = true
return d.updatePassphraseErr
}
type stubClusterInitializer struct {
initClusterKubeconfig []byte
initClusterErr error
}
func (i *stubClusterInitializer) InitCluster(context.Context, []string, string, string, attestationtypes.ID, kubernetes.KMSConfig, map[string]string,
) ([]byte, error) {
return i.initClusterKubeconfig, i.initClusterErr
}
type stubServeStopper struct {
shutdownCalled chan struct{}
}
func newStubServeStopper() *stubServeStopper {
return &stubServeStopper{shutdownCalled: make(chan struct{}, 1)}
}
func (s *stubServeStopper) Serve(net.Listener) error {
panic("should not be called in a test")
}
func (s *stubServeStopper) GracefulStop() {
s.shutdownCalled <- struct{}{}
}

View file

@ -11,6 +11,7 @@ import (
"github.com/edgelesssys/constellation/activation/activationproto" "github.com/edgelesssys/constellation/activation/activationproto"
"github.com/edgelesssys/constellation/coordinator/internal/diskencryption" "github.com/edgelesssys/constellation/coordinator/internal/diskencryption"
"github.com/edgelesssys/constellation/coordinator/internal/nodelock"
"github.com/edgelesssys/constellation/coordinator/nodestate" "github.com/edgelesssys/constellation/coordinator/nodestate"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/internal/cloud/metadata" "github.com/edgelesssys/constellation/internal/cloud/metadata"
@ -19,7 +20,7 @@ import (
"github.com/spf13/afero" "github.com/spf13/afero"
"go.uber.org/zap" "go.uber.org/zap"
"google.golang.org/grpc" "google.golang.org/grpc"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
"k8s.io/utils/clock" "k8s.io/utils/clock"
) )
@ -30,11 +31,11 @@ const (
// JoinClient is a client for self-activation of node. // JoinClient is a client for self-activation of node.
type JoinClient struct { type JoinClient struct {
nodeLock *sync.Mutex nodeLock *nodelock.Lock
diskUUID string diskUUID string
nodeName string nodeName string
role role.Role role role.Role
disk EncryptedDisk disk encryptedDisk
fileHandler file.Handler fileHandler file.Handler
timeout time.Duration timeout time.Duration
@ -43,7 +44,7 @@ type JoinClient struct {
dialer grpcDialer dialer grpcDialer
joiner ClusterJoiner joiner ClusterJoiner
metadataAPI metadataAPI metadataAPI MetadataAPI
log *zap.Logger log *zap.Logger
@ -53,7 +54,7 @@ type JoinClient struct {
} }
// New creates a new SelfActivationClient. // New creates a new SelfActivationClient.
func New(nodeLock *sync.Mutex, dial grpcDialer, joiner ClusterJoiner, meta metadataAPI, log *zap.Logger) *JoinClient { func New(lock *nodelock.Lock, dial grpcDialer, joiner ClusterJoiner, meta MetadataAPI, log *zap.Logger) *JoinClient {
return &JoinClient{ return &JoinClient{
disk: diskencryption.New(), disk: diskencryption.New(),
fileHandler: file.NewHandler(afero.NewOsFs()), fileHandler: file.NewHandler(afero.NewOsFs()),
@ -87,11 +88,11 @@ func (c *JoinClient) Start() {
go func() { go func() {
defer ticker.Stop() defer ticker.Stop()
defer func() { c.stopDone <- struct{}{} }() defer func() { c.stopDone <- struct{}{} }()
defer c.log.Info("Client stopped")
diskUUID, err := c.GetDiskUUID() diskUUID, err := c.getDiskUUID()
if err != nil { if err != nil {
c.log.Error("Failed to get disk UUID", zap.Error(err)) c.log.Error("Failed to get disk UUID", zap.Error(err))
c.log.Error("Stopping self-activation client")
return return
} }
c.diskUUID = diskUUID c.diskUUID = diskUUID
@ -117,6 +118,9 @@ func (c *JoinClient) Start() {
if err == nil { if err == nil {
c.log.Info("Activated successfully. SelfActivationClient shut down.") c.log.Info("Activated successfully. SelfActivationClient shut down.")
return return
} else if isUnrecoverable(err) {
c.log.Error("Unrecoverable error occurred", zap.Error(err))
return
} }
c.log.Info("Activation failed for all available endpoints", zap.Error(err)) c.log.Info("Activation failed for all available endpoints", zap.Error(err))
@ -247,8 +251,18 @@ func (c *JoinClient) joinAsControlPlaneNode(ctx context.Context, client activati
func (c *JoinClient) startNodeAndJoin(ctx context.Context, diskKey, ownerID, clusterID, kubeletKey, kubeletCert []byte, endpoint, token, func (c *JoinClient) startNodeAndJoin(ctx context.Context, diskKey, ownerID, clusterID, kubeletKey, kubeletCert []byte, endpoint, token,
discoveryCACertHash, certKey string, discoveryCACertHash, certKey string,
) error { ) (retErr error) {
if ok := c.nodeLock.TryLock(); !ok { // If an error occurs in this func, the client cannot continue.
defer func() {
if retErr != nil {
retErr = unrecoverableError{retErr}
}
}()
if ok := c.nodeLock.TryLockOnce(); !ok {
// There is already a cluster initialization in progress on
// this node, so there is no need to also join the cluster,
// as the initializing node is automatically part of the cluster.
return errors.New("node is already being initialized") return errors.New("node is already being initialized")
} }
@ -310,7 +324,7 @@ func (c *JoinClient) updateDiskPassphrase(passphrase string) error {
return c.disk.UpdatePassphrase(passphrase) return c.disk.UpdatePassphrase(passphrase)
} }
func (c *JoinClient) GetDiskUUID() (string, error) { func (c *JoinClient) getDiskUUID() (string, error) {
if err := c.disk.Open(); err != nil { if err := c.disk.Open(); err != nil {
return "", fmt.Errorf("opening disk: %w", err) return "", fmt.Errorf("opening disk: %w", err)
} }
@ -343,11 +357,21 @@ func (c *JoinClient) timeoutCtx() (context.Context, context.CancelFunc) {
return context.WithTimeout(context.Background(), c.timeout) return context.WithTimeout(context.Background(), c.timeout)
} }
type unrecoverableError struct{ error }
func isUnrecoverable(err error) bool {
var ue *unrecoverableError
ok := errors.As(err, &ue)
return ok
}
type grpcDialer interface { type grpcDialer interface {
Dial(ctx context.Context, target string) (*grpc.ClientConn, error) Dial(ctx context.Context, target string) (*grpc.ClientConn, error)
} }
// ClusterJoiner has the ability to join a new node to an existing cluster.
type ClusterJoiner interface { type ClusterJoiner interface {
// JoinCluster joins a new node to an existing cluster.
JoinCluster( JoinCluster(
ctx context.Context, ctx context.Context,
args *kubeadm.BootstrapTokenDiscovery, args *kubeadm.BootstrapTokenDiscovery,
@ -356,15 +380,15 @@ type ClusterJoiner interface {
) error ) error
} }
type metadataAPI interface { // MetadataAPI provides information about the instances.
type MetadataAPI interface {
// List retrieves all instances belonging to the current constellation. // List retrieves all instances belonging to the current constellation.
List(ctx context.Context) ([]metadata.InstanceMetadata, error) List(ctx context.Context) ([]metadata.InstanceMetadata, error)
// Self retrieves the current instance. // Self retrieves the current instance.
Self(ctx context.Context) (metadata.InstanceMetadata, error) Self(ctx context.Context) (metadata.InstanceMetadata, error)
} }
// EncryptedDisk manages the encrypted state disk. type encryptedDisk interface {
type EncryptedDisk interface {
// Open prepares the underlying device for disk operations. // Open prepares the underlying device for disk operations.
Open() error Open() error
// Close closes the underlying device. // Close closes the underlying device.

View file

@ -10,6 +10,7 @@ import (
"time" "time"
"github.com/edgelesssys/constellation/activation/activationproto" "github.com/edgelesssys/constellation/activation/activationproto"
"github.com/edgelesssys/constellation/coordinator/internal/nodelock"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/internal/cloud/metadata" "github.com/edgelesssys/constellation/internal/cloud/metadata"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
@ -18,11 +19,12 @@ import (
"github.com/edgelesssys/constellation/internal/grpc/dialer" "github.com/edgelesssys/constellation/internal/grpc/dialer"
"github.com/edgelesssys/constellation/internal/grpc/testdialer" "github.com/edgelesssys/constellation/internal/grpc/testdialer"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"go.uber.org/goleak" "go.uber.org/goleak"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
"google.golang.org/grpc" "google.golang.org/grpc"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
testclock "k8s.io/utils/clock/testing" testclock "k8s.io/utils/clock/testing"
) )
@ -41,9 +43,9 @@ func TestClient(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
role role.Role role role.Role
clusterJoiner ClusterJoiner clusterJoiner *stubClusterJoiner
disk EncryptedDisk disk encryptedDisk
nodeLock *sync.Mutex nodeLock *nodelock.Lock
apiAnswers []any apiAnswers []any
}{ }{
"on node: metadata self: errors occur": { "on node: metadata self: errors occur": {
@ -57,7 +59,7 @@ func TestClient(t *testing.T) {
activateWorkerNodeAnswer{}, activateWorkerNodeAnswer{},
}, },
clusterJoiner: &stubClusterJoiner{}, clusterJoiner: &stubClusterJoiner{},
nodeLock: &sync.Mutex{}, nodeLock: nodelock.New(),
disk: &stubDisk{}, disk: &stubDisk{},
}, },
"on node: metadata self: invalid answer": { "on node: metadata self: invalid answer": {
@ -71,7 +73,7 @@ func TestClient(t *testing.T) {
activateWorkerNodeAnswer{}, activateWorkerNodeAnswer{},
}, },
clusterJoiner: &stubClusterJoiner{}, clusterJoiner: &stubClusterJoiner{},
nodeLock: &sync.Mutex{}, nodeLock: nodelock.New(),
disk: &stubDisk{}, disk: &stubDisk{},
}, },
"on node: metadata list: errors occur": { "on node: metadata list: errors occur": {
@ -85,7 +87,7 @@ func TestClient(t *testing.T) {
activateWorkerNodeAnswer{}, activateWorkerNodeAnswer{},
}, },
clusterJoiner: &stubClusterJoiner{}, clusterJoiner: &stubClusterJoiner{},
nodeLock: &sync.Mutex{}, nodeLock: nodelock.New(),
disk: &stubDisk{}, disk: &stubDisk{},
}, },
"on node: metadata list: no coordinators in answer": { "on node: metadata list: no coordinators in answer": {
@ -99,7 +101,7 @@ func TestClient(t *testing.T) {
activateWorkerNodeAnswer{}, activateWorkerNodeAnswer{},
}, },
clusterJoiner: &stubClusterJoiner{}, clusterJoiner: &stubClusterJoiner{},
nodeLock: &sync.Mutex{}, nodeLock: nodelock.New(),
disk: &stubDisk{}, disk: &stubDisk{},
}, },
"on node: aaas ActivateNode: errors": { "on node: aaas ActivateNode: errors": {
@ -114,13 +116,15 @@ func TestClient(t *testing.T) {
activateWorkerNodeAnswer{}, activateWorkerNodeAnswer{},
}, },
clusterJoiner: &stubClusterJoiner{}, clusterJoiner: &stubClusterJoiner{},
nodeLock: &sync.Mutex{}, nodeLock: nodelock.New(),
disk: &stubDisk{}, disk: &stubDisk{},
}, },
} }
for name, tc := range testCases { for name, tc := range testCases {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
assert := assert.New(t)
clock := testclock.NewFakeClock(time.Now()) clock := testclock.NewFakeClock(time.Now())
metadataAPI := newStubMetadataAPI() metadataAPI := newStubMetadataAPI()
fileHandler := file.NewHandler(afero.NewMemMapFs()) fileHandler := file.NewHandler(afero.NewMemMapFs())
@ -165,12 +169,24 @@ func TestClient(t *testing.T) {
} }
client.Stop() client.Stop()
assert.True(tc.clusterJoiner.joinClusterCalled)
assert.False(client.nodeLock.TryLockOnce()) // lock should be locked
}) })
} }
} }
func TestClientConcurrentStartStop(t *testing.T) { func TestClientConcurrentStartStop(t *testing.T) {
netDialer := testdialer.NewBufconnDialer()
dialer := dialer.New(nil, nil, netDialer)
client := &JoinClient{ client := &JoinClient{
nodeLock: nodelock.New(),
timeout: 30 * time.Second,
interval: 30 * time.Second,
dialer: dialer,
disk: &stubDisk{},
joiner: &stubClusterJoiner{},
fileHandler: file.NewHandler(afero.NewMemMapFs()),
metadataAPI: &stubRepeaterMetadataAPI{}, metadataAPI: &stubRepeaterMetadataAPI{},
clock: testclock.NewFakeClock(time.Now()), clock: testclock.NewFakeClock(time.Now()),
log: zap.NewNop(), log: zap.NewNop(),
@ -293,10 +309,12 @@ type activateControlPlaneNodeAnswer struct {
} }
type stubClusterJoiner struct { type stubClusterJoiner struct {
joinClusterCalled bool
joinClusterErr error joinClusterErr error
} }
func (j *stubClusterJoiner) JoinCluster(context.Context, *kubeadm.BootstrapTokenDiscovery, string, role.Role) error { func (j *stubClusterJoiner) JoinCluster(context.Context, *kubeadm.BootstrapTokenDiscovery, string, role.Role) error {
j.joinClusterCalled = true
return j.joinClusterErr return j.joinClusterErr
} }

View file

@ -4,7 +4,6 @@ import (
"context" "context"
"github.com/edgelesssys/constellation/coordinator/internal/kubernetes/k8sapi/resources" "github.com/edgelesssys/constellation/coordinator/internal/kubernetes/k8sapi/resources"
"github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/internal/cloud/metadata" "github.com/edgelesssys/constellation/internal/cloud/metadata"
k8s "k8s.io/api/core/v1" k8s "k8s.io/api/core/v1"
) )
@ -23,10 +22,6 @@ type ProviderMetadata interface {
GetLoadBalancerIP(ctx context.Context) (string, error) GetLoadBalancerIP(ctx context.Context) (string, error)
// GetInstance retrieves an instance using its providerID. // GetInstance retrieves an instance using its providerID.
GetInstance(ctx context.Context, providerID string) (metadata.InstanceMetadata, error) GetInstance(ctx context.Context, providerID string) (metadata.InstanceMetadata, error)
// SignalRole signals the constellation role via cloud provider metadata (if supported by the CSP and deployment type, otherwise does nothing).
SignalRole(ctx context.Context, role role.Role) error
// SetVPNIP stores the internally used VPN IP in cloud provider metadata (if supported and required for autoscaling by the CSP, otherwise does nothing).
SetVPNIP(ctx context.Context, vpnIP string) error
// Supported is used to determine if metadata API is implemented for this cloud provider. // Supported is used to determine if metadata API is implemented for this cloud provider.
Supported() bool Supported() bool
} }
@ -96,9 +91,6 @@ type stubProviderMetadata struct {
ListErr error ListErr error
ListResp []metadata.InstanceMetadata ListResp []metadata.InstanceMetadata
SignalRoleErr error
SetVPNIPErr error
SelfErr error SelfErr error
SelfResp metadata.InstanceMetadata SelfResp metadata.InstanceMetadata
@ -129,14 +121,6 @@ func (m *stubProviderMetadata) GetInstance(ctx context.Context, providerID strin
return m.GetInstanceResp, m.GetInstanceErr return m.GetInstanceResp, m.GetInstanceErr
} }
func (m *stubProviderMetadata) SignalRole(ctx context.Context, role role.Role) error {
return m.SignalRoleErr
}
func (m *stubProviderMetadata) SetVPNIP(ctx context.Context, vpnIP string) error {
return m.SetVPNIPErr
}
func (m *stubProviderMetadata) Supported() bool { func (m *stubProviderMetadata) Supported() bool {
return m.SupportedResp return m.SupportedResp
} }

View file

@ -0,0 +1,12 @@
package resources
const (
// Constellation images.
activationImage = "ghcr.io/edgelesssys/constellation/activation-service:v1.2"
accessManagerImage = "ghcr.io/edgelesssys/constellation/access-manager:v1.2"
kmsImage = "ghcr.io/edgelesssys/constellation/kmsserver:v1.2"
verificationImage = "ghcr.io/edgelesssys/constellation/verification-service:v1.2"
// external images.
clusterAutoscalerImage = "k8s.gcr.io/autoscaling/cluster-autoscaler:v1.23.0"
)

View file

@ -0,0 +1,153 @@
package resources
import (
"fmt"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/secrets"
apps "k8s.io/api/apps/v1"
k8s "k8s.io/api/core/v1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
type verificationDaemonset struct {
DaemonSet apps.DaemonSet
Service k8s.Service
}
func NewVerificationDaemonSet(csp string) *verificationDaemonset {
return &verificationDaemonset{
DaemonSet: apps.DaemonSet{
TypeMeta: meta.TypeMeta{
APIVersion: "apps/v1",
Kind: "DaemonSet",
},
ObjectMeta: meta.ObjectMeta{
Name: "verification-service",
Namespace: "kube-system",
Labels: map[string]string{
"k8s-app": "verification-service",
"component": "verification-service",
},
},
Spec: apps.DaemonSetSpec{
Selector: &meta.LabelSelector{
MatchLabels: map[string]string{
"k8s-app": "verification-service",
},
},
Template: k8s.PodTemplateSpec{
ObjectMeta: meta.ObjectMeta{
Labels: map[string]string{
"k8s-app": "verification-service",
},
},
Spec: k8s.PodSpec{
Tolerations: []k8s.Toleration{
{
Key: "node-role.kubernetes.io/master",
Operator: k8s.TolerationOpEqual,
Value: "true",
Effect: k8s.TaintEffectNoSchedule,
},
{
Key: "node-role.kubernetes.io/control-plane",
Operator: k8s.TolerationOpExists,
Effect: k8s.TaintEffectNoSchedule,
},
{
Operator: k8s.TolerationOpExists,
Effect: k8s.TaintEffectNoExecute,
},
{
Operator: k8s.TolerationOpExists,
Effect: k8s.TaintEffectNoSchedule,
},
},
ImagePullSecrets: []k8s.LocalObjectReference{
{
Name: secrets.PullSecretName,
},
},
Containers: []k8s.Container{
{
Name: "verification-service",
Image: verificationImage,
Ports: []k8s.ContainerPort{
{
Name: "http",
ContainerPort: constants.VerifyServicePortHTTP,
},
{
Name: "grpc",
ContainerPort: constants.VerifyServicePortGRPC,
},
},
SecurityContext: &k8s.SecurityContext{
Privileged: func(b bool) *bool { return &b }(true),
},
Args: []string{
fmt.Sprintf("--cloud-provider=%s", csp),
},
VolumeMounts: []k8s.VolumeMount{
{
Name: "event-log",
ReadOnly: true,
MountPath: "/sys/kernel/security/",
},
},
},
},
Volumes: []k8s.Volume{
{
Name: "event-log",
VolumeSource: k8s.VolumeSource{
HostPath: &k8s.HostPathVolumeSource{
Path: "/sys/kernel/security/",
},
},
},
},
},
},
},
},
Service: k8s.Service{
TypeMeta: meta.TypeMeta{
APIVersion: "v1",
Kind: "Service",
},
ObjectMeta: meta.ObjectMeta{
Name: "activation-service",
Namespace: "kube-system",
},
Spec: k8s.ServiceSpec{
Type: k8s.ServiceTypeNodePort,
Ports: []k8s.ServicePort{
{
Name: "http",
Protocol: k8s.ProtocolTCP,
Port: constants.VerifyServicePortHTTP,
TargetPort: intstr.FromInt(constants.VerifyServicePortHTTP),
NodePort: constants.VerifyServiceNodePortHTTP,
},
{
Name: "grpc",
Protocol: k8s.ProtocolTCP,
Port: constants.VerifyServicePortGRPC,
TargetPort: intstr.FromInt(constants.VerifyServicePortGRPC),
NodePort: constants.VerifyServiceNodePortGRPC,
},
},
Selector: map[string]string{
"k8s-app": "verification-service",
},
},
},
}
}
func (v *verificationDaemonset) Marshal() ([]byte, error) {
return MarshalK8SResources(v)
}

View file

@ -0,0 +1,18 @@
package resources
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewVerificationDaemonset(t *testing.T) {
deployment := NewVerificationDaemonSet("csp")
deploymentYAML, err := deployment.Marshal()
require.NoError(t, err)
var recreated verificationDaemonset
require.NoError(t, UnmarshalK8SResources(deploymentYAML, &recreated))
assert.Equal(t, deployment, &recreated)
}

View file

@ -74,15 +74,15 @@ type KMSConfig struct {
func (k *KubeWrapper) InitCluster( func (k *KubeWrapper) InitCluster(
ctx context.Context, autoscalingNodeGroups []string, cloudServiceAccountURI, k8sVersion string, ctx context.Context, autoscalingNodeGroups []string, cloudServiceAccountURI, k8sVersion string,
id attestationtypes.ID, kmsConfig KMSConfig, sshUsers map[string]string, id attestationtypes.ID, kmsConfig KMSConfig, sshUsers map[string]string,
) error { ) ([]byte, error) {
// TODO: k8s version should be user input // TODO: k8s version should be user input
if err := k.clusterUtil.InstallComponents(ctx, k8sVersion); err != nil { if err := k.clusterUtil.InstallComponents(ctx, k8sVersion); err != nil {
return err return nil, err
} }
ip, err := k.getIPAddr() ip, err := k.getIPAddr()
if err != nil { if err != nil {
return err return nil, err
} }
nodeName := ip nodeName := ip
var providerID string var providerID string
@ -98,7 +98,7 @@ func (k *KubeWrapper) InitCluster(
if k.providerMetadata.Supported() { if k.providerMetadata.Supported() {
instance, err = k.providerMetadata.Self(ctx) instance, err = k.providerMetadata.Self(ctx)
if err != nil { if err != nil {
return fmt.Errorf("retrieving own instance metadata failed: %w", err) return nil, fmt.Errorf("retrieving own instance metadata failed: %w", err)
} }
nodeName = k8sCompliantHostname(instance.Name) nodeName = k8sCompliantHostname(instance.Name)
providerID = instance.ProviderID providerID = instance.ProviderID
@ -113,13 +113,13 @@ func (k *KubeWrapper) InitCluster(
} }
subnetworkPodCIDR, err = k.providerMetadata.GetSubnetworkCIDR(ctx) subnetworkPodCIDR, err = k.providerMetadata.GetSubnetworkCIDR(ctx)
if err != nil { if err != nil {
return fmt.Errorf("retrieving subnetwork CIDR failed: %w", err) return nil, fmt.Errorf("retrieving subnetwork CIDR failed: %w", err)
} }
controlPlaneEndpointIP = publicIP controlPlaneEndpointIP = publicIP
if k.providerMetadata.SupportsLoadBalancer() { if k.providerMetadata.SupportsLoadBalancer() {
controlPlaneEndpointIP, err = k.providerMetadata.GetLoadBalancerIP(ctx) controlPlaneEndpointIP, err = k.providerMetadata.GetLoadBalancerIP(ctx)
if err != nil { if err != nil {
return fmt.Errorf("retrieving load balancer IP failed: %w", err) return nil, fmt.Errorf("retrieving load balancer IP failed: %w", err)
} }
} }
} }
@ -133,14 +133,14 @@ func (k *KubeWrapper) InitCluster(
initConfig.SetControlPlaneEndpoint(controlPlaneEndpointIP) initConfig.SetControlPlaneEndpoint(controlPlaneEndpointIP)
initConfigYAML, err := initConfig.Marshal() initConfigYAML, err := initConfig.Marshal()
if err != nil { if err != nil {
return fmt.Errorf("encoding kubeadm init configuration as YAML: %w", err) return nil, fmt.Errorf("encoding kubeadm init configuration as YAML: %w", err)
} }
if err := k.clusterUtil.InitCluster(ctx, initConfigYAML); err != nil { if err := k.clusterUtil.InitCluster(ctx, initConfigYAML); err != nil {
return fmt.Errorf("kubeadm init: %w", err) return nil, fmt.Errorf("kubeadm init: %w", err)
} }
kubeConfig, err := k.GetKubeconfig() kubeConfig, err := k.GetKubeconfig()
if err != nil { if err != nil {
return fmt.Errorf("reading kubeconfig after cluster initialization: %w", err) return nil, fmt.Errorf("reading kubeconfig after cluster initialization: %w", err)
} }
k.client.SetKubeconfig(kubeConfig) k.client.SetKubeconfig(kubeConfig)
@ -154,43 +154,43 @@ func (k *KubeWrapper) InitCluster(
ProviderID: providerID, ProviderID: providerID,
} }
if err = k.clusterUtil.SetupPodNetwork(ctx, setupPodNetworkInput); err != nil { if err = k.clusterUtil.SetupPodNetwork(ctx, setupPodNetworkInput); err != nil {
return fmt.Errorf("setting up pod network: %w", err) return nil, fmt.Errorf("setting up pod network: %w", err)
} }
kms := resources.NewKMSDeployment(k.cloudProvider, kmsConfig.MasterSecret) kms := resources.NewKMSDeployment(k.cloudProvider, kmsConfig.MasterSecret)
if err = k.clusterUtil.SetupKMS(k.client, kms); err != nil { if err = k.clusterUtil.SetupKMS(k.client, kms); err != nil {
return fmt.Errorf("setting up kms: %w", err) return nil, fmt.Errorf("setting up kms: %w", err)
} }
if err := k.setupActivationService(k.cloudProvider, k.initialMeasurementsJSON, id); err != nil { if err := k.setupActivationService(k.cloudProvider, k.initialMeasurementsJSON, id); err != nil {
return fmt.Errorf("setting up activation service failed: %w", err) return nil, fmt.Errorf("setting up activation service failed: %w", err)
} }
if err := k.setupCCM(ctx, subnetworkPodCIDR, cloudServiceAccountURI, instance); err != nil { if err := k.setupCCM(ctx, subnetworkPodCIDR, cloudServiceAccountURI, instance); err != nil {
return fmt.Errorf("setting up cloud controller manager: %w", err) return nil, fmt.Errorf("setting up cloud controller manager: %w", err)
} }
if err := k.setupCloudNodeManager(); err != nil { if err := k.setupCloudNodeManager(); err != nil {
return fmt.Errorf("setting up cloud node manager: %w", err) return nil, fmt.Errorf("setting up cloud node manager: %w", err)
} }
if err := k.setupClusterAutoscaler(instance, cloudServiceAccountURI, autoscalingNodeGroups); err != nil { if err := k.setupClusterAutoscaler(instance, cloudServiceAccountURI, autoscalingNodeGroups); err != nil {
return fmt.Errorf("setting up cluster autoscaler: %w", err) return nil, fmt.Errorf("setting up cluster autoscaler: %w", err)
} }
accessManager := resources.NewAccessManagerDeployment(sshUsers) accessManager := resources.NewAccessManagerDeployment(sshUsers)
if err := k.clusterUtil.SetupAccessManager(k.client, accessManager); err != nil { if err := k.clusterUtil.SetupAccessManager(k.client, accessManager); err != nil {
return fmt.Errorf("failed to setup access-manager: %w", err) return nil, fmt.Errorf("failed to setup access-manager: %w", err)
} }
if err := k.clusterUtil.SetupVerificationService( if err := k.clusterUtil.SetupVerificationService(
k.client, resources.NewVerificationDaemonSet(k.cloudProvider), k.client, resources.NewVerificationDaemonSet(k.cloudProvider),
); err != nil { ); err != nil {
return fmt.Errorf("failed to setup verification service: %w", err) return nil, fmt.Errorf("failed to setup verification service: %w", err)
} }
go k.clusterUtil.FixCilium(nodeName) go k.clusterUtil.FixCilium(nodeName)
return nil return k.GetKubeconfig()
} }
// JoinCluster joins existing Kubernetes cluster. // JoinCluster joins existing Kubernetes cluster.

View file

@ -268,7 +268,7 @@ func TestInitCluster(t *testing.T) {
kubeconfigReader: tc.kubeconfigReader, kubeconfigReader: tc.kubeconfigReader,
getIPAddr: func() (string, error) { return privateIP, nil }, getIPAddr: func() (string, error) { return privateIP, nil },
} }
err := kube.InitCluster(context.Background(), autoscalingNodeGroups, serviceAccountUri, k8sVersion, attestationtypes.ID{}, KMSConfig{MasterSecret: masterSecret}, nil) _, err := kube.InitCluster(context.Background(), autoscalingNodeGroups, serviceAccountUri, k8sVersion, attestationtypes.ID{}, KMSConfig{MasterSecret: masterSecret}, nil)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)

View file

@ -0,0 +1,25 @@
package nodelock
import "sync"
// Lock locks the node once there the join or the init is at a point
// where there is no turning back and the other operation does not need
// to continue.
//
// This can be viewed as a state machine with two states: unlocked and locked.
// There is no way to unlock, so the state changes only once from unlock to
// locked.
type Lock struct {
mux *sync.Mutex
}
// New creates a new NodeLock, which is unlocked.
func New() *Lock {
return &Lock{mux: &sync.Mutex{}}
}
// TryLockOnce tries to lock the node. If the node is already locked, it
// returns false. If the node is unlocked, it locks it and returns true.
func (n *Lock) TryLockOnce() bool {
return n.mux.TryLock()
}

View file

@ -5,17 +5,17 @@ import (
"fmt" "fmt"
azurecloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/azure" azurecloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/azure"
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
gcpcloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/gcp" gcpcloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/gcp"
qemucloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/qemu" qemucloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/qemu"
"github.com/edgelesssys/constellation/internal/cloud/metadata"
"github.com/edgelesssys/constellation/internal/deploy/ssh" "github.com/edgelesssys/constellation/internal/deploy/ssh"
) )
type providerMetadata interface { type providerMetadata interface {
// List retrieves all instances belonging to the current constellation. // List retrieves all instances belonging to the current constellation.
List(ctx context.Context) ([]cloudtypes.Instance, error) List(ctx context.Context) ([]metadata.InstanceMetadata, error)
// Self retrieves the current instance. // Self retrieves the current instance.
Self(ctx context.Context) (cloudtypes.Instance, error) Self(ctx context.Context) (metadata.InstanceMetadata, error)
} }
// Fetcher checks the metadata service to search for instances that were set up for debugging and cloud provider specific SSH keys. // Fetcher checks the metadata service to search for instances that were set up for debugging and cloud provider specific SSH keys.

View file

@ -5,7 +5,7 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes" "github.com/edgelesssys/constellation/internal/cloud/metadata"
"github.com/edgelesssys/constellation/internal/deploy/ssh" "github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -29,7 +29,7 @@ func TestDiscoverDebugIPs(t *testing.T) {
}{ }{
"disovery works": { "disovery works": {
meta: stubMetadata{ meta: stubMetadata{
listRes: []cloudtypes.Instance{ listRes: []metadata.InstanceMetadata{
{ {
PrivateIPs: []string{"192.0.2.0"}, PrivateIPs: []string{"192.0.2.0"},
}, },
@ -83,7 +83,7 @@ func TestFetchSSHKeys(t *testing.T) {
}{ }{
"fetch works": { "fetch works": {
meta: stubMetadata{ meta: stubMetadata{
selfRes: cloudtypes.Instance{ selfRes: metadata.InstanceMetadata{
Name: "name", Name: "name",
ProviderID: "provider-id", ProviderID: "provider-id",
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}}, SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
@ -125,24 +125,24 @@ func TestFetchSSHKeys(t *testing.T) {
} }
type stubMetadata struct { type stubMetadata struct {
listRes []cloudtypes.Instance listRes []metadata.InstanceMetadata
listErr error listErr error
selfRes cloudtypes.Instance selfRes metadata.InstanceMetadata
selfErr error selfErr error
getInstanceRes cloudtypes.Instance getInstanceRes metadata.InstanceMetadata
getInstanceErr error getInstanceErr error
supportedRes bool supportedRes bool
} }
func (m *stubMetadata) List(ctx context.Context) ([]cloudtypes.Instance, error) { func (m *stubMetadata) List(ctx context.Context) ([]metadata.InstanceMetadata, error) {
return m.listRes, m.listErr return m.listRes, m.listErr
} }
func (m *stubMetadata) Self(ctx context.Context) (cloudtypes.Instance, error) { func (m *stubMetadata) Self(ctx context.Context) (metadata.InstanceMetadata, error) {
return m.selfRes, m.selfErr return m.selfRes, m.selfErr
} }
func (m *stubMetadata) GetInstance(ctx context.Context, providerID string) (cloudtypes.Instance, error) { func (m *stubMetadata) GetInstance(ctx context.Context, providerID string) (metadata.InstanceMetadata, error) {
return m.getInstanceRes, m.getInstanceErr return m.getInstanceRes, m.getInstanceErr
} }

View file

@ -17,8 +17,8 @@ import (
"math/big" "math/big"
"time" "time"
"github.com/edgelesssys/constellation/coordinator/config"
"github.com/edgelesssys/constellation/coordinator/util" "github.com/edgelesssys/constellation/coordinator/util"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/oid" "github.com/edgelesssys/constellation/internal/oid"
) )
@ -45,7 +45,7 @@ func CreateAttestationServerTLSConfig(issuer Issuer, validators []Validator) (*t
// If no validators are set, the server's attestation document will not be verified. // If no validators are set, the server's attestation document will not be verified.
// If issuer is nil, the client will be unable to perform mutual aTLS. // If issuer is nil, the client will be unable to perform mutual aTLS.
func CreateAttestationClientTLSConfig(issuer Issuer, validators []Validator) (*tls.Config, error) { func CreateAttestationClientTLSConfig(issuer Issuer, validators []Validator) (*tls.Config, error) {
clientNonce, err := util.GenerateRandomBytes(config.RNGLengthDefault) clientNonce, err := util.GenerateRandomBytes(constants.RNGLengthDefault)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -87,7 +87,7 @@ func getATLSConfigForClientFunc(issuer Issuer, validators []Validator) (func(*tl
// this function will be called once for every client // this function will be called once for every client
return func(chi *tls.ClientHelloInfo) (*tls.Config, error) { return func(chi *tls.ClientHelloInfo) (*tls.Config, error) {
// generate nonce for this connection // generate nonce for this connection
serverNonce, err := util.GenerateRandomBytes(config.RNGLengthDefault) serverNonce, err := util.GenerateRandomBytes(constants.RNGLengthDefault)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -81,56 +81,6 @@ func TestUIDFromProviderID(t *testing.T) {
} }
} }
func TestVMInformationFromProviderID(t *testing.T) {
testCases := map[string]struct {
providerID string
wantSubscriptionID string
wantResourceGroup string
wantInstanceName string
wantErr bool
}{
"simple id": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-id",
wantSubscriptionID: "subscription-id",
wantResourceGroup: "resource-group",
wantInstanceName: "instance-id",
},
"missing instance": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines",
wantErr: true,
},
"providerID for scale set instance must fail": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
wantErr: true,
},
"wrong provider": {
providerID: "gcp:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-id",
wantErr: true,
},
"providerID is malformed": {
providerID: "malformed-provider-id",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
subscriptionID, resourceGroup, instanceName, err := VMInformationFromProviderID(tc.providerID)
if tc.wantErr {
assert.Error(err)
return
}
assert.NoError(err)
assert.Equal(tc.wantSubscriptionID, subscriptionID)
assert.Equal(tc.wantResourceGroup, resourceGroup)
assert.Equal(tc.wantInstanceName, instanceName)
})
}
}
func TestScaleSetInformationFromProviderID(t *testing.T) { func TestScaleSetInformationFromProviderID(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
providerID string providerID string

View file

@ -27,10 +27,6 @@ type metadataAPI interface {
List(ctx context.Context) ([]InstanceMetadata, error) List(ctx context.Context) ([]InstanceMetadata, error)
// Self retrieves the current instance. // Self retrieves the current instance.
Self(ctx context.Context) (InstanceMetadata, error) Self(ctx context.Context) (InstanceMetadata, error)
// SignalRole signals the constellation role via cloud provider metadata (if supported by the CSP and deployment type, otherwise does nothing).
SignalRole(ctx context.Context, role role.Role) error
// SetVPNIP stores the internally used VPN IP in cloud provider metadata (if supported and required for autoscaling by the CSP, otherwise does nothing).
SetVPNIP(ctx context.Context, vpnIP string) error
// Supported is used to determine if metadata API is implemented for this cloud provider. // Supported is used to determine if metadata API is implemented for this cloud provider.
Supported() bool Supported() bool
} }

View file

@ -82,6 +82,8 @@ const (
MasterSecretLengthDefault = 32 MasterSecretLengthDefault = 32
// MasterSecretLengthMin is the minimal length in bytes for user provided master secrets. // MasterSecretLengthMin is the minimal length in bytes for user provided master secrets.
MasterSecretLengthMin = 16 MasterSecretLengthMin = 16
// RNGLengthDefault is the number of bytes used for generating nonces.
RNGLengthDefault = 32
// //
// CLI. // CLI.

View file

@ -7,9 +7,9 @@ import (
"sync" "sync"
"time" "time"
"github.com/edgelesssys/constellation/coordinator/config"
"github.com/edgelesssys/constellation/coordinator/core" "github.com/edgelesssys/constellation/coordinator/core"
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto" "github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/grpc/atlscredentials" "github.com/edgelesssys/constellation/internal/grpc/atlscredentials"
"github.com/edgelesssys/constellation/internal/logger" "github.com/edgelesssys/constellation/internal/logger"
"github.com/edgelesssys/constellation/internal/oid" "github.com/edgelesssys/constellation/internal/oid"
@ -51,8 +51,8 @@ func (a *KeyAPI) PushStateDiskKey(ctx context.Context, in *keyproto.PushStateDis
if len(a.key) != 0 { if len(a.key) != 0 {
return nil, status.Error(codes.FailedPrecondition, "node already received a passphrase") return nil, status.Error(codes.FailedPrecondition, "node already received a passphrase")
} }
if len(in.StateDiskKey) != config.RNGLengthDefault { if len(in.StateDiskKey) != constants.RNGLengthDefault {
return nil, status.Errorf(codes.InvalidArgument, "received invalid passphrase: expected length: %d, but got: %d", config.RNGLengthDefault, len(in.StateDiskKey)) return nil, status.Errorf(codes.InvalidArgument, "received invalid passphrase: expected length: %d, but got: %d", constants.RNGLengthDefault, len(in.StateDiskKey))
} }
a.key = in.StateDiskKey a.key = in.StateDiskKey

View file

@ -214,14 +214,6 @@ func (s stubMetadata) Self(ctx context.Context) (cloudtypes.Instance, error) {
return cloudtypes.Instance{}, nil return cloudtypes.Instance{}, nil
} }
func (s stubMetadata) SignalRole(ctx context.Context, role role.Role) error {
return nil
}
func (s stubMetadata) SetVPNIP(ctx context.Context, vpnIP string) error {
return nil
}
func (s stubMetadata) Supported() bool { func (s stubMetadata) Supported() bool {
return true return true
} }

View file

@ -8,9 +8,9 @@ import (
"path/filepath" "path/filepath"
"syscall" "syscall"
"github.com/edgelesssys/constellation/coordinator/config"
"github.com/edgelesssys/constellation/coordinator/nodestate" "github.com/edgelesssys/constellation/coordinator/nodestate"
"github.com/edgelesssys/constellation/internal/attestation/vtpm" "github.com/edgelesssys/constellation/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file" "github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/logger" "github.com/edgelesssys/constellation/internal/logger"
"github.com/spf13/afero" "github.com/spf13/afero"
@ -99,7 +99,7 @@ func (s *SetupManager) PrepareNewDisk() error {
return err return err
} }
passphrase := make([]byte, config.RNGLengthDefault) passphrase := make([]byte, constants.RNGLengthDefault)
if _, err := rand.Read(passphrase); err != nil { if _, err := rand.Read(passphrase); err != nil {
return err return err
} }

View file

@ -7,9 +7,9 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/edgelesssys/constellation/coordinator/config"
"github.com/edgelesssys/constellation/coordinator/nodestate" "github.com/edgelesssys/constellation/coordinator/nodestate"
"github.com/edgelesssys/constellation/internal/attestation/vtpm" "github.com/edgelesssys/constellation/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file" "github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/logger" "github.com/edgelesssys/constellation/internal/logger"
"github.com/spf13/afero" "github.com/spf13/afero"
@ -193,7 +193,7 @@ func TestPrepareNewDisk(t *testing.T) {
data, err := tc.fs.ReadFile(filepath.Join(keyPath, keyFile)) data, err := tc.fs.ReadFile(filepath.Join(keyPath, keyFile))
require.NoError(t, err) require.NoError(t, err)
assert.Len(data, config.RNGLengthDefault) assert.Len(data, constants.RNGLengthDefault)
} }
}) })
} }