diff --git a/activation/README.md b/activation/README.md index fa557c738..31dbf0394 100644 --- a/activation/README.md +++ b/activation/README.md @@ -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 . ``` diff --git a/activation/activationproto/activation.pb.go b/activation/activationproto/activation.pb.go index 6dba4dd0c..9e2a8176e 100644 --- a/activation/activationproto/activation.pb.go +++ b/activation/activationproto/activation.pb.go @@ -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: diff --git a/activation/activationproto/activation.proto b/activation/activationproto/activation.proto index 0fe416838..e698715a7 100644 --- a/activation/activationproto/activation.proto +++ b/activation/activationproto/activation.proto @@ -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; } diff --git a/activation/activationproto/activation_grpc.pb.go b/activation/activationproto/activation_grpc.pb.go index 58df83e43..650a9eee6 100644 --- a/activation/activationproto/activation_grpc.pb.go +++ b/activation/activationproto/activation_grpc.pb.go @@ -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{}, diff --git a/activation/kubeadm/kubeadm.go b/activation/kubeadm/kubeadm.go index c401168d0..b1edce3de 100644 --- a/activation/kubeadm/kubeadm.go +++ b/activation/kubeadm/kubeadm.go @@ -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 +} diff --git a/activation/kubeadm/kubeadm_test.go b/activation/kubeadm/kubeadm_test.go index 531d7fcfe..ed9e4d542 100644 --- a/activation/kubeadm/kubeadm_test.go +++ b/activation/kubeadm/kubeadm_test.go @@ -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 +} diff --git a/activation/server/server.go b/activation/server/server.go index 34b2861b6..4babd3504 100644 --- a/activation/server/server.go +++ b/activation/server/server.go @@ -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. diff --git a/activation/server/server_test.go b/activation/server/server_test.go index 148f482ae..c8bba76aa 100644 --- a/activation/server/server_test.go +++ b/activation/server/server_test.go @@ -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 diff --git a/coordinator/kubernetes/k8sapi/resources/activation.go b/coordinator/kubernetes/k8sapi/resources/activation.go index 705fdc4f5..556fa4d88 100644 --- a/coordinator/kubernetes/k8sapi/resources/activation.go +++ b/coordinator/kubernetes/k8sapi/resources/activation.go @@ -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"}, }, }, }, diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 2ed3e551f..09b26d918 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -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"