AB#2169 Implement control-plane activation in activation service (#217)

* Implement Control Plane activation flow

* Rename Activation RPCs

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2022-06-21 11:10:32 +02:00 committed by GitHub
parent a1103b6da6
commit e6b1156849
10 changed files with 633 additions and 163 deletions

View File

@ -67,5 +67,6 @@ Uses fsnotify to wait for expected measurement updates, and updates the validato
## [Dockerfile](./Dockerfile)
```shell
DOCKER_BUILDKIT=1 docker build --build-arg PROJECT_VERSION="v1.0.0" -t ghcr.io/edgelesssys/activation-service:v1.0.0 -f activation/Dockerfile .
export VERSION=1.0.0
DOCKER_BUILDKIT=1 docker build --build-arg PROJECT_VERSION=${VERSION} -t ghcr.io/edgelesssys/constellation/activation-service:v${VERSION} -f activation/Dockerfile .
```

View File

@ -20,7 +20,7 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ActivateNodeRequest struct {
type ActivateWorkerNodeRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
@ -29,8 +29,8 @@ type ActivateNodeRequest struct {
NodeName string `protobuf:"bytes,2,opt,name=node_name,json=nodeName,proto3" json:"node_name,omitempty"`
}
func (x *ActivateNodeRequest) Reset() {
*x = ActivateNodeRequest{}
func (x *ActivateWorkerNodeRequest) Reset() {
*x = ActivateWorkerNodeRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_activation_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@ -38,13 +38,13 @@ func (x *ActivateNodeRequest) Reset() {
}
}
func (x *ActivateNodeRequest) String() string {
func (x *ActivateWorkerNodeRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ActivateNodeRequest) ProtoMessage() {}
func (*ActivateWorkerNodeRequest) ProtoMessage() {}
func (x *ActivateNodeRequest) ProtoReflect() protoreflect.Message {
func (x *ActivateWorkerNodeRequest) ProtoReflect() protoreflect.Message {
mi := &file_activation_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@ -56,26 +56,26 @@ func (x *ActivateNodeRequest) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use ActivateNodeRequest.ProtoReflect.Descriptor instead.
func (*ActivateNodeRequest) Descriptor() ([]byte, []int) {
// Deprecated: Use ActivateWorkerNodeRequest.ProtoReflect.Descriptor instead.
func (*ActivateWorkerNodeRequest) Descriptor() ([]byte, []int) {
return file_activation_proto_rawDescGZIP(), []int{0}
}
func (x *ActivateNodeRequest) GetDiskUuid() string {
func (x *ActivateWorkerNodeRequest) GetDiskUuid() string {
if x != nil {
return x.DiskUuid
}
return ""
}
func (x *ActivateNodeRequest) GetNodeName() string {
func (x *ActivateWorkerNodeRequest) GetNodeName() string {
if x != nil {
return x.NodeName
}
return ""
}
type ActivateNodeResponse struct {
type ActivateWorkerNodeResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
@ -90,8 +90,8 @@ type ActivateNodeResponse struct {
DiscoveryTokenCaCertHash string `protobuf:"bytes,8,opt,name=discovery_token_ca_cert_hash,json=discoveryTokenCaCertHash,proto3" json:"discovery_token_ca_cert_hash,omitempty"`
}
func (x *ActivateNodeResponse) Reset() {
*x = ActivateNodeResponse{}
func (x *ActivateWorkerNodeResponse) Reset() {
*x = ActivateWorkerNodeResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_activation_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@ -99,13 +99,13 @@ func (x *ActivateNodeResponse) Reset() {
}
}
func (x *ActivateNodeResponse) String() string {
func (x *ActivateWorkerNodeResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ActivateNodeResponse) ProtoMessage() {}
func (*ActivateWorkerNodeResponse) ProtoMessage() {}
func (x *ActivateNodeResponse) ProtoReflect() protoreflect.Message {
func (x *ActivateWorkerNodeResponse) ProtoReflect() protoreflect.Message {
mi := &file_activation_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@ -117,75 +117,78 @@ func (x *ActivateNodeResponse) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use ActivateNodeResponse.ProtoReflect.Descriptor instead.
func (*ActivateNodeResponse) Descriptor() ([]byte, []int) {
// Deprecated: Use ActivateWorkerNodeResponse.ProtoReflect.Descriptor instead.
func (*ActivateWorkerNodeResponse) Descriptor() ([]byte, []int) {
return file_activation_proto_rawDescGZIP(), []int{1}
}
func (x *ActivateNodeResponse) GetStateDiskKey() []byte {
func (x *ActivateWorkerNodeResponse) GetStateDiskKey() []byte {
if x != nil {
return x.StateDiskKey
}
return nil
}
func (x *ActivateNodeResponse) GetOwnerId() []byte {
func (x *ActivateWorkerNodeResponse) GetOwnerId() []byte {
if x != nil {
return x.OwnerId
}
return nil
}
func (x *ActivateNodeResponse) GetClusterId() []byte {
func (x *ActivateWorkerNodeResponse) GetClusterId() []byte {
if x != nil {
return x.ClusterId
}
return nil
}
func (x *ActivateNodeResponse) GetKubeletKey() []byte {
func (x *ActivateWorkerNodeResponse) GetKubeletKey() []byte {
if x != nil {
return x.KubeletKey
}
return nil
}
func (x *ActivateNodeResponse) GetKubeletCert() []byte {
func (x *ActivateWorkerNodeResponse) GetKubeletCert() []byte {
if x != nil {
return x.KubeletCert
}
return nil
}
func (x *ActivateNodeResponse) GetApiServerEndpoint() string {
func (x *ActivateWorkerNodeResponse) GetApiServerEndpoint() string {
if x != nil {
return x.ApiServerEndpoint
}
return ""
}
func (x *ActivateNodeResponse) GetToken() string {
func (x *ActivateWorkerNodeResponse) GetToken() string {
if x != nil {
return x.Token
}
return ""
}
func (x *ActivateNodeResponse) GetDiscoveryTokenCaCertHash() string {
func (x *ActivateWorkerNodeResponse) GetDiscoveryTokenCaCertHash() string {
if x != nil {
return x.DiscoveryTokenCaCertHash
}
return ""
}
type ActivateCoordinatorRequest struct {
type ActivateControlPlaneNodeRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
DiskUuid string `protobuf:"bytes,1,opt,name=disk_uuid,json=diskUuid,proto3" json:"disk_uuid,omitempty"`
NodeName string `protobuf:"bytes,2,opt,name=node_name,json=nodeName,proto3" json:"node_name,omitempty"`
}
func (x *ActivateCoordinatorRequest) Reset() {
*x = ActivateCoordinatorRequest{}
func (x *ActivateControlPlaneNodeRequest) Reset() {
*x = ActivateControlPlaneNodeRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_activation_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@ -193,13 +196,13 @@ func (x *ActivateCoordinatorRequest) Reset() {
}
}
func (x *ActivateCoordinatorRequest) String() string {
func (x *ActivateControlPlaneNodeRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ActivateCoordinatorRequest) ProtoMessage() {}
func (*ActivateControlPlaneNodeRequest) ProtoMessage() {}
func (x *ActivateCoordinatorRequest) ProtoReflect() protoreflect.Message {
func (x *ActivateControlPlaneNodeRequest) ProtoReflect() protoreflect.Message {
mi := &file_activation_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@ -211,19 +214,43 @@ func (x *ActivateCoordinatorRequest) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use ActivateCoordinatorRequest.ProtoReflect.Descriptor instead.
func (*ActivateCoordinatorRequest) Descriptor() ([]byte, []int) {
// Deprecated: Use ActivateControlPlaneNodeRequest.ProtoReflect.Descriptor instead.
func (*ActivateControlPlaneNodeRequest) Descriptor() ([]byte, []int) {
return file_activation_proto_rawDescGZIP(), []int{2}
}
type ActivateCoordinatorResponse struct {
func (x *ActivateControlPlaneNodeRequest) GetDiskUuid() string {
if x != nil {
return x.DiskUuid
}
return ""
}
func (x *ActivateControlPlaneNodeRequest) GetNodeName() string {
if x != nil {
return x.NodeName
}
return ""
}
type ActivateControlPlaneNodeResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
StateDiskKey []byte `protobuf:"bytes,1,opt,name=state_disk_key,json=stateDiskKey,proto3" json:"state_disk_key,omitempty"`
OwnerId []byte `protobuf:"bytes,2,opt,name=owner_id,json=ownerId,proto3" json:"owner_id,omitempty"`
ClusterId []byte `protobuf:"bytes,3,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"`
KubeletKey []byte `protobuf:"bytes,4,opt,name=kubelet_key,json=kubeletKey,proto3" json:"kubelet_key,omitempty"`
KubeletCert []byte `protobuf:"bytes,5,opt,name=kubelet_cert,json=kubeletCert,proto3" json:"kubelet_cert,omitempty"`
ApiServerEndpoint string `protobuf:"bytes,6,opt,name=api_server_endpoint,json=apiServerEndpoint,proto3" json:"api_server_endpoint,omitempty"`
Token string `protobuf:"bytes,7,opt,name=token,proto3" json:"token,omitempty"`
DiscoveryTokenCaCertHash string `protobuf:"bytes,8,opt,name=discovery_token_ca_cert_hash,json=discoveryTokenCaCertHash,proto3" json:"discovery_token_ca_cert_hash,omitempty"`
CertificateKey string `protobuf:"bytes,9,opt,name=certificate_key,json=certificateKey,proto3" json:"certificate_key,omitempty"`
}
func (x *ActivateCoordinatorResponse) Reset() {
*x = ActivateCoordinatorResponse{}
func (x *ActivateControlPlaneNodeResponse) Reset() {
*x = ActivateControlPlaneNodeResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_activation_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@ -231,13 +258,13 @@ func (x *ActivateCoordinatorResponse) Reset() {
}
}
func (x *ActivateCoordinatorResponse) String() string {
func (x *ActivateControlPlaneNodeResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ActivateCoordinatorResponse) ProtoMessage() {}
func (*ActivateControlPlaneNodeResponse) ProtoMessage() {}
func (x *ActivateCoordinatorResponse) ProtoReflect() protoreflect.Message {
func (x *ActivateControlPlaneNodeResponse) ProtoReflect() protoreflect.Message {
mi := &file_activation_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@ -249,22 +276,86 @@ func (x *ActivateCoordinatorResponse) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use ActivateCoordinatorResponse.ProtoReflect.Descriptor instead.
func (*ActivateCoordinatorResponse) Descriptor() ([]byte, []int) {
// Deprecated: Use ActivateControlPlaneNodeResponse.ProtoReflect.Descriptor instead.
func (*ActivateControlPlaneNodeResponse) Descriptor() ([]byte, []int) {
return file_activation_proto_rawDescGZIP(), []int{3}
}
func (x *ActivateControlPlaneNodeResponse) GetStateDiskKey() []byte {
if x != nil {
return x.StateDiskKey
}
return nil
}
func (x *ActivateControlPlaneNodeResponse) GetOwnerId() []byte {
if x != nil {
return x.OwnerId
}
return nil
}
func (x *ActivateControlPlaneNodeResponse) GetClusterId() []byte {
if x != nil {
return x.ClusterId
}
return nil
}
func (x *ActivateControlPlaneNodeResponse) GetKubeletKey() []byte {
if x != nil {
return x.KubeletKey
}
return nil
}
func (x *ActivateControlPlaneNodeResponse) GetKubeletCert() []byte {
if x != nil {
return x.KubeletCert
}
return nil
}
func (x *ActivateControlPlaneNodeResponse) GetApiServerEndpoint() string {
if x != nil {
return x.ApiServerEndpoint
}
return ""
}
func (x *ActivateControlPlaneNodeResponse) GetToken() string {
if x != nil {
return x.Token
}
return ""
}
func (x *ActivateControlPlaneNodeResponse) GetDiscoveryTokenCaCertHash() string {
if x != nil {
return x.DiscoveryTokenCaCertHash
}
return ""
}
func (x *ActivateControlPlaneNodeResponse) GetCertificateKey() string {
if x != nil {
return x.CertificateKey
}
return ""
}
var File_activation_proto protoreflect.FileDescriptor
var file_activation_proto_rawDesc = []byte{
0x0a, 0x10, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x06, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x22, 0x4f, 0x0a, 0x13, 0x41, 0x63,
0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x12, 0x1b,
0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xc0, 0x02, 0x0a, 0x14,
0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70,
0x74, 0x6f, 0x12, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x55,
0x0a, 0x19, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72,
0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64,
0x69, 0x73, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x64, 0x69, 0x73, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64,
0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xc6, 0x02, 0x0a, 0x1a, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61,
0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69,
0x73, 0x6b, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x74,
0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x6b, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77,
@ -283,27 +374,56 @@ var file_activation_proto_rawDesc = []byte{
0x0a, 0x1c, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x74, 0x6f, 0x6b, 0x65,
0x6e, 0x5f, 0x63, 0x61, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x08,
0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x54,
0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x61, 0x43, 0x65, 0x72, 0x74, 0x48, 0x61, 0x73, 0x68, 0x22, 0x1c,
0x0a, 0x1a, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69,
0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1d, 0x0a, 0x1b,
0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61,
0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xb0, 0x01, 0x0a, 0x03,
0x41, 0x50, 0x49, 0x12, 0x49, 0x0a, 0x0c, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e,
0x6f, 0x64, 0x65, 0x12, 0x1b, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74,
0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1c, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61,
0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e,
0x0a, 0x13, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69,
0x6e, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x22, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74,
0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x75, 0x62, 0x61,
0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6f, 0x72, 0x64,
0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x48,
0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x64, 0x67,
0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x65, 0x6c,
0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x61, 0x43, 0x65, 0x72, 0x74, 0x48, 0x61, 0x73, 0x68, 0x22, 0x5b,
0x0a, 0x1f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x12, 0x1b,
0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xf5, 0x02, 0x0a, 0x20,
0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50,
0x6c, 0x61, 0x6e, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x24, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x6b,
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65, 0x44,
0x69, 0x73, 0x6b, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f,
0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49,
0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64,
0x12, 0x1f, 0x0a, 0x0b, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x4b, 0x65,
0x79, 0x12, 0x21, 0x0a, 0x0c, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x5f, 0x63, 0x65, 0x72,
0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74,
0x43, 0x65, 0x72, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28,
0x09, 0x52, 0x11, 0x61, 0x70, 0x69, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70,
0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x07, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x3e, 0x0a, 0x1c, 0x64, 0x69,
0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x63, 0x61,
0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09,
0x52, 0x18, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
0x43, 0x61, 0x43, 0x65, 0x72, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x65,
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x09, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
0x4b, 0x65, 0x79, 0x32, 0xe1, 0x01, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x63, 0x0a, 0x12, 0x41,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4e, 0x6f, 0x64,
0x65, 0x12, 0x25, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4e, 0x6f, 0x64,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x76,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x57, 0x6f,
0x72, 0x6b, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x75, 0x0a, 0x18, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x2b, 0x2e, 0x61,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61,
0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x4e, 0x6f,
0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x61, 0x63, 0x74, 0x69,
0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x43,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79,
0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f,
0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -320,16 +440,16 @@ func file_activation_proto_rawDescGZIP() []byte {
var file_activation_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_activation_proto_goTypes = []interface{}{
(*ActivateNodeRequest)(nil), // 0: pubapi.ActivateNodeRequest
(*ActivateNodeResponse)(nil), // 1: pubapi.ActivateNodeResponse
(*ActivateCoordinatorRequest)(nil), // 2: pubapi.ActivateCoordinatorRequest
(*ActivateCoordinatorResponse)(nil), // 3: pubapi.ActivateCoordinatorResponse
(*ActivateWorkerNodeRequest)(nil), // 0: activation.ActivateWorkerNodeRequest
(*ActivateWorkerNodeResponse)(nil), // 1: activation.ActivateWorkerNodeResponse
(*ActivateControlPlaneNodeRequest)(nil), // 2: activation.ActivateControlPlaneNodeRequest
(*ActivateControlPlaneNodeResponse)(nil), // 3: activation.ActivateControlPlaneNodeResponse
}
var file_activation_proto_depIdxs = []int32{
0, // 0: pubapi.API.ActivateNode:input_type -> pubapi.ActivateNodeRequest
2, // 1: pubapi.API.ActivateCoordinator:input_type -> pubapi.ActivateCoordinatorRequest
1, // 2: pubapi.API.ActivateNode:output_type -> pubapi.ActivateNodeResponse
3, // 3: pubapi.API.ActivateCoordinator:output_type -> pubapi.ActivateCoordinatorResponse
0, // 0: activation.API.ActivateWorkerNode:input_type -> activation.ActivateWorkerNodeRequest
2, // 1: activation.API.ActivateControlPlaneNode:input_type -> activation.ActivateControlPlaneNodeRequest
1, // 2: activation.API.ActivateWorkerNode:output_type -> activation.ActivateWorkerNodeResponse
3, // 3: activation.API.ActivateControlPlaneNode:output_type -> activation.ActivateControlPlaneNodeResponse
2, // [2:4] is the sub-list for method output_type
0, // [0:2] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
@ -344,7 +464,7 @@ func file_activation_proto_init() {
}
if !protoimpl.UnsafeEnabled {
file_activation_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ActivateNodeRequest); i {
switch v := v.(*ActivateWorkerNodeRequest); i {
case 0:
return &v.state
case 1:
@ -356,7 +476,7 @@ func file_activation_proto_init() {
}
}
file_activation_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ActivateNodeResponse); i {
switch v := v.(*ActivateWorkerNodeResponse); i {
case 0:
return &v.state
case 1:
@ -368,7 +488,7 @@ func file_activation_proto_init() {
}
}
file_activation_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ActivateCoordinatorRequest); i {
switch v := v.(*ActivateControlPlaneNodeRequest); i {
case 0:
return &v.state
case 1:
@ -380,7 +500,7 @@ func file_activation_proto_init() {
}
}
file_activation_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ActivateCoordinatorResponse); i {
switch v := v.(*ActivateControlPlaneNodeResponse); i {
case 0:
return &v.state
case 1:

View File

@ -1,21 +1,21 @@
syntax = "proto3";
package pubapi;
package activation;
option go_package = "github.com/edgelesssys/constellation/activation/server/activationproto";
service API {
rpc ActivateNode(ActivateNodeRequest) returns (ActivateNodeResponse);
rpc ActivateCoordinator(ActivateCoordinatorRequest) returns (ActivateCoordinatorResponse);
rpc ActivateWorkerNode(ActivateWorkerNodeRequest) returns (ActivateWorkerNodeResponse);
rpc ActivateControlPlaneNode(ActivateControlPlaneNodeRequest) returns (ActivateControlPlaneNodeResponse);
}
message ActivateNodeRequest {
message ActivateWorkerNodeRequest {
string disk_uuid = 1;
string node_name = 2;
}
message ActivateNodeResponse {
message ActivateWorkerNodeResponse {
bytes state_disk_key = 1;
bytes owner_id = 2;
bytes cluster_id = 3;
@ -27,8 +27,19 @@ message ActivateNodeResponse {
}
message ActivateCoordinatorRequest {
message ActivateControlPlaneNodeRequest {
string disk_uuid = 1;
string node_name = 2;
}
message ActivateCoordinatorResponse {
message ActivateControlPlaneNodeResponse {
bytes state_disk_key = 1;
bytes owner_id = 2;
bytes cluster_id = 3;
bytes kubelet_key = 4;
bytes kubelet_cert = 5;
string api_server_endpoint = 6;
string token = 7;
string discovery_token_ca_cert_hash = 8;
string certificate_key = 9;
}

View File

@ -22,8 +22,8 @@ const _ = grpc.SupportPackageIsVersion7
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type APIClient interface {
ActivateNode(ctx context.Context, in *ActivateNodeRequest, opts ...grpc.CallOption) (*ActivateNodeResponse, error)
ActivateCoordinator(ctx context.Context, in *ActivateCoordinatorRequest, opts ...grpc.CallOption) (*ActivateCoordinatorResponse, error)
ActivateWorkerNode(ctx context.Context, in *ActivateWorkerNodeRequest, opts ...grpc.CallOption) (*ActivateWorkerNodeResponse, error)
ActivateControlPlaneNode(ctx context.Context, in *ActivateControlPlaneNodeRequest, opts ...grpc.CallOption) (*ActivateControlPlaneNodeResponse, error)
}
type aPIClient struct {
@ -34,18 +34,18 @@ func NewAPIClient(cc grpc.ClientConnInterface) APIClient {
return &aPIClient{cc}
}
func (c *aPIClient) ActivateNode(ctx context.Context, in *ActivateNodeRequest, opts ...grpc.CallOption) (*ActivateNodeResponse, error) {
out := new(ActivateNodeResponse)
err := c.cc.Invoke(ctx, "/pubapi.API/ActivateNode", in, out, opts...)
func (c *aPIClient) ActivateWorkerNode(ctx context.Context, in *ActivateWorkerNodeRequest, opts ...grpc.CallOption) (*ActivateWorkerNodeResponse, error) {
out := new(ActivateWorkerNodeResponse)
err := c.cc.Invoke(ctx, "/activation.API/ActivateWorkerNode", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *aPIClient) ActivateCoordinator(ctx context.Context, in *ActivateCoordinatorRequest, opts ...grpc.CallOption) (*ActivateCoordinatorResponse, error) {
out := new(ActivateCoordinatorResponse)
err := c.cc.Invoke(ctx, "/pubapi.API/ActivateCoordinator", in, out, opts...)
func (c *aPIClient) ActivateControlPlaneNode(ctx context.Context, in *ActivateControlPlaneNodeRequest, opts ...grpc.CallOption) (*ActivateControlPlaneNodeResponse, error) {
out := new(ActivateControlPlaneNodeResponse)
err := c.cc.Invoke(ctx, "/activation.API/ActivateControlPlaneNode", in, out, opts...)
if err != nil {
return nil, err
}
@ -56,8 +56,8 @@ func (c *aPIClient) ActivateCoordinator(ctx context.Context, in *ActivateCoordin
// All implementations must embed UnimplementedAPIServer
// for forward compatibility
type APIServer interface {
ActivateNode(context.Context, *ActivateNodeRequest) (*ActivateNodeResponse, error)
ActivateCoordinator(context.Context, *ActivateCoordinatorRequest) (*ActivateCoordinatorResponse, error)
ActivateWorkerNode(context.Context, *ActivateWorkerNodeRequest) (*ActivateWorkerNodeResponse, error)
ActivateControlPlaneNode(context.Context, *ActivateControlPlaneNodeRequest) (*ActivateControlPlaneNodeResponse, error)
mustEmbedUnimplementedAPIServer()
}
@ -65,11 +65,11 @@ type APIServer interface {
type UnimplementedAPIServer struct {
}
func (UnimplementedAPIServer) ActivateNode(context.Context, *ActivateNodeRequest) (*ActivateNodeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ActivateNode not implemented")
func (UnimplementedAPIServer) ActivateWorkerNode(context.Context, *ActivateWorkerNodeRequest) (*ActivateWorkerNodeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ActivateWorkerNode not implemented")
}
func (UnimplementedAPIServer) ActivateCoordinator(context.Context, *ActivateCoordinatorRequest) (*ActivateCoordinatorResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ActivateCoordinator not implemented")
func (UnimplementedAPIServer) ActivateControlPlaneNode(context.Context, *ActivateControlPlaneNodeRequest) (*ActivateControlPlaneNodeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ActivateControlPlaneNode not implemented")
}
func (UnimplementedAPIServer) mustEmbedUnimplementedAPIServer() {}
@ -84,38 +84,38 @@ func RegisterAPIServer(s grpc.ServiceRegistrar, srv APIServer) {
s.RegisterService(&API_ServiceDesc, srv)
}
func _API_ActivateNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ActivateNodeRequest)
func _API_ActivateWorkerNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ActivateWorkerNodeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(APIServer).ActivateNode(ctx, in)
return srv.(APIServer).ActivateWorkerNode(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pubapi.API/ActivateNode",
FullMethod: "/activation.API/ActivateWorkerNode",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).ActivateNode(ctx, req.(*ActivateNodeRequest))
return srv.(APIServer).ActivateWorkerNode(ctx, req.(*ActivateWorkerNodeRequest))
}
return interceptor(ctx, in, info, handler)
}
func _API_ActivateCoordinator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ActivateCoordinatorRequest)
func _API_ActivateControlPlaneNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ActivateControlPlaneNodeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(APIServer).ActivateCoordinator(ctx, in)
return srv.(APIServer).ActivateControlPlaneNode(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pubapi.API/ActivateCoordinator",
FullMethod: "/activation.API/ActivateControlPlaneNode",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).ActivateCoordinator(ctx, req.(*ActivateCoordinatorRequest))
return srv.(APIServer).ActivateControlPlaneNode(ctx, req.(*ActivateControlPlaneNodeRequest))
}
return interceptor(ctx, in, info, handler)
}
@ -124,16 +124,16 @@ func _API_ActivateCoordinator_Handler(srv interface{}, ctx context.Context, dec
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var API_ServiceDesc = grpc.ServiceDesc{
ServiceName: "pubapi.API",
ServiceName: "activation.API",
HandlerType: (*APIServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ActivateNode",
Handler: _API_ActivateNode_Handler,
MethodName: "ActivateWorkerNode",
Handler: _API_ActivateWorkerNode_Handler,
},
{
MethodName: "ActivateCoordinator",
Handler: _API_ActivateCoordinator_Handler,
MethodName: "ActivateControlPlaneNode",
Handler: _API_ActivateControlPlaneNode_Handler,
},
},
Streams: []grpc.StreamDesc{},

View File

@ -16,8 +16,10 @@ import (
bootstraputil "k8s.io/cluster-bootstrap/token/util"
"k8s.io/klog/v2"
bootstraptoken "k8s.io/kubernetes/cmd/kubeadm/app/apis/bootstraptoken/v1"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
tokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/copycerts"
"k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin"
)
@ -98,3 +100,25 @@ func (k *Kubeadm) GetJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscov
CACertHashes: publicKeyPins,
}, nil
}
// GetControlPlaneCertificateKey uploads Kubernetes encrypted CA certificates to Kubernetes and returns the decryption key.
// The key can be used by new nodes to join the cluster as a control plane node.
func (k *Kubeadm) GetControlPlaneCertificateKey() (string, error) {
klog.V(6).Info("[kubeadm] Creating new random control plane certificate key")
key, err := copycerts.CreateCertificateKey()
if err != nil {
return "", fmt.Errorf("couldn't create control plane certificate key: %w", err)
}
klog.V(6).Info("[kubeadm] Uploading certs to Kubernetes")
cfg := &kubeadmapi.InitConfiguration{
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
CertificatesDir: constants.KubeadmCertificateDir,
},
}
if err := copycerts.UploadCerts(k.client, cfg, key); err != nil {
return "", fmt.Errorf("uploading certs: %w", err)
}
return key, nil
}

View File

@ -1,6 +1,7 @@
package kubeadm
import (
"context"
"testing"
"time"
@ -9,7 +10,12 @@ import (
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
fakecorev1 "k8s.io/client-go/kubernetes/typed/core/v1/fake"
)
func TestGetJoinToken(t *testing.T) {
@ -88,3 +94,67 @@ kind: Config`,
})
}
}
func TestGetControlPlaneCertificateKey(t *testing.T) {
testCases := map[string]struct {
wantErr bool
client clientset.Interface
}{
"success": {
client: fake.NewSimpleClientset(),
wantErr: false,
},
"failure": {
client: &failingClient{
fake.NewSimpleClientset(),
},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
client := &Kubeadm{
client: tc.client,
}
_, err := client.GetControlPlaneCertificateKey()
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
}
})
}
}
type failingClient struct {
*fake.Clientset
}
func (f *failingClient) CoreV1() corev1.CoreV1Interface {
return &failingCoreV1{
&fakecorev1.FakeCoreV1{Fake: &f.Clientset.Fake},
}
}
type failingCoreV1 struct {
*fakecorev1.FakeCoreV1
}
func (f *failingCoreV1) Secrets(namespace string) corev1.SecretInterface {
return &failingSecretInterface{
&fakecorev1.FakeSecrets{Fake: f.FakeCoreV1},
}
}
type failingSecretInterface struct {
*fakecorev1.FakeSecrets
}
// copycerts.UploadCerts will fail if a secret already exists.
func (f *failingSecretInterface) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Secret, error) {
return &v1.Secret{}, nil
}

View File

@ -57,62 +57,113 @@ func (s *Server) Run(creds credentials.TransportCredentials, port string) error
return grpcServer.Serve(lis)
}
// ActivateNode handles activation requests of Constellation worker nodes.
// ActivateWorkerNode handles activation requests of Constellation worker nodes.
// A worker node will receive:
// - stateful disk encryption key.
// - Kubernetes join token.
// - cluster and owner ID to taint the node as initialized.
func (s *Server) ActivateNode(ctx context.Context, req *proto.ActivateNodeRequest) (*proto.ActivateNodeResponse, error) {
klog.V(4).Info("ActivateNode: loading IDs")
var id attestationtypes.ID
if err := s.file.ReadJSON(filepath.Join(constants.ActivationBasePath, constants.ActivationIDFilename), &id); err != nil {
klog.Errorf("unable to load IDs: %s", err)
return nil, status.Errorf(codes.Internal, "unable to load IDs: %s", err)
}
klog.V(4).Info("ActivateNode: requesting disk encryption key")
stateDiskKey, err := s.dataKeyGetter.GetDataKey(ctx, req.DiskUuid, constants.StateDiskKeyLength)
func (s *Server) ActivateWorkerNode(ctx context.Context, req *proto.ActivateWorkerNodeRequest) (*proto.ActivateWorkerNodeResponse, error) {
nodeParameters, err := s.activateNode(ctx, "ActivateWorker", req.DiskUuid, req.NodeName)
if err != nil {
klog.Errorf("unable to get key for stateful disk: %s", err)
return nil, status.Errorf(codes.Internal, "unable to get key for stateful disk: %s", err)
}
klog.V(4).Info("ActivateNode: creating Kubernetes join token")
kubeArgs, err := s.joinTokenGetter.GetJoinToken(constants.KubernetesJoinTokenTTL)
if err != nil {
klog.Errorf("unable to generate Kubernetes join arguments: %s", err)
return nil, status.Errorf(codes.Internal, "unable to generate Kubernetes join arguments: %s", err)
}
klog.V(4).Info("ActivateNode: creating signed kubelet certificate")
kubeletCert, kubeletKey, err := s.ca.GetCertificate(req.NodeName)
if err != nil {
return nil, status.Errorf(codes.Internal, "unable to generate kubelet certificate: %s", err)
return nil, fmt.Errorf("ActivateNode failed: %w", err)
}
klog.V(4).Info("ActivateNode successful")
return &proto.ActivateNodeResponse{
StateDiskKey: stateDiskKey,
ClusterId: id.Cluster,
OwnerId: id.Owner,
ApiServerEndpoint: kubeArgs.APIServerEndpoint,
Token: kubeArgs.Token,
DiscoveryTokenCaCertHash: kubeArgs.CACertHashes[0],
KubeletCert: kubeletCert,
KubeletKey: kubeletKey,
return &proto.ActivateWorkerNodeResponse{
StateDiskKey: nodeParameters.stateDiskKey,
ClusterId: nodeParameters.id.Cluster,
OwnerId: nodeParameters.id.Owner,
ApiServerEndpoint: nodeParameters.kubeArgs.APIServerEndpoint,
Token: nodeParameters.kubeArgs.Token,
DiscoveryTokenCaCertHash: nodeParameters.kubeArgs.CACertHashes[0],
KubeletCert: nodeParameters.kubeletCert,
KubeletKey: nodeParameters.kubeletKey,
}, nil
}
// ActivateCoordinator handles activation requests of Constellation control-plane nodes.
func (s *Server) ActivateCoordinator(ctx context.Context, req *proto.ActivateCoordinatorRequest) (*proto.ActivateCoordinatorResponse, error) {
panic("not implemented")
// ActivateControlPlaneNode handles activation requests of Constellation control-plane nodes.
// A control-plane node will receive:
// - stateful disk encryption key.
// - Kubernetes join token.
// - cluster and owner ID to taint the node as initialized.
// - a decryption key for CA certificates uploaded to the Kubernetes cluster.
func (s *Server) ActivateControlPlaneNode(ctx context.Context, req *proto.ActivateControlPlaneNodeRequest) (*proto.ActivateControlPlaneNodeResponse, error) {
nodeParameters, err := s.activateNode(ctx, "ActivateControlPlane", req.DiskUuid, req.NodeName)
if err != nil {
return nil, fmt.Errorf("ActivateControlPlane failed: %w", err)
}
certKey, err := s.joinTokenGetter.GetControlPlaneCertificateKey()
if err != nil {
return nil, fmt.Errorf("ActivateControlPlane failed: %w", err)
}
klog.V(4).Info("ActivateControlPlane successful")
return &proto.ActivateControlPlaneNodeResponse{
StateDiskKey: nodeParameters.stateDiskKey,
ClusterId: nodeParameters.id.Cluster,
OwnerId: nodeParameters.id.Owner,
ApiServerEndpoint: nodeParameters.kubeArgs.APIServerEndpoint,
Token: nodeParameters.kubeArgs.Token,
DiscoveryTokenCaCertHash: nodeParameters.kubeArgs.CACertHashes[0],
KubeletCert: nodeParameters.kubeletCert,
KubeletKey: nodeParameters.kubeletKey,
CertificateKey: certKey,
}, nil
}
func (s *Server) activateNode(ctx context.Context, logPrefix, diskUUID, nodeName string) (nodeParameters, error) {
klog.V(4).Infof("%s: loading IDs", logPrefix)
var id attestationtypes.ID
if err := s.file.ReadJSON(filepath.Join(constants.ActivationBasePath, constants.ActivationIDFilename), &id); err != nil {
klog.Errorf("unable to load IDs: %s", err)
return nodeParameters{}, status.Errorf(codes.Internal, "unable to load IDs: %s", err)
}
klog.V(4).Infof("%s: requesting disk encryption key", logPrefix)
stateDiskKey, err := s.dataKeyGetter.GetDataKey(ctx, diskUUID, constants.StateDiskKeyLength)
if err != nil {
klog.Errorf("unable to get key for stateful disk: %s", err)
return nodeParameters{}, status.Errorf(codes.Internal, "unable to get key for stateful disk: %s", err)
}
klog.V(4).Infof("%s: creating Kubernetes join token", logPrefix)
kubeArgs, err := s.joinTokenGetter.GetJoinToken(constants.KubernetesJoinTokenTTL)
if err != nil {
klog.Errorf("unable to generate Kubernetes join arguments: %s", err)
return nodeParameters{}, status.Errorf(codes.Internal, "unable to generate Kubernetes join arguments: %s", err)
}
klog.V(4).Infof("%s: creating signed kubelet certificate", logPrefix)
kubeletCert, kubeletKey, err := s.ca.GetCertificate(nodeName)
if err != nil {
return nodeParameters{}, status.Errorf(codes.Internal, "unable to generate kubelet certificate: %s", err)
}
return nodeParameters{
stateDiskKey: stateDiskKey,
id: id,
kubeArgs: kubeArgs,
kubeletCert: kubeletCert,
kubeletKey: kubeletKey,
}, nil
}
type nodeParameters struct {
stateDiskKey []byte
id attestationtypes.ID
kubeArgs *kubeadmv1.BootstrapTokenDiscovery
kubeletCert []byte
kubeletKey []byte
}
// joinTokenGetter returns Kubernetes bootstrap (join) tokens.
type joinTokenGetter interface {
// GetJoinToken returns a bootstrap (join) token.
GetJoinToken(ttl time.Duration) (*kubeadmv1.BootstrapTokenDiscovery, error)
GetControlPlaneCertificateKey() (string, error)
}
// dataKeyGetter interacts with Constellation's key management system to retrieve keys.

View File

@ -8,7 +8,7 @@ import (
"testing"
"time"
proto "github.com/edgelesssys/constellation/activation/activationproto"
"github.com/edgelesssys/constellation/activation/activationproto"
attestationtypes "github.com/edgelesssys/constellation/internal/attestation/types"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
@ -133,7 +133,88 @@ func TestActivateNode(t *testing.T) {
}
api := New(file, tc.ca, tc.kubeadm, tc.kms)
resp, err := api.ActivateNode(context.Background(), &proto.ActivateNodeRequest{DiskUuid: "uuid"})
resp, err := api.activateNode(context.Background(), "test", "uuid", "test")
if tc.wantErr {
assert.Error(err)
return
}
var expectedIDs attestationtypes.ID
require.NoError(json.Unmarshal(tc.id, &expectedIDs))
require.NoError(err)
assert.Equal(tc.kms.dataKey, resp.stateDiskKey)
assert.Equal(expectedIDs.Cluster, resp.id.Cluster)
assert.Equal(expectedIDs.Owner, resp.id.Owner)
assert.Equal(tc.kubeadm.token.APIServerEndpoint, resp.kubeArgs.APIServerEndpoint)
assert.Equal(tc.kubeadm.token.CACertHashes[0], resp.kubeArgs.CACertHashes[0])
assert.Equal(tc.kubeadm.token.Token, resp.kubeArgs.Token)
assert.Equal(tc.ca.cert, resp.kubeletCert)
assert.Equal(tc.ca.key, resp.kubeletKey)
})
}
}
func TestActivateWorkerNode(t *testing.T) {
testKey := []byte{0x1, 0x2, 0x3}
testCert := []byte{0x4, 0x5, 0x6}
testID := attestationtypes.ID{
Owner: []byte{0x4, 0x5, 0x6},
Cluster: []byte{0x7, 0x8, 0x9},
}
testJoinToken := &kubeadmv1.BootstrapTokenDiscovery{
APIServerEndpoint: "192.0.2.1",
CACertHashes: []string{"hash"},
Token: "token",
}
testCases := map[string]struct {
kubeadm stubTokenGetter
kms stubKeyGetter
ca stubCA
id []byte
wantErr bool
}{
"success": {
kubeadm: stubTokenGetter{
token: testJoinToken,
},
kms: stubKeyGetter{
dataKey: testKey,
},
ca: stubCA{
cert: testCert,
key: testKey,
},
id: mustMarshalID(testID),
},
"activateNode fails": {
kubeadm: stubTokenGetter{
token: testJoinToken,
},
kms: stubKeyGetter{
getDataKeyErr: errors.New("error"),
},
ca: stubCA{
cert: testCert,
key: testKey,
},
id: mustMarshalID(testID),
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
file := file.NewHandler(afero.NewMemMapFs())
require.NoError(file.Write(filepath.Join(constants.ActivationBasePath, constants.ActivationIDFilename), tc.id, 0o644))
api := New(file, tc.ca, tc.kubeadm, tc.kms)
resp, err := api.ActivateWorkerNode(context.Background(), &activationproto.ActivateWorkerNodeRequest{DiskUuid: "uuid", NodeName: "test"})
if tc.wantErr {
assert.Error(err)
return
@ -155,6 +236,106 @@ func TestActivateNode(t *testing.T) {
}
}
func TestActivateControlPlaneNode(t *testing.T) {
someErr := errors.New("error")
testKey := []byte{0x1, 0x2, 0x3}
testCert := []byte{0x4, 0x5, 0x6}
testID := attestationtypes.ID{
Owner: []byte{0x4, 0x5, 0x6},
Cluster: []byte{0x7, 0x8, 0x9},
}
testJoinToken := &kubeadmv1.BootstrapTokenDiscovery{
APIServerEndpoint: "192.0.2.1",
CACertHashes: []string{"hash"},
Token: "token",
}
testCases := map[string]struct {
kubeadm stubTokenGetter
kms stubKeyGetter
ca stubCA
id []byte
wantErr bool
}{
"success": {
kubeadm: stubTokenGetter{
token: testJoinToken,
certificateKey: "test",
},
kms: stubKeyGetter{
dataKey: testKey,
},
ca: stubCA{
cert: testCert,
key: testKey,
},
id: mustMarshalID(testID),
},
"activateNode fails": {
kubeadm: stubTokenGetter{
token: testJoinToken,
certificateKey: "test",
},
kms: stubKeyGetter{
getDataKeyErr: someErr,
},
ca: stubCA{
cert: testCert,
key: testKey,
},
id: mustMarshalID(testID),
wantErr: true,
},
"GetControlPlaneCertificateKey fails": {
kubeadm: stubTokenGetter{
token: testJoinToken,
certificateKeyErr: someErr,
},
kms: stubKeyGetter{
dataKey: testKey,
},
ca: stubCA{
cert: testCert,
key: testKey,
},
id: mustMarshalID(testID),
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
file := file.NewHandler(afero.NewMemMapFs())
require.NoError(file.Write(filepath.Join(constants.ActivationBasePath, constants.ActivationIDFilename), tc.id, 0o644))
api := New(file, tc.ca, tc.kubeadm, tc.kms)
resp, err := api.ActivateControlPlaneNode(context.Background(), &activationproto.ActivateControlPlaneNodeRequest{DiskUuid: "uuid", NodeName: "test"})
if tc.wantErr {
assert.Error(err)
return
}
var expectedIDs attestationtypes.ID
require.NoError(json.Unmarshal(tc.id, &expectedIDs))
require.NoError(err)
assert.Equal(tc.kms.dataKey, resp.StateDiskKey)
assert.Equal(expectedIDs.Cluster, resp.ClusterId)
assert.Equal(expectedIDs.Owner, resp.OwnerId)
assert.Equal(tc.kubeadm.token.APIServerEndpoint, resp.ApiServerEndpoint)
assert.Equal(tc.kubeadm.token.CACertHashes[0], resp.DiscoveryTokenCaCertHash)
assert.Equal(tc.kubeadm.token.Token, resp.Token)
assert.Equal(tc.ca.cert, resp.KubeletCert)
assert.Equal(tc.ca.key, resp.KubeletKey)
assert.Equal(tc.kubeadm.certificateKey, resp.CertificateKey)
})
}
}
func mustMarshalID(id attestationtypes.ID) []byte {
b, err := json.Marshal(id)
if err != nil {
@ -164,14 +345,20 @@ func mustMarshalID(id attestationtypes.ID) []byte {
}
type stubTokenGetter struct {
token *kubeadmv1.BootstrapTokenDiscovery
getJoinTokenErr error
token *kubeadmv1.BootstrapTokenDiscovery
getJoinTokenErr error
certificateKey string
certificateKeyErr error
}
func (f stubTokenGetter) GetJoinToken(time.Duration) (*kubeadmv1.BootstrapTokenDiscovery, error) {
return f.token, f.getJoinTokenErr
}
func (f stubTokenGetter) GetControlPlaneCertificateKey() (string, error) {
return f.certificateKey, f.certificateKeyErr
}
type stubKeyGetter struct {
dataKey []byte
getDataKeyErr error

View File

@ -41,7 +41,12 @@ func NewActivationDaemonset(csp, measurementsJSON, idJSON string) *activationDae
{
APIGroups: []string{""},
Resources: []string{"secrets"},
Verbs: []string{"get", "list", "create"},
Verbs: []string{"get", "list", "create", "update"},
},
{
APIGroups: []string{"rbac.authorization.k8s.io"},
Resources: []string{"roles", "rolebindings"},
Verbs: []string{"create", "update"},
},
},
},

View File

@ -47,6 +47,7 @@ const (
MasterSecretFilename = "constellation-mastersecret.base64"
WGQuickConfigFilename = "wg0.conf"
CoreOSAdminConfFilename = "/etc/kubernetes/admin.conf"
KubeadmCertificateDir = "/etc/kubernetes/pki"
// Filenames for the Activation service.
ActivationBasePath = "/var/config"