From 88a6ca036f60511dc1ff6ecd488405ecaf343ff1 Mon Sep 17 00:00:00 2001 From: irenarindos Date: Tue, 10 May 2022 10:03:08 -0400 Subject: [PATCH] feat(pki): Add PKI schemas and protos --- Makefile | 2 + .../99/02_worker_authentication.up.sql | 96 ++++ .../servers/store/v1/root_certificate.proto | 42 ++ .../servers/store/v1/worker_auth.proto | 53 ++ internal/servers/options.go | 51 +- internal/servers/options_test.go | 45 ++ internal/servers/root_certificate.go | 125 +++++ internal/servers/state.go | 20 + internal/servers/store/root_certificate.pb.go | 251 +++++++++ internal/servers/store/worker_auth.pb.go | 326 +++++++++++ internal/servers/testing.go | 103 ++++ internal/servers/worker_auth.go | 183 ++++++ internal/servers/workerauth_store_test.go | 524 ++++++++++++++++++ 13 files changed, 1813 insertions(+), 8 deletions(-) create mode 100644 internal/db/schema/migrations/oss/postgres/99/02_worker_authentication.up.sql create mode 100644 internal/proto/controller/storage/servers/store/v1/root_certificate.proto create mode 100644 internal/proto/controller/storage/servers/store/v1/worker_auth.proto create mode 100644 internal/servers/root_certificate.go create mode 100644 internal/servers/state.go create mode 100644 internal/servers/store/root_certificate.pb.go create mode 100644 internal/servers/store/worker_auth.pb.go create mode 100644 internal/servers/testing.go create mode 100644 internal/servers/worker_auth.go create mode 100644 internal/servers/workerauth_store_test.go diff --git a/Makefile b/Makefile index 8bf0244ae..bdd427988 100644 --- a/Makefile +++ b/Makefile @@ -149,6 +149,8 @@ protobuild: @protoc-go-inject-tag -input=./internal/kms/store/oidc_key.pb.go @protoc-go-inject-tag -input=./internal/servers/store/controller.pb.go @protoc-go-inject-tag -input=./internal/servers/store/worker.pb.go + @protoc-go-inject-tag -input=./internal/servers/store/root_certificate.pb.go + @protoc-go-inject-tag -input=./internal/servers/store/worker_auth.pb.go @protoc-go-inject-tag -input=./internal/target/store/target.pb.go @protoc-go-inject-tag -input=./internal/target/targettest/store/target.pb.go @protoc-go-inject-tag -input=./internal/target/tcp/store/target.pb.go diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_authentication.up.sql b/internal/db/schema/migrations/oss/postgres/99/02_worker_authentication.up.sql new file mode 100644 index 000000000..d64967532 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/99/02_worker_authentication.up.sql @@ -0,0 +1,96 @@ +begin; + +create table worker_auth_operational_indicator_enm( + state text primary key + constraint only_predefined_worker_auth_states_allowed + check ( + state in ('next','current') + ) +); + +comment on table worker_auth_operational_indicator_enm is + 'worker_auth_operational_indicator_enm is an enumeration table representing the state of certificates used for worker authentication.'; + +insert into worker_auth_operational_indicator_enm (state) +values + ('next'), + ('current'); + +create table worker_auth_ca_certificate( + serial_number numeric, + certificate bytea not null + constraint certificate_must_not_be_empty + check(length(certificate) > 0), + not_valid_before wt_timestamp not null, + not_valid_after wt_timestamp not null + constraint not_valid_before_must_be_before_not_valid_after + check(not_valid_before < not_valid_after), + public_key bytea primary key + constraint public_key_must_not_be_empty + check(length(public_key) > 0), + private_key bytea not null -- encrypted PEM encoded private key + constraint private_key_must_not_be_empty + check(length(private_key) > 0), + key_id kms_private_id not null -- key used to encrypt entries via wrapping wrapper. + constraint kms_data_key_version_fkey + references kms_data_key_version (private_id) + on delete restrict + on update cascade, + state text unique not null + constraint worker_auth_operational_indicator_enm_fkey + references worker_auth_operational_indicator_enm(state) + on delete restrict + on update cascade +); + +comment on table worker_auth_ca_certificate is + 'worker_auth_ca_certificate is a table where each row represents a root certificate for used for worker authentication.'; + +create table worker_auth_authorized( + worker_key_identifier text primary key, -- The public key id for this WorkerAuth entry, generated from the signing pub key + worker_id wt_public_id not null + constraint server_worker_fkey + references server_worker (public_id) + on delete cascade + on update cascade, + worker_signing_pub_key bytea not null + constraint worker_signing_pub_key_must_not_be_empty + check(length(worker_signing_pub_key) > 0), + worker_encryption_pub_key bytea not null + constraint worker_encryption_pub_key_must_not_be_empty + check(length(worker_encryption_pub_key) > 0), + controller_encryption_priv_key bytea not null -- encrypted PEM encoded private key + constraint controller_encryption_priv_key_must_not_be_empty + check(length(controller_encryption_priv_key) > 0), + key_id kms_private_id not null -- key used to encrypt entries via wrapping wrapper. + constraint kms_data_key_version_fkey + references kms_data_key_version (private_id) + on delete restrict + on update cascade, + nonce bytea +); + +comment on table worker_auth_authorized is + 'worker_auth_authorized is a table where each row represents key and cert data associated with an authorized worker.'; + +create table worker_auth_certificate_bundle( + certificate_public_key bytea not null + constraint worker_auth_ca_certificate_fkey + references worker_auth_ca_certificate(public_key) + on delete cascade + on update cascade, + worker_key_identifier text not null + constraint worker_auth_authorized_fkey + references worker_auth_authorized(worker_key_identifier) + on delete cascade + on update cascade, + cert_bundle bytea + constraint current_cert_bundle_must_not_be_empty + check(length(cert_bundle) > 0), + primary key(certificate_public_key, worker_key_identifier) +); + +comment on table worker_auth_certificate_bundle is + 'worker_auth_certificate_bundle is a table where each row represents a cert bundle issued by a ca certificate for a worker.'; + +commit; \ No newline at end of file diff --git a/internal/proto/controller/storage/servers/store/v1/root_certificate.proto b/internal/proto/controller/storage/servers/store/v1/root_certificate.proto new file mode 100644 index 000000000..16a4d2907 --- /dev/null +++ b/internal/proto/controller/storage/servers/store/v1/root_certificate.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; + +// Package store provides protobufs for storing types in the pki package. +package controller.storage.servers.store.v1; + +import "controller/storage/timestamp/v1/timestamp.proto"; + +option go_package = "github.com/hashicorp/boundary/internal/servers/store;store"; + +// RootCertificate contains all fields related to a RootCertificate resource +message RootCertificate { + // The serial number of the root certificate + // @inject_tag: `gorm:"not_null"` + uint64 serial_number = 10; + + // Certificate is the PEM encoded certificate. + // @inject_tag: `gorm:"not_null"` + bytes certificate = 20; + + // Not valid before is the timestamp at which this certificate's validity period starts + storage.timestamp.v1.Timestamp not_valid_before = 30; + + // Not valid after is the timestamp at which this certificate's validity period ends + storage.timestamp.v1.Timestamp not_valid_after = 40; + + // The public key associated with this certificate + // @inject_tag: `gorm:"not_null"` + bytes public_key = 50; + + // The private key associated with this certificate + // This is a ciphertext field + // @inject_tag: `gorm:"not_null"` + bytes private_key = 60; + + // The id of the kms database key used for encrypting this entry. + // @inject_tag: `gorm:"not_null"` + string key_id = 70; + + // State is an enum value indicating if this is the next or current root cert + // @inject_tag: `gorm:"not_null"` + string state = 80; +} diff --git a/internal/proto/controller/storage/servers/store/v1/worker_auth.proto b/internal/proto/controller/storage/servers/store/v1/worker_auth.proto new file mode 100644 index 000000000..197ea4005 --- /dev/null +++ b/internal/proto/controller/storage/servers/store/v1/worker_auth.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; + +// Package store provides protobufs for storing types in the pki package. +package controller.storage.servers.store.v1; + +option go_package = "github.com/hashicorp/boundary/internal/servers/store;store"; + +// WorkerAuth contains all fields related to an authorized Worker resource +message WorkerAuth { + // The key id for this worker auth record, generated from the signing pub key + // @inject_tag: `gorm:"primary_key"` + string worker_key_identifier = 10; + + // The worker id this worker authentication record is for + // @inject_tag: `gorm:"not_null"` + string worker_id = 20; + + // The worker's public signing key + // @inject_tag: `gorm:"not_null"` + bytes worker_signing_pub_key = 30; + + // The worker's public encryption key + // @inject_tag: `gorm:"not_null"` + bytes worker_encryption_pub_key = 40; + + // The private key associated with this certificate + // This is a ciphertext field + // @inject_tag: `gorm:"not_null"` + bytes controller_encryption_priv_key = 50; + + // The id of the kms database key used for encrypting this entry. + // @inject_tag: `gorm:"not_null"` + string key_id = 60; + + // Nonce used by a worker in authenticating + // @inject_tag: `gorm:"default:null"` + bytes nonce = 70; +} + +// WorkerCertBundle contains all fields related to a WorkerCertBundle resource +message WorkerCertBundle { + // The public key of the issuing root certificate + // @inject_tag: `gorm:"not_null"` + bytes certificate_public_key = 10; + + // The WorkerAuth worker_key_identifier this cert bundle record is for + // @inject_tag: `gorm:"not_null"` + string worker_key_identifier = 20; + + // CertBundle is the marshaled protobuf certificate bundle for a WorkerAuth + // @inject_tag: `gorm:"not_null"` + bytes cert_bundle = 30; +} diff --git a/internal/servers/options.go b/internal/servers/options.go index d58d9dd84..6d1e274f3 100644 --- a/internal/servers/options.go +++ b/internal/servers/options.go @@ -18,14 +18,19 @@ type Option func(*options) // options = how options are represented type options struct { - withName string - withDescription string - withAddress string - withPublicId string - withLimit int - withLiveness time.Duration - withUpdateTags bool - withWorkerTags []*Tag + withName string + withDescription string + withAddress string + withPublicId string + withLimit int + withLiveness time.Duration + withUpdateTags bool + withWorkerTags []*Tag + withWorkerKeyIdentifier string + withWorkerKeys WorkerKeys + withControllerEncryptionPrivateKey []byte + withKeyId string + withNonce []byte } func getDefaultOptions() options { @@ -93,3 +98,33 @@ func WithWorkerTags(tags ...*Tag) Option { o.withWorkerTags = tags } } + +func WithWorkerKeyIdentifier(workerKeyIdentifier string) Option { + return func(o *options) { + o.withWorkerKeyIdentifier = workerKeyIdentifier + } +} + +func WithWorkerKeys(workerKeys WorkerKeys) Option { + return func(o *options) { + o.withWorkerKeys = workerKeys + } +} + +func WithControllerEncryptionPrivateKey(controllerKey []byte) Option { + return func(o *options) { + o.withControllerEncryptionPrivateKey = controllerKey + } +} + +func WithKeyId(keyId string) Option { + return func(o *options) { + o.withKeyId = keyId + } +} + +func WithNonce(nonce []byte) Option { + return func(o *options) { + o.withNonce = nonce + } +} diff --git a/internal/servers/options_test.go b/internal/servers/options_test.go index 05b9d4070..2e4e21381 100644 --- a/internal/servers/options_test.go +++ b/internal/servers/options_test.go @@ -65,4 +65,49 @@ func Test_GetOpts(t *testing.T) { testOpts.withWorkerTags = tags assert.Equal(t, opts, testOpts) }) + t.Run("WithWorkerKeys", func(t *testing.T) { + assert := assert.New(t) + testOpts := getDefaultOptions() + + keys := WorkerKeys{ + workerEncryptionPubKey: populateBytes(20), + workerSigningPubKey: populateBytes(20), + } + + opts := getOpts(WithWorkerKeys(keys)) + testOpts = getDefaultOptions() + testOpts.withWorkerKeys = keys + assert.Equal(opts, testOpts) + }) + t.Run("WithControllerEncryptionPrivateKey", func(t *testing.T) { + assert := assert.New(t) + testOpts := getDefaultOptions() + + key := populateBytes(20) + + opts := getOpts(WithControllerEncryptionPrivateKey(key)) + testOpts = getDefaultOptions() + testOpts.withControllerEncryptionPrivateKey = key + assert.Equal(opts, testOpts) + }) + t.Run("WithKeyId", func(t *testing.T) { + assert := assert.New(t) + testOpts := getDefaultOptions() + + opts := getOpts(WithKeyId("hi i'm another key id")) + testOpts = getDefaultOptions() + testOpts.withKeyId = "hi i'm another key id" + assert.Equal(opts, testOpts) + }) + t.Run("WithNonce", func(t *testing.T) { + assert := assert.New(t) + testOpts := getDefaultOptions() + + nonce := populateBytes(20) + + opts := getOpts(WithNonce(nonce)) + testOpts = getDefaultOptions() + testOpts.withNonce = nonce + assert.Equal(opts, testOpts) + }) } diff --git a/internal/servers/root_certificate.go b/internal/servers/root_certificate.go new file mode 100644 index 000000000..4e935fa95 --- /dev/null +++ b/internal/servers/root_certificate.go @@ -0,0 +1,125 @@ +package servers + +import ( + "context" + "fmt" + + "github.com/hashicorp/boundary/internal/errors" + + timestamp "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/servers/store" + "google.golang.org/protobuf/proto" +) + +// RootCertificate contains fields related to a RootCertificate resource +// This includes public/ private keys, the PEM encoded certificate, and the certificate validity period +type RootCertificate struct { + *store.RootCertificate + tableName string `gorm:"-"` +} + +type RootCertificateKeys struct { + publicKey []byte + privateKey []byte +} + +func newRootCertificate(ctx context.Context, serialNumber uint64, certificate []byte, notValidBefore, notValidAfter *timestamp.Timestamp, + rootCertificateKeys RootCertificateKeys, keyId string, state CertificateState, +) (*RootCertificate, error) { + const op = "servers.newRootCertificate" + + if &serialNumber == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no serialNumber") + } + if certificate == nil || len(certificate) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty certificate") + } + if notValidAfter == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no notValidAfter") + } + if notValidBefore == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no notValidBefore") + } + if rootCertificateKeys.publicKey == nil || len(rootCertificateKeys.publicKey) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty publicKey") + } + if rootCertificateKeys.privateKey == nil || len(rootCertificateKeys.privateKey) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty privateKey") + } + if keyId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no keyId") + } + if !validState(state) { + return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is not a valid certificate state", state)) + } + + l := &RootCertificate{ + RootCertificate: &store.RootCertificate{ + SerialNumber: serialNumber, + Certificate: certificate, + NotValidAfter: notValidAfter, + NotValidBefore: notValidBefore, + PublicKey: rootCertificateKeys.publicKey, + PrivateKey: rootCertificateKeys.privateKey, + KeyId: keyId, + State: string(state), + }, + } + return l, nil +} + +func AllocRootCertificate() *RootCertificate { + return &RootCertificate{ + RootCertificate: &store.RootCertificate{}, + } +} + +func (r *RootCertificate) clone() *RootCertificate { + cp := proto.Clone(r.RootCertificate) + return &RootCertificate{ + RootCertificate: cp.(*store.RootCertificate), + } +} + +func (r *RootCertificate) ValidateNewRootCertificate(ctx context.Context) error { + const op = "servers.(RootCertificate).ValidateNewRootCertificate" + if &r.SerialNumber == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing SerialNumber") + } + if r.Certificate == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing Certificate") + } + if r.NotValidBefore.GetTimestamp().AsTime().IsZero() { + return errors.New(ctx, errors.InvalidParameter, op, "missing not valid before timestamp") + } + if r.NotValidAfter.GetTimestamp().AsTime().IsZero() { + return errors.New(ctx, errors.InvalidParameter, op, "missing not valid after timestamp") + } + if r.PublicKey == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing public key") + } + if r.PrivateKey == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing private key") + } + if r.KeyId == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing key id") + } + if r.State == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing state") + } + + return nil +} + +// TableName returns the table name. +func (r *RootCertificate) TableName() string { + if r.tableName != "" { + return r.tableName + } + return "worker_auth_ca_certificate" +} + +// SetTableName sets the table name. +func (r *RootCertificate) SetTableName(n string) { + r.tableName = n +} diff --git a/internal/servers/state.go b/internal/servers/state.go new file mode 100644 index 000000000..2cbf6c295 --- /dev/null +++ b/internal/servers/state.go @@ -0,0 +1,20 @@ +package servers + +// CertificateState defines the possible states for a workerauth certificate +type CertificateState string + +const ( + UnknownState CertificateState = "unknown" + CurrentState CertificateState = "current" + NextState CertificateState = "next" +) + +func validState(s CertificateState) bool { + st := CertificateState(s) + switch st { + case CurrentState, NextState: + return true + default: + return false + } +} diff --git a/internal/servers/store/root_certificate.pb.go b/internal/servers/store/root_certificate.pb.go new file mode 100644 index 000000000..7e8feee6a --- /dev/null +++ b/internal/servers/store/root_certificate.pb.go @@ -0,0 +1,251 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: controller/storage/servers/store/v1/root_certificate.proto + +// Package store provides protobufs for storing types in the pki package. + +package store + +import ( + timestamp "github.com/hashicorp/boundary/internal/db/timestamp" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// RootCertificate contains all fields related to a RootCertificate resource +type RootCertificate struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The serial number of the root certificate + // @inject_tag: `gorm:"not_null"` + SerialNumber uint64 `protobuf:"varint,10,opt,name=serial_number,json=serialNumber,proto3" json:"serial_number,omitempty" gorm:"not_null"` + // Certificate is the PEM encoded certificate. + // @inject_tag: `gorm:"not_null"` + Certificate []byte `protobuf:"bytes,20,opt,name=certificate,proto3" json:"certificate,omitempty" gorm:"not_null"` + // Not valid before is the timestamp at which this certificate's validity period starts + NotValidBefore *timestamp.Timestamp `protobuf:"bytes,30,opt,name=not_valid_before,json=notValidBefore,proto3" json:"not_valid_before,omitempty"` + // Not valid after is the timestamp at which this certificate's validity period ends + NotValidAfter *timestamp.Timestamp `protobuf:"bytes,40,opt,name=not_valid_after,json=notValidAfter,proto3" json:"not_valid_after,omitempty"` + // The public key associated with this certificate + // @inject_tag: `gorm:"not_null"` + PublicKey []byte `protobuf:"bytes,50,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty" gorm:"not_null"` + // The private key associated with this certificate + // This is a ciphertext field + // @inject_tag: `gorm:"not_null"` + PrivateKey []byte `protobuf:"bytes,60,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty" gorm:"not_null"` + // The id of the kms database key used for encrypting this entry. + // @inject_tag: `gorm:"not_null"` + KeyId string `protobuf:"bytes,70,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"not_null"` + // State is an enum value indicating if this is the next or current root cert + // @inject_tag: `gorm:"not_null"` + State string `protobuf:"bytes,80,opt,name=state,proto3" json:"state,omitempty" gorm:"not_null"` +} + +func (x *RootCertificate) Reset() { + *x = RootCertificate{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RootCertificate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RootCertificate) ProtoMessage() {} + +func (x *RootCertificate) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RootCertificate.ProtoReflect.Descriptor instead. +func (*RootCertificate) Descriptor() ([]byte, []int) { + return file_controller_storage_servers_store_v1_root_certificate_proto_rawDescGZIP(), []int{0} +} + +func (x *RootCertificate) GetSerialNumber() uint64 { + if x != nil { + return x.SerialNumber + } + return 0 +} + +func (x *RootCertificate) GetCertificate() []byte { + if x != nil { + return x.Certificate + } + return nil +} + +func (x *RootCertificate) GetNotValidBefore() *timestamp.Timestamp { + if x != nil { + return x.NotValidBefore + } + return nil +} + +func (x *RootCertificate) GetNotValidAfter() *timestamp.Timestamp { + if x != nil { + return x.NotValidAfter + } + return nil +} + +func (x *RootCertificate) GetPublicKey() []byte { + if x != nil { + return x.PublicKey + } + return nil +} + +func (x *RootCertificate) GetPrivateKey() []byte { + if x != nil { + return x.PrivateKey + } + return nil +} + +func (x *RootCertificate) GetKeyId() string { + if x != nil { + return x.KeyId + } + return "" +} + +func (x *RootCertificate) GetState() string { + if x != nil { + return x.State + } + return "" +} + +var File_controller_storage_servers_store_v1_root_certificate_proto protoreflect.FileDescriptor + +var file_controller_storage_servers_store_v1_root_certificate_proto_rawDesc = []byte{ + 0x0a, 0x3a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, + 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0xef, 0x02, 0x0a, 0x0f, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x73, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x63, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x54, 0x0a, + 0x10, 0x6e, 0x6f, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, + 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6e, 0x6f, 0x74, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x42, 0x65, 0x66, + 0x6f, 0x72, 0x65, 0x12, 0x52, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x41, 0x66, 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, + 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x14, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_storage_servers_store_v1_root_certificate_proto_rawDescOnce sync.Once + file_controller_storage_servers_store_v1_root_certificate_proto_rawDescData = file_controller_storage_servers_store_v1_root_certificate_proto_rawDesc +) + +func file_controller_storage_servers_store_v1_root_certificate_proto_rawDescGZIP() []byte { + file_controller_storage_servers_store_v1_root_certificate_proto_rawDescOnce.Do(func() { + file_controller_storage_servers_store_v1_root_certificate_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_servers_store_v1_root_certificate_proto_rawDescData) + }) + return file_controller_storage_servers_store_v1_root_certificate_proto_rawDescData +} + +var file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_storage_servers_store_v1_root_certificate_proto_goTypes = []interface{}{ + (*RootCertificate)(nil), // 0: controller.storage.servers.store.v1.RootCertificate + (*timestamp.Timestamp)(nil), // 1: controller.storage.timestamp.v1.Timestamp +} +var file_controller_storage_servers_store_v1_root_certificate_proto_depIdxs = []int32{ + 1, // 0: controller.storage.servers.store.v1.RootCertificate.not_valid_before:type_name -> controller.storage.timestamp.v1.Timestamp + 1, // 1: controller.storage.servers.store.v1.RootCertificate.not_valid_after:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_controller_storage_servers_store_v1_root_certificate_proto_init() } +func file_controller_storage_servers_store_v1_root_certificate_proto_init() { + if File_controller_storage_servers_store_v1_root_certificate_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RootCertificate); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_storage_servers_store_v1_root_certificate_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_storage_servers_store_v1_root_certificate_proto_goTypes, + DependencyIndexes: file_controller_storage_servers_store_v1_root_certificate_proto_depIdxs, + MessageInfos: file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes, + }.Build() + File_controller_storage_servers_store_v1_root_certificate_proto = out.File + file_controller_storage_servers_store_v1_root_certificate_proto_rawDesc = nil + file_controller_storage_servers_store_v1_root_certificate_proto_goTypes = nil + file_controller_storage_servers_store_v1_root_certificate_proto_depIdxs = nil +} diff --git a/internal/servers/store/worker_auth.pb.go b/internal/servers/store/worker_auth.pb.go new file mode 100644 index 000000000..119db8fee --- /dev/null +++ b/internal/servers/store/worker_auth.pb.go @@ -0,0 +1,326 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: controller/storage/servers/store/v1/worker_auth.proto + +// Package store provides protobufs for storing types in the pki package. + +package store + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// WorkerAuth contains all fields related to an authorized Worker resource +type WorkerAuth struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The key id for this worker auth record, generated from the signing pub key + // @inject_tag: `gorm:"primary_key"` + WorkerKeyIdentifier string `protobuf:"bytes,10,opt,name=worker_key_identifier,json=workerKeyIdentifier,proto3" json:"worker_key_identifier,omitempty" gorm:"primary_key"` + // The worker id this worker authentication record is for + // @inject_tag: `gorm:"not_null"` + WorkerId string `protobuf:"bytes,20,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty" gorm:"not_null"` + // The worker's public signing key + // @inject_tag: `gorm:"not_null"` + WorkerSigningPubKey []byte `protobuf:"bytes,30,opt,name=worker_signing_pub_key,json=workerSigningPubKey,proto3" json:"worker_signing_pub_key,omitempty" gorm:"not_null"` + // The worker's public encryption key + // @inject_tag: `gorm:"not_null"` + WorkerEncryptionPubKey []byte `protobuf:"bytes,40,opt,name=worker_encryption_pub_key,json=workerEncryptionPubKey,proto3" json:"worker_encryption_pub_key,omitempty" gorm:"not_null"` + // The private key associated with this certificate + // This is a ciphertext field + // @inject_tag: `gorm:"not_null"` + ControllerEncryptionPrivKey []byte `protobuf:"bytes,50,opt,name=controller_encryption_priv_key,json=controllerEncryptionPrivKey,proto3" json:"controller_encryption_priv_key,omitempty" gorm:"not_null"` + // The id of the kms database key used for encrypting this entry. + // @inject_tag: `gorm:"not_null"` + KeyId string `protobuf:"bytes,60,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"not_null"` + // Nonce used by a worker in authenticating + // @inject_tag: `gorm:"default:null"` + Nonce []byte `protobuf:"bytes,70,opt,name=nonce,proto3" json:"nonce,omitempty" gorm:"default:null"` +} + +func (x *WorkerAuth) Reset() { + *x = WorkerAuth{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WorkerAuth) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkerAuth) ProtoMessage() {} + +func (x *WorkerAuth) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkerAuth.ProtoReflect.Descriptor instead. +func (*WorkerAuth) Descriptor() ([]byte, []int) { + return file_controller_storage_servers_store_v1_worker_auth_proto_rawDescGZIP(), []int{0} +} + +func (x *WorkerAuth) GetWorkerKeyIdentifier() string { + if x != nil { + return x.WorkerKeyIdentifier + } + return "" +} + +func (x *WorkerAuth) GetWorkerId() string { + if x != nil { + return x.WorkerId + } + return "" +} + +func (x *WorkerAuth) GetWorkerSigningPubKey() []byte { + if x != nil { + return x.WorkerSigningPubKey + } + return nil +} + +func (x *WorkerAuth) GetWorkerEncryptionPubKey() []byte { + if x != nil { + return x.WorkerEncryptionPubKey + } + return nil +} + +func (x *WorkerAuth) GetControllerEncryptionPrivKey() []byte { + if x != nil { + return x.ControllerEncryptionPrivKey + } + return nil +} + +func (x *WorkerAuth) GetKeyId() string { + if x != nil { + return x.KeyId + } + return "" +} + +func (x *WorkerAuth) GetNonce() []byte { + if x != nil { + return x.Nonce + } + return nil +} + +// WorkerCertBundle contains all fields related to a WorkerCertBundle resource +type WorkerCertBundle struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The public key of the issuing root certificate + // @inject_tag: `gorm:"not_null"` + CertificatePublicKey []byte `protobuf:"bytes,10,opt,name=certificate_public_key,json=certificatePublicKey,proto3" json:"certificate_public_key,omitempty" gorm:"not_null"` + // The WorkerAuth worker_key_identifier this cert bundle record is for + // @inject_tag: `gorm:"not_null"` + WorkerKeyIdentifier string `protobuf:"bytes,20,opt,name=worker_key_identifier,json=workerKeyIdentifier,proto3" json:"worker_key_identifier,omitempty" gorm:"not_null"` + // CertBundle is the marshaled protobuf certificate bundle for a WorkerAuth + // @inject_tag: `gorm:"not_null"` + CertBundle []byte `protobuf:"bytes,30,opt,name=cert_bundle,json=certBundle,proto3" json:"cert_bundle,omitempty" gorm:"not_null"` +} + +func (x *WorkerCertBundle) Reset() { + *x = WorkerCertBundle{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WorkerCertBundle) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkerCertBundle) ProtoMessage() {} + +func (x *WorkerCertBundle) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkerCertBundle.ProtoReflect.Descriptor instead. +func (*WorkerCertBundle) Descriptor() ([]byte, []int) { + return file_controller_storage_servers_store_v1_worker_auth_proto_rawDescGZIP(), []int{1} +} + +func (x *WorkerCertBundle) GetCertificatePublicKey() []byte { + if x != nil { + return x.CertificatePublicKey + } + return nil +} + +func (x *WorkerCertBundle) GetWorkerKeyIdentifier() string { + if x != nil { + return x.WorkerKeyIdentifier + } + return "" +} + +func (x *WorkerCertBundle) GetCertBundle() []byte { + if x != nil { + return x.CertBundle + } + return nil +} + +var File_controller_storage_servers_store_v1_worker_auth_proto protoreflect.FileDescriptor + +var file_controller_storage_servers_store_v1_worker_auth_proto_rawDesc = []byte{ + 0x0a, 0x35, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x61, 0x75, 0x74, + 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x22, 0xbf, 0x02, 0x0a, + 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x12, 0x32, 0x0a, 0x15, 0x77, + 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, + 0x65, 0x72, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, + 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x16, + 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x70, + 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x77, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, + 0x79, 0x12, 0x39, 0x0a, 0x19, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x65, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x28, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x16, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x45, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x43, 0x0a, 0x1e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x65, 0x6e, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x32, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x1b, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x69, 0x76, 0x4b, 0x65, + 0x79, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x3c, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x9d, + 0x01, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x42, 0x75, 0x6e, + 0x64, 0x6c, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x14, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1f, 0x0a, + 0x0b, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x1e, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x65, 0x72, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x42, 0x3c, + 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, + 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_storage_servers_store_v1_worker_auth_proto_rawDescOnce sync.Once + file_controller_storage_servers_store_v1_worker_auth_proto_rawDescData = file_controller_storage_servers_store_v1_worker_auth_proto_rawDesc +) + +func file_controller_storage_servers_store_v1_worker_auth_proto_rawDescGZIP() []byte { + file_controller_storage_servers_store_v1_worker_auth_proto_rawDescOnce.Do(func() { + file_controller_storage_servers_store_v1_worker_auth_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_servers_store_v1_worker_auth_proto_rawDescData) + }) + return file_controller_storage_servers_store_v1_worker_auth_proto_rawDescData +} + +var file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_controller_storage_servers_store_v1_worker_auth_proto_goTypes = []interface{}{ + (*WorkerAuth)(nil), // 0: controller.storage.servers.store.v1.WorkerAuth + (*WorkerCertBundle)(nil), // 1: controller.storage.servers.store.v1.WorkerCertBundle +} +var file_controller_storage_servers_store_v1_worker_auth_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_controller_storage_servers_store_v1_worker_auth_proto_init() } +func file_controller_storage_servers_store_v1_worker_auth_proto_init() { + if File_controller_storage_servers_store_v1_worker_auth_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WorkerAuth); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WorkerCertBundle); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_storage_servers_store_v1_worker_auth_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_storage_servers_store_v1_worker_auth_proto_goTypes, + DependencyIndexes: file_controller_storage_servers_store_v1_worker_auth_proto_depIdxs, + MessageInfos: file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes, + }.Build() + File_controller_storage_servers_store_v1_worker_auth_proto = out.File + file_controller_storage_servers_store_v1_worker_auth_proto_rawDesc = nil + file_controller_storage_servers_store_v1_worker_auth_proto_goTypes = nil + file_controller_storage_servers_store_v1_worker_auth_proto_depIdxs = nil +} diff --git a/internal/servers/testing.go b/internal/servers/testing.go new file mode 100644 index 000000000..0c12e7a62 --- /dev/null +++ b/internal/servers/testing.go @@ -0,0 +1,103 @@ +package servers + +import ( + "context" + "math/rand" + "testing" + "time" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/servers/store" + wrapping "github.com/hashicorp/go-kms-wrapping/v2" + "github.com/hashicorp/go-uuid" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/timestamppb" +) + +const defaultLength = 20 + +// Generate random bytes for byte fields +func populateBytes(length int) []byte { + fieldBytes := make([]byte, length) + rand.Read(fieldBytes) + return fieldBytes +} + +func TestKkmsKey(ctx context.Context, t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) string { + t.Helper() + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + kmsCache := kms.TestKms(t, conn, wrapper) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + testKey, err := databaseWrapper.KeyId(ctx) + require.NoError(t, err) + + return testKey +} + +func TestRootCertificate(ctx context.Context, t *testing.T, conn *db.DB, kmsKey string) *store.RootCertificate { + t.Helper() + rw := db.New(conn) + + beforeTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(-1 * time.Hour))} + afterTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(1 * time.Hour))} + + rootCertKeys := RootCertificateKeys{ + publicKey: populateBytes(defaultLength), + privateKey: populateBytes(defaultLength), + } + + cert, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, + rootCertKeys, kmsKey, CurrentState) + err = rw.Create(ctx, cert) + require.NoError(t, err) + return cert.RootCertificate +} + +func TestWorkerAuth(ctx context.Context, t *testing.T, conn *db.DB, worker *store.Worker, kmsKey string) *store.WorkerAuth { + t.Helper() + rw := db.New(conn) + wSignPubKey := populateBytes(defaultLength) + wEncPubKey := populateBytes(defaultLength) + workerKeys := WorkerKeys{workerSigningPubKey: wSignPubKey, workerEncryptionPubKey: wEncPubKey} + controllerKey := populateBytes(defaultLength) + nonce := populateBytes(defaultLength) + opt := []Option{ + WithKeyId(kmsKey), + WithWorkerKeys(workerKeys), + WithControllerEncryptionPrivateKey(controllerKey), + WithNonce(nonce), + } + + workerAuth, err := newWorkerAuth(ctx, "worker-key-identifier", worker.PublicId, opt...) + require.NoError(t, err) + err = rw.Create(ctx, workerAuth) + require.NoError(t, err) + + return workerAuth.WorkerAuth +} + +func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) *store.Worker { + t.Helper() + rw := db.New(conn) + kms := kms.TestKms(t, conn, wrapper) + serversRepo, err := NewRepository(rw, rw, kms) + require.NoError(t, err) + + id, err := uuid.GenerateUUID() + require.NoError(t, err) + id = "test-session-worker-" + id + + name := "test-worker-" + id + worker := &store.Worker{ + PublicId: id, + Name: name, + Address: "127.0.0.1", + } + _, _, err = serversRepo.UpsertWorker(context.Background(), worker) + require.NoError(t, err) + return worker +} diff --git a/internal/servers/worker_auth.go b/internal/servers/worker_auth.go new file mode 100644 index 000000000..d3bd84907 --- /dev/null +++ b/internal/servers/worker_auth.go @@ -0,0 +1,183 @@ +package servers + +import ( + "context" + + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/servers/store" + "google.golang.org/protobuf/proto" +) + +// WorkerAuth contains all fields related to an authorized Worker resource +// This includes worker public keys, the controller encryption key, +// and certificate bundles issued by the Boundary CA +type WorkerAuth struct { + *store.WorkerAuth + tableName string `gorm:"-"` +} + +// WorkerKeys contain the signing and encryption keys for a WorkerAuth resource +type WorkerKeys struct { + workerSigningPubKey []byte + workerEncryptionPubKey []byte +} + +func newWorkerAuth(ctx context.Context, workerKeyIdentifier, workerId string, opt ...Option) (*WorkerAuth, error) { + const op = "servers.newWorkerAuth" + opts := getOpts(opt...) + + if workerKeyIdentifier == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no workerKeyIdentifier") + } + if workerId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no workerId") + } + + l := &WorkerAuth{ + WorkerAuth: &store.WorkerAuth{ + WorkerKeyIdentifier: workerKeyIdentifier, + WorkerId: workerId, + }, + } + + if &opts.withWorkerKeys != nil { + l.WorkerSigningPubKey = opts.withWorkerKeys.workerSigningPubKey + l.WorkerEncryptionPubKey = opts.withWorkerKeys.workerEncryptionPubKey + } + if &opts.withControllerEncryptionPrivateKey != nil { + l.ControllerEncryptionPrivKey = opts.withControllerEncryptionPrivateKey + } + if opts.withKeyId != "" { + l.KeyId = opts.withKeyId + } + if opts.withNonce != nil { + l.Nonce = opts.withNonce + } + return l, nil +} + +func AllocWorkerAuth() *WorkerAuth { + return &WorkerAuth{ + WorkerAuth: &store.WorkerAuth{}, + } +} + +func (w *WorkerAuth) clone() *WorkerAuth { + cp := proto.Clone(w.WorkerAuth) + return &WorkerAuth{ + WorkerAuth: cp.(*store.WorkerAuth), + } +} + +// Validate is called before storing a WorkerAuth in the db +func (w *WorkerAuth) ValidateNewWorkerAuth(ctx context.Context) error { + const op = "servers.(WorkerAuth).validateNewWorkerAuth" + if w.WorkerKeyIdentifier == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing WorkerKeyIdentifier") + } + if w.WorkerId == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing WorkerId") + } + if w.WorkerSigningPubKey == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing WorkerSigningPubKey") + } + if w.WorkerEncryptionPubKey == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing WorkerEncryptionPubKey") + } + if w.ControllerEncryptionPrivKey == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing ControllerEncryptionPrivKey") + } + if w.KeyId == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing KeyId") + } + if w.Nonce == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing Nonce") + } + + return nil +} + +// TableName returns the table name. +func (w *WorkerAuth) TableName() string { + if w.tableName != "" { + return w.tableName + } + return "worker_auth_authorized" +} + +// SetTableName sets the table name. +func (w *WorkerAuth) SetTableName(n string) { + w.tableName = n +} + +// WorkerCertBundle contains all fields related to a WorkerCertBundle resource +// This includes the serial number of the issuing CA, the worker id, and the certificate bundles issued by the CA +type WorkerCertBundle struct { + *store.WorkerCertBundle + tableName string `gorm:"-"` +} + +func newWorkerCertBundle(ctx context.Context, certificatePublicKey []byte, workerKeyIdentifier string, certBundle []byte) (*WorkerCertBundle, error) { + const op = "servers.newWorkerCertBundle" + + if certificatePublicKey == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no certificatePublicKey") + } + if workerKeyIdentifier == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no workerKeyIdentifier") + } + if certBundle == nil || len(certBundle) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty certBundle") + } + + l := &WorkerCertBundle{ + WorkerCertBundle: &store.WorkerCertBundle{ + CertificatePublicKey: certificatePublicKey, + WorkerKeyIdentifier: workerKeyIdentifier, + CertBundle: certBundle, + }, + } + return l, nil +} + +func AllocWorkerCertBundle() *WorkerCertBundle { + return &WorkerCertBundle{ + WorkerCertBundle: &store.WorkerCertBundle{}, + } +} + +func (w *WorkerCertBundle) clone() *WorkerCertBundle { + cp := proto.Clone(w.WorkerCertBundle) + return &WorkerCertBundle{ + WorkerCertBundle: cp.(*store.WorkerCertBundle), + } +} + +// Validate is called before storing a WorkerCertBundle in the db +func (w *WorkerCertBundle) ValidateNewWorkerCertBundle(ctx context.Context) error { + const op = "servers.(WorkerAuth).validateNewWorkerCertBundle" + if w.CertificatePublicKey == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing CertificatePublicKey") + } + if w.WorkerKeyIdentifier == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing WorkerKeyIdentifier") + } + if w.CertBundle == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing certificate bundle") + } + + return nil +} + +// TableName returns the table name. +func (w *WorkerCertBundle) TableName() string { + if w.tableName != "" { + return w.tableName + } + return "worker_auth_certificate_bundle" +} + +// SetTableName sets the table name. +func (w *WorkerCertBundle) SetTableName(n string) { + w.tableName = n +} diff --git a/internal/servers/workerauth_store_test.go b/internal/servers/workerauth_store_test.go new file mode 100644 index 000000000..d362ce78e --- /dev/null +++ b/internal/servers/workerauth_store_test.go @@ -0,0 +1,524 @@ +package servers + +import ( + "context" + "math/rand" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "google.golang.org/protobuf/testing/protocmp" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestRootCertStore(t *testing.T) { + t.Parallel() + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + kmsCache := kms.TestKms(t, conn, wrapper) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + testKey, err := databaseWrapper.KeyId(ctx) + require.NoError(t, err) + + beforeTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(-1 * time.Hour))} + afterTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(1 * time.Hour))} + publicKey := populateBytes(defaultLength) + privateKey := populateBytes(defaultLength) + certificate := populateBytes(defaultLength) + + type args struct { + serialNumber uint64 + certificate []byte + rootCertKeys RootCertificateKeys + notValidBefore *timestamp.Timestamp + notValidAfter *timestamp.Timestamp + keyId string + state CertificateState + } + + tests := []struct { + name string + args args + expectedRootCert *store.RootCertificate + wantErr bool + wantCreateErr bool + }{ + { + name: "rootcert-store-next", + args: args{ + serialNumber: 1234567890, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + certificate: certificate, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: NextState, + }, + expectedRootCert: &store.RootCertificate{ + SerialNumber: 1234567890, + PublicKey: publicKey, + PrivateKey: privateKey, + Certificate: certificate, + NotValidBefore: beforeTimestamp, + NotValidAfter: afterTimestamp, + KeyId: testKey, + State: "next", + }, + wantErr: false, + }, + { + name: "rootcert-store-current", + args: args{ + serialNumber: 9876543210, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + certificate: certificate, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: CurrentState, + }, + expectedRootCert: &store.RootCertificate{ + SerialNumber: 9876543210, + PublicKey: publicKey, + PrivateKey: privateKey, + Certificate: certificate, + NotValidBefore: beforeTimestamp, + NotValidAfter: afterTimestamp, + KeyId: testKey, + State: "current", + }, + wantErr: false, + }, + { + name: "rootcert-store-empty-key", + args: args{ + serialNumber: 9876543210, + rootCertKeys: RootCertificateKeys{publicKey: populateBytes(0), privateKey: privateKey}, + certificate: certificate, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: NextState, + }, + wantErr: true, + }, + { + name: "rootcert-no-keys", + args: args{ + serialNumber: 1011121314, + certificate: certificate, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: NextState, + }, + wantErr: true, + }, + { + name: "rootcert-no-cert", + args: args{ + serialNumber: 15161718, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: NextState, + }, + wantErr: true, + }, + { + name: "rootcert-invalid-timestamps", + args: args{ + serialNumber: 1920212223, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + certificate: certificate, + notValidBefore: afterTimestamp, + notValidAfter: beforeTimestamp, + keyId: testKey, + state: NextState, + }, + wantCreateErr: true, + }, + { + name: "rootcert-invalid-timestamps-2", + args: args{ + serialNumber: 2425262728, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + certificate: certificate, + notValidBefore: afterTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: NextState, + }, + wantCreateErr: true, + }, + { + name: "rootcert-invalid-state", + args: args{ + serialNumber: 3031323334, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + certificate: certificate, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: UnknownState, + }, + wantErr: true, + }, + { + name: "rootcert-no-key-id", + args: args{ + serialNumber: 4041424344, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + certificate: certificate, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + state: NextState, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + cert, err := newRootCertificate(ctx, tt.args.serialNumber, tt.args.certificate, tt.args.notValidBefore, tt.args.notValidAfter, + tt.args.rootCertKeys, tt.args.keyId, tt.args.state) + + if tt.wantErr { + assert.Error(err) + return + } + require.NoError(err) + require.NotNil(cert) + + err = rw.Create(ctx, cert) + if tt.wantCreateErr { + assert.Error(err) + return + } else { + assert.NoError(err) + assert.Equal(tt.expectedRootCert, cert.RootCertificate) + assert.Empty(cmp.Diff(tt.expectedRootCert, cert, protocmp.Transform())) + } + + deleteWhere := `state = ?` + deleted, err := rw.Delete(ctx, &RootCertificate{}, db.WithWhere(deleteWhere, cert.State)) + assert.NoError(err) + assert.Equal(1, deleted) + }) + } +} + +func TestDuplicateRootCert(t *testing.T) { + t.Parallel() + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + kmsCache := kms.TestKms(t, conn, wrapper) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + testKey, err := databaseWrapper.KeyId(ctx) + require.NoError(t, err) + + beforeTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(-1 * time.Hour))} + afterTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(1 * time.Hour))} + + // Make two current and next root CAs + current1, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, + RootCertificateKeys{publicKey: populateBytes(defaultLength), privateKey: populateBytes(defaultLength)}, + testKey, CurrentState) + require.NoError(t, err) + current2, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, + RootCertificateKeys{publicKey: populateBytes(defaultLength), privateKey: populateBytes(defaultLength)}, + testKey, CurrentState) + next1, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, + RootCertificateKeys{publicKey: populateBytes(defaultLength), privateKey: populateBytes(defaultLength)}, + testKey, NextState) + require.NoError(t, err) + next2, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, + RootCertificateKeys{publicKey: populateBytes(defaultLength), privateKey: populateBytes(defaultLength)}, + testKey, NextState) + require.NoError(t, err) + + // Insert first set of certs + err = rw.Create(ctx, current1) + require.NoError(t, err) + err = rw.Create(ctx, next1) + require.NoError(t, err) + + // Insert second current cert and expect an error + err = rw.Create(ctx, current2) + require.Error(t, err) + err = rw.Create(ctx, next2) + require.Error(t, err) +} + +func TestWorkerAuthStore(t *testing.T) { + t.Parallel() + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + kmsCache := kms.TestKms(t, conn, wrapper) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + testKey, err := databaseWrapper.KeyId(ctx) + require.NoError(t, err) + + worker := TestWorker(t, conn, wrapper) + + wSignPubKey := populateBytes(defaultLength) + wEncPubKey := populateBytes(defaultLength) + workerKeys := WorkerKeys{workerSigningPubKey: wSignPubKey, workerEncryptionPubKey: wEncPubKey} + controllerKey := populateBytes(defaultLength) + nonce := populateBytes(defaultLength) + + type args struct { + workerKeyIdentifier string + workerId string + } + tests := []struct { + name string + args args + expectedWorkerAuth *store.WorkerAuth + opt []Option + wantErr bool + wantCreateErr bool + }{ + { + name: "workerauth-store", + args: args{ + workerKeyIdentifier: "worker-auth-id-123", + workerId: worker.PublicId, + }, + opt: []Option{ + WithWorkerKeys(workerKeys), + WithControllerEncryptionPrivateKey(controllerKey), + WithNonce(nonce), + WithKeyId(testKey), + }, + expectedWorkerAuth: &store.WorkerAuth{ + WorkerKeyIdentifier: "worker-auth-id-123", + WorkerId: worker.PublicId, + WorkerSigningPubKey: wSignPubKey, + WorkerEncryptionPubKey: wEncPubKey, + ControllerEncryptionPrivKey: controllerKey, + KeyId: testKey, + Nonce: nonce, + }, + wantErr: false, + }, + { + name: "workerauth-store-invalid-key-id", + args: args{ + workerKeyIdentifier: "worker-auth-id-456", + workerId: worker.PublicId, + }, + opt: []Option{ + WithWorkerKeys(workerKeys), + WithControllerEncryptionPrivateKey(controllerKey), + WithNonce(nonce), + WithKeyId("fakeyityfake"), + }, + wantCreateErr: true, + }, + { + name: "workerauth-no-pkey", + args: args{ + workerId: worker.PublicId, + }, + opt: []Option{ + WithWorkerKeys(workerKeys), + WithControllerEncryptionPrivateKey(controllerKey), + WithNonce(nonce), + WithKeyId(testKey), + }, + wantErr: true, + }, + { + name: "workerauth-empty-pkey", + args: args{ + workerKeyIdentifier: "", + workerId: worker.PublicId, + }, + opt: []Option{ + WithWorkerKeys(workerKeys), + WithControllerEncryptionPrivateKey(controllerKey), + WithNonce(nonce), + WithKeyId(testKey), + }, + wantErr: true, + }, + { + name: "workerauth-invalid-workerid", + args: args{ + workerKeyIdentifier: "worker-auth-id-101", + workerId: "bogus-worker", + }, + opt: []Option{ + WithWorkerKeys(workerKeys), + WithControllerEncryptionPrivateKey(controllerKey), + WithNonce(nonce), + WithKeyId(testKey), + }, + wantCreateErr: true, + }, + { + name: "empty-workerauth", + args: args{}, + wantErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + wAuth, err := newWorkerAuth(ctx, tt.args.workerKeyIdentifier, tt.args.workerId, tt.opt...) + + if tt.wantErr { + assert.Error(err) + return + } + require.NoError(err) + require.NotNil(wAuth) + + err = rw.Create(ctx, wAuth) + if tt.wantCreateErr { + assert.Error(err) + } else { + assert.NoError(err) + assert.Equal(tt.expectedWorkerAuth, wAuth.WorkerAuth) + assert.Empty(cmp.Diff(tt.expectedWorkerAuth, wAuth, protocmp.Transform())) + } + }) + } +} + +func TestWorkerCertBundle(t *testing.T) { + t.Parallel() + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + testKey := TestKkmsKey(ctx, t, conn, wrapper) + + worker := TestWorker(t, conn, wrapper) + workerAuth := TestWorkerAuth(ctx, t, conn, worker, testKey) + rootCA := TestRootCertificate(ctx, t, conn, testKey) + certBundle := populateBytes(defaultLength) + + type args struct { + certificatePubKey []byte + workerAuthId string + certificateBundle []byte + } + tests := []struct { + name string + args args + expected *store.WorkerCertBundle + wantErr bool + wantCreateErr bool + }{ + { + name: "workercertbundle-store", + args: args{ + certificatePubKey: rootCA.PublicKey, + workerAuthId: workerAuth.WorkerKeyIdentifier, + certificateBundle: certBundle, + }, + expected: &store.WorkerCertBundle{ + CertificatePublicKey: rootCA.PublicKey, + WorkerKeyIdentifier: workerAuth.WorkerKeyIdentifier, + CertBundle: certBundle, + }, + wantErr: false, + }, + { + name: "workercertbundle-empty-cert-bundle", + args: args{ + certificatePubKey: rootCA.PublicKey, + workerAuthId: workerAuth.WorkerKeyIdentifier, + }, + wantErr: true, + }, + { + name: "workercertbundle-no-worker-id", + args: args{ + certificatePubKey: rootCA.PublicKey, + certificateBundle: certBundle, + }, + wantErr: true, + }, + { + name: "workercertbundle-invalid-worker-id", + args: args{ + certificatePubKey: rootCA.PublicKey, + workerAuthId: "bogus-worker", + certificateBundle: certBundle, + }, + wantCreateErr: true, + }, + { + name: "workercertbundle-empty-publickey", + args: args{ + workerAuthId: workerAuth.WorkerKeyIdentifier, + certificateBundle: certBundle, + }, + wantErr: true, + }, + { + name: "workercertbundle-invalid-publickey", + args: args{ + certificatePubKey: populateBytes(defaultLength), + workerAuthId: workerAuth.WorkerKeyIdentifier, + certificateBundle: certBundle, + }, + wantCreateErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + workerCertBundle, err := newWorkerCertBundle(ctx, tt.args.certificatePubKey, tt.args.workerAuthId, tt.args.certificateBundle) + + if tt.wantErr { + assert.Error(err) + return + } + require.NoError(err) + require.NotNil(workerCertBundle) + + err = rw.Create(ctx, workerCertBundle) + if tt.wantCreateErr { + assert.Error(err) + } else { + assert.NoError(err) + assert.Equal(tt.expected, workerCertBundle.WorkerCertBundle) + assert.Empty(cmp.Diff(tt.expected, workerCertBundle, protocmp.Transform())) + } + }) + } +}