feat(target): add proxy certificate repos and protos (#1577)
This commit is contained in:
1 parent
bcd2a62890
commit
6afb42e334
13 files changed
+1489
-24
No files matched your search
@@ -56,6 +56,8 @@ type options struct {
|
||||
WithNetResolver intglobals.NetIpResolver
|
||||
WithStartPageAfterItem pagination.Item
|
||||
withAliases []*talias.Alias
|
||||
withAlias *talias.Alias
|
||||
withTargetId string
|
||||
}
|
||||
|
||||
func getDefaultOptions() options {
|
||||
@@ -83,6 +85,7 @@ func getDefaultOptions() options {
|
||||
WithIngressWorkerFilter: "",
|
||||
WithAddress: "",
|
||||
WithNetResolver: net.DefaultResolver,
|
||||
withTargetId: "",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,3 +287,17 @@ func WithAliases(in []*talias.Alias) Option {
|
||||
o.withAliases = in
|
||||
}
|
||||
}
|
||||
|
||||
// WithAlias provides an option to provide a single alias.
|
||||
func WithAlias(in *talias.Alias) Option {
|
||||
return func(o *options) {
|
||||
o.withAlias = in
|
||||
}
|
||||
}
|
||||
|
||||
// WithTargetId provides an option to provide a target ID.
|
||||
func WithTargetId(in string) Option {
|
||||
return func(o *options) {
|
||||
o.withTargetId = in
|
||||
}
|
||||
}
|
||||
@@ -268,4 +268,18 @@ func Test_GetOpts(t *testing.T) {
|
||||
opts := GetOpts(WithAliases(input))
|
||||
assert.Equal(input, opts.withAliases)
|
||||
})
|
||||
t.Run("WithAlias", func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
al, err := talias.NewAlias(context.Background(), "global", "test")
|
||||
require.NoError(err)
|
||||
opts := GetOpts(WithAlias(al))
|
||||
assert.Equal(al, opts.withAlias)
|
||||
})
|
||||
t.Run("WithTargetId", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
opts := GetOpts(WithTargetId("testId"))
|
||||
testOpts := getDefaultOptions()
|
||||
testOpts.withTargetId = "testId"
|
||||
assert.Equal(opts, testOpts)
|
||||
})
|
||||
}
|
||||
@@ -649,6 +649,308 @@ func (x *CredentialSourceView) GetType() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type TargetProxyCertificate struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// public_id is used to identify the target proxy key
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
|
||||
// target_id is used to access the proxy target this key is for
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
TargetId string `protobuf:"bytes,20,opt,name=target_id,json=targetId,proto3" json:"target_id,omitempty" gorm:"not_null"`
|
||||
// public_key is the public key associated with this certificate
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
PublicKey []byte `protobuf:"bytes,30,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty" gorm:"not_null"`
|
||||
// private_key is the plaintext key. this is not stored in the db
|
||||
// @inject_tag: `gorm:"-" wrapping:"pt,private_key"`
|
||||
PrivateKey []byte `protobuf:"bytes,40,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty" gorm:"-" wrapping:"pt,private_key"`
|
||||
// private_key_encrypted is the encrypted PEM encoded private key
|
||||
// @inject_tag: `gorm:"not_null" wrapping:"ct,private_key"`
|
||||
PrivateKeyEncrypted []byte `protobuf:"bytes,50,opt,name=private_key_encrypted,json=privateKeyEncrypted,proto3" json:"private_key_encrypted,omitempty" gorm:"not_null" wrapping:"ct,private_key"`
|
||||
// key_id is the kms private id used to encrypt this entry's private key
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
KeyId string `protobuf:"bytes,60,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"not_null"`
|
||||
// certificate is the PEM encoded certificate
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
Certificate []byte `protobuf:"bytes,70,opt,name=certificate,proto3" json:"certificate,omitempty" gorm:"not_null"`
|
||||
// not_valid_after is the timestamp at which this certificate's validity period ends
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
NotValidAfter *timestamp.Timestamp `protobuf:"bytes,80,opt,name=not_valid_after,json=notValidAfter,proto3" json:"not_valid_after,omitempty" gorm:"not_null"`
|
||||
// version allows optimistic locking during modification
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Version uint32 `protobuf:"varint,90,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
|
||||
// create_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
CreateTime *timestamp.Timestamp `protobuf:"bytes,100,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// update_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
UpdateTime *timestamp.Timestamp `protobuf:"bytes,110,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TargetProxyCertificate) Reset() {
|
||||
*x = TargetProxyCertificate{}
|
||||
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TargetProxyCertificate) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TargetProxyCertificate) ProtoMessage() {}
|
||||
|
||||
func (x *TargetProxyCertificate) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[7]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TargetProxyCertificate.ProtoReflect.Descriptor instead.
|
||||
func (*TargetProxyCertificate) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *TargetProxyCertificate) GetPublicId() string {
|
||||
if x != nil {
|
||||
return x.PublicId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TargetProxyCertificate) GetTargetId() string {
|
||||
if x != nil {
|
||||
return x.TargetId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TargetProxyCertificate) GetPublicKey() []byte {
|
||||
if x != nil {
|
||||
return x.PublicKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TargetProxyCertificate) GetPrivateKey() []byte {
|
||||
if x != nil {
|
||||
return x.PrivateKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TargetProxyCertificate) GetPrivateKeyEncrypted() []byte {
|
||||
if x != nil {
|
||||
return x.PrivateKeyEncrypted
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TargetProxyCertificate) GetKeyId() string {
|
||||
if x != nil {
|
||||
return x.KeyId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TargetProxyCertificate) GetCertificate() []byte {
|
||||
if x != nil {
|
||||
return x.Certificate
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TargetProxyCertificate) GetNotValidAfter() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.NotValidAfter
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TargetProxyCertificate) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TargetProxyCertificate) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TargetProxyCertificate) GetUpdateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TargetAliasProxyCertificate struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// public_id is used to identify the target proxy key
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
|
||||
// target_id is used to access the proxy target this key is for
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
TargetId string `protobuf:"bytes,20,opt,name=target_id,json=targetId,proto3" json:"target_id,omitempty" gorm:"not_null"`
|
||||
// public_key is the public key associated with this certificate
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
PublicKey []byte `protobuf:"bytes,30,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty" gorm:"not_null"`
|
||||
// private_key is the plaintext key. this is not stored in the db
|
||||
// @inject_tag: `gorm:"-" wrapping:"pt,private_key"`
|
||||
PrivateKey []byte `protobuf:"bytes,40,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty" gorm:"-" wrapping:"pt,private_key"`
|
||||
// private_key_encrypted is the encrypted PEM encoded private key
|
||||
// @inject_tag: `gorm:"not_null" wrapping:"ct,private_key"`
|
||||
PrivateKeyEncrypted []byte `protobuf:"bytes,50,opt,name=private_key_encrypted,json=privateKeyEncrypted,proto3" json:"private_key_encrypted,omitempty" gorm:"not_null" wrapping:"ct,private_key"`
|
||||
// key_id is the kms private id used to encrypt this entry's private key
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
KeyId string `protobuf:"bytes,60,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"not_null"`
|
||||
// alias_id is the public id of the alias target
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
AliasId string `protobuf:"bytes,70,opt,name=alias_id,json=aliasId,proto3" json:"alias_id,omitempty" gorm:"not_null"`
|
||||
// certificate is the PEM encoded certificate
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
Certificate []byte `protobuf:"bytes,80,opt,name=certificate,proto3" json:"certificate,omitempty" gorm:"not_null"`
|
||||
// not_valid_after is the timestamp at which this certificate's validity period ends
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
NotValidAfter *timestamp.Timestamp `protobuf:"bytes,90,opt,name=not_valid_after,json=notValidAfter,proto3" json:"not_valid_after,omitempty" gorm:"not_null"`
|
||||
// version allows optimistic locking during modification
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Version uint32 `protobuf:"varint,100,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
|
||||
// create_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
CreateTime *timestamp.Timestamp `protobuf:"bytes,110,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// update_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
UpdateTime *timestamp.Timestamp `protobuf:"bytes,120,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) Reset() {
|
||||
*x = TargetAliasProxyCertificate{}
|
||||
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TargetAliasProxyCertificate) ProtoMessage() {}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[8]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TargetAliasProxyCertificate.ProtoReflect.Descriptor instead.
|
||||
func (*TargetAliasProxyCertificate) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) GetPublicId() string {
|
||||
if x != nil {
|
||||
return x.PublicId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) GetTargetId() string {
|
||||
if x != nil {
|
||||
return x.TargetId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) GetPublicKey() []byte {
|
||||
if x != nil {
|
||||
return x.PublicKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) GetPrivateKey() []byte {
|
||||
if x != nil {
|
||||
return x.PrivateKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) GetPrivateKeyEncrypted() []byte {
|
||||
if x != nil {
|
||||
return x.PrivateKeyEncrypted
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) GetKeyId() string {
|
||||
if x != nil {
|
||||
return x.KeyId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) GetAliasId() string {
|
||||
if x != nil {
|
||||
return x.AliasId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) GetCertificate() []byte {
|
||||
if x != nil {
|
||||
return x.Certificate
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) GetNotValidAfter() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.NotValidAfter
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TargetAliasProxyCertificate) GetUpdateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_controller_storage_target_store_v1_target_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_controller_storage_target_store_v1_target_proto_rawDesc = "" +
|
||||
@@ -713,7 +1015,42 @@ const file_controller_storage_target_store_v1_target_proto_rawDesc = "" +
|
||||
"\x14CredentialSourceView\x12\x1b\n" +
|
||||
"\tpublic_id\x18\n" +
|
||||
" \x01(\tR\bpublicId\x12\x12\n" +
|
||||
"\x04type\x18\x14 \x01(\tR\x04typeB;Z9github.com/hashicorp/boundary/internal/target/store;storeb\x06proto3"
|
||||
"\x04type\x18\x14 \x01(\tR\x04type\"\x87\x04\n" +
|
||||
"\x16TargetProxyCertificate\x12\x1b\n" +
|
||||
"\tpublic_id\x18\n" +
|
||||
" \x01(\tR\bpublicId\x12\x1b\n" +
|
||||
"\ttarget_id\x18\x14 \x01(\tR\btargetId\x12\x1d\n" +
|
||||
"\n" +
|
||||
"public_key\x18\x1e \x01(\fR\tpublicKey\x12\x1f\n" +
|
||||
"\vprivate_key\x18( \x01(\fR\n" +
|
||||
"privateKey\x122\n" +
|
||||
"\x15private_key_encrypted\x182 \x01(\fR\x13privateKeyEncrypted\x12\x15\n" +
|
||||
"\x06key_id\x18< \x01(\tR\x05keyId\x12 \n" +
|
||||
"\vcertificate\x18F \x01(\fR\vcertificate\x12R\n" +
|
||||
"\x0fnot_valid_after\x18P \x01(\v2*.controller.storage.timestamp.v1.TimestampR\rnotValidAfter\x12\x18\n" +
|
||||
"\aversion\x18Z \x01(\rR\aversion\x12K\n" +
|
||||
"\vcreate_time\x18d \x01(\v2*.controller.storage.timestamp.v1.TimestampR\n" +
|
||||
"createTime\x12K\n" +
|
||||
"\vupdate_time\x18n \x01(\v2*.controller.storage.timestamp.v1.TimestampR\n" +
|
||||
"updateTime\"\xa7\x04\n" +
|
||||
"\x1bTargetAliasProxyCertificate\x12\x1b\n" +
|
||||
"\tpublic_id\x18\n" +
|
||||
" \x01(\tR\bpublicId\x12\x1b\n" +
|
||||
"\ttarget_id\x18\x14 \x01(\tR\btargetId\x12\x1d\n" +
|
||||
"\n" +
|
||||
"public_key\x18\x1e \x01(\fR\tpublicKey\x12\x1f\n" +
|
||||
"\vprivate_key\x18( \x01(\fR\n" +
|
||||
"privateKey\x122\n" +
|
||||
"\x15private_key_encrypted\x182 \x01(\fR\x13privateKeyEncrypted\x12\x15\n" +
|
||||
"\x06key_id\x18< \x01(\tR\x05keyId\x12\x19\n" +
|
||||
"\balias_id\x18F \x01(\tR\aaliasId\x12 \n" +
|
||||
"\vcertificate\x18P \x01(\fR\vcertificate\x12R\n" +
|
||||
"\x0fnot_valid_after\x18Z \x01(\v2*.controller.storage.timestamp.v1.TimestampR\rnotValidAfter\x12\x18\n" +
|
||||
"\aversion\x18d \x01(\rR\aversion\x12K\n" +
|
||||
"\vcreate_time\x18n \x01(\v2*.controller.storage.timestamp.v1.TimestampR\n" +
|
||||
"createTime\x12K\n" +
|
||||
"\vupdate_time\x18x \x01(\v2*.controller.storage.timestamp.v1.TimestampR\n" +
|
||||
"updateTimeB;Z9github.com/hashicorp/boundary/internal/target/store;storeb\x06proto3"
|
||||
|
||||
var (
|
||||
file_controller_storage_target_store_v1_target_proto_rawDescOnce sync.Once
|
||||
@@ -727,29 +1064,37 @@ func file_controller_storage_target_store_v1_target_proto_rawDescGZIP() []byte {
|
||||
return file_controller_storage_target_store_v1_target_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_target_store_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||
var file_controller_storage_target_store_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
|
||||
var file_controller_storage_target_store_v1_target_proto_goTypes = []any{
|
||||
(*TargetView)(nil), // 0: controller.storage.target.store.v1.TargetView
|
||||
(*TargetHostSet)(nil), // 1: controller.storage.target.store.v1.TargetHostSet
|
||||
(*TargetAddress)(nil), // 2: controller.storage.target.store.v1.TargetAddress
|
||||
(*CredentialLibrary)(nil), // 3: controller.storage.target.store.v1.CredentialLibrary
|
||||
(*StaticCredential)(nil), // 4: controller.storage.target.store.v1.StaticCredential
|
||||
(*CredentialSource)(nil), // 5: controller.storage.target.store.v1.CredentialSource
|
||||
(*CredentialSourceView)(nil), // 6: controller.storage.target.store.v1.CredentialSourceView
|
||||
(*timestamp.Timestamp)(nil), // 7: controller.storage.timestamp.v1.Timestamp
|
||||
(*TargetView)(nil), // 0: controller.storage.target.store.v1.TargetView
|
||||
(*TargetHostSet)(nil), // 1: controller.storage.target.store.v1.TargetHostSet
|
||||
(*TargetAddress)(nil), // 2: controller.storage.target.store.v1.TargetAddress
|
||||
(*CredentialLibrary)(nil), // 3: controller.storage.target.store.v1.CredentialLibrary
|
||||
(*StaticCredential)(nil), // 4: controller.storage.target.store.v1.StaticCredential
|
||||
(*CredentialSource)(nil), // 5: controller.storage.target.store.v1.CredentialSource
|
||||
(*CredentialSourceView)(nil), // 6: controller.storage.target.store.v1.CredentialSourceView
|
||||
(*TargetProxyCertificate)(nil), // 7: controller.storage.target.store.v1.TargetProxyCertificate
|
||||
(*TargetAliasProxyCertificate)(nil), // 8: controller.storage.target.store.v1.TargetAliasProxyCertificate
|
||||
(*timestamp.Timestamp)(nil), // 9: controller.storage.timestamp.v1.Timestamp
|
||||
}
|
||||
var file_controller_storage_target_store_v1_target_proto_depIdxs = []int32{
|
||||
7, // 0: controller.storage.target.store.v1.TargetView.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 1: controller.storage.target.store.v1.TargetView.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 2: controller.storage.target.store.v1.TargetHostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 3: controller.storage.target.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 4: controller.storage.target.store.v1.StaticCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 5: controller.storage.target.store.v1.CredentialSource.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // [6:6] is the sub-list for method output_type
|
||||
6, // [6:6] is the sub-list for method input_type
|
||||
6, // [6:6] is the sub-list for extension type_name
|
||||
6, // [6:6] is the sub-list for extension extendee
|
||||
0, // [0:6] is the sub-list for field type_name
|
||||
9, // 0: controller.storage.target.store.v1.TargetView.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 1: controller.storage.target.store.v1.TargetView.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 2: controller.storage.target.store.v1.TargetHostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 3: controller.storage.target.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 4: controller.storage.target.store.v1.StaticCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 5: controller.storage.target.store.v1.CredentialSource.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 6: controller.storage.target.store.v1.TargetProxyCertificate.not_valid_after:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 7: controller.storage.target.store.v1.TargetProxyCertificate.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 8: controller.storage.target.store.v1.TargetProxyCertificate.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 9: controller.storage.target.store.v1.TargetAliasProxyCertificate.not_valid_after:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 10: controller.storage.target.store.v1.TargetAliasProxyCertificate.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 11: controller.storage.target.store.v1.TargetAliasProxyCertificate.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
12, // [12:12] is the sub-list for method output_type
|
||||
12, // [12:12] is the sub-list for method input_type
|
||||
12, // [12:12] is the sub-list for extension type_name
|
||||
12, // [12:12] is the sub-list for extension extendee
|
||||
0, // [0:12] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_storage_target_store_v1_target_proto_init() }
|
||||
@@ -763,7 +1108,7 @@ func file_controller_storage_target_store_v1_target_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_controller_storage_target_store_v1_target_proto_rawDesc), len(file_controller_storage_target_store_v1_target_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 7,
|
||||
NumMessages: 9,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -18,6 +18,12 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
)
|
||||
|
||||
// ServerCertificate holds the PEM encoded certificate and key for a target
|
||||
type ServerCertificate struct {
|
||||
CertificatePem []byte
|
||||
PrivateKeyPem []byte
|
||||
}
|
||||
|
||||
// Target is a commmon interface for all target subtypes
|
||||
type Target interface {
|
||||
GetPublicId() string
|
||||
@@ -42,6 +48,7 @@ type Target interface {
|
||||
GetCredentialSources() []CredentialSource
|
||||
GetStorageBucketId() string
|
||||
GetEnableSessionRecording() bool
|
||||
GetProxyServerCertificate() *ServerCertificate
|
||||
Clone() Target
|
||||
SetPublicId(context.Context, string) error
|
||||
SetProjectId(string)
|
||||
@@ -64,6 +71,7 @@ type Target interface {
|
||||
SetStorageBucketId(string)
|
||||
SetEnableSessionRecording(bool)
|
||||
Oplog(op oplog.OpType) oplog.Metadata
|
||||
SetProxyServerCertificate(*ServerCertificate)
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package target
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"math/big"
|
||||
mathrand "math/rand"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
talias "github.com/hashicorp/boundary/internal/alias/target"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/target/store"
|
||||
"github.com/hashicorp/boundary/internal/util"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
||||
"github.com/hashicorp/go-kms-wrapping/v2/extras/structwrapping"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func generatePrivAndPubKeys(ctx context.Context) (privKeyBytes []byte, pubKeyBytes []byte, err error) {
|
||||
const op = "target.generatePrivAndPubKeys"
|
||||
// Generate a private key using the P521 curve
|
||||
key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "failed to generate ECDSA key")
|
||||
}
|
||||
|
||||
privKeyBytes, err = x509.MarshalECPrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("error marshalling private key"))
|
||||
}
|
||||
|
||||
pubKeyBytes, err = x509.MarshalPKIXPublicKey(&key.PublicKey)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("error marshalling public key"))
|
||||
}
|
||||
return privKeyBytes, pubKeyBytes, nil
|
||||
}
|
||||
|
||||
// generateTargetCert generates a self-signed certificate for the target for localhost with the localhost addresses for ipv4 and ipv6.
|
||||
// Supports the option withAlias to pass an alias for use in the cert DNS names field
|
||||
func generateTargetCert(ctx context.Context, privKey *ecdsa.PrivateKey, exp time.Time, opt ...Option) ([]byte, error) {
|
||||
const op = "target.generateTargetCert"
|
||||
switch {
|
||||
case util.IsNil(privKey):
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing private key")
|
||||
case exp.IsZero():
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing expiry")
|
||||
case exp.Before(time.Now()):
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "expiration time must be in the future")
|
||||
}
|
||||
|
||||
opts := GetOpts(opt...)
|
||||
|
||||
template := &x509.Certificate{
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||
x509.ExtKeyUsageServerAuth,
|
||||
},
|
||||
Subject: pkix.Name{
|
||||
CommonName: "localhost",
|
||||
},
|
||||
IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement | x509.KeyUsageCertSign,
|
||||
SerialNumber: big.NewInt(mathrand.Int63()),
|
||||
NotBefore: time.Now().Add(-1 * time.Minute),
|
||||
NotAfter: exp,
|
||||
BasicConstraintsValid: true,
|
||||
DNSNames: []string{"localhost"},
|
||||
}
|
||||
|
||||
if opts.withAlias != nil {
|
||||
template.DNSNames = append(template.DNSNames, opts.withAlias.Value)
|
||||
}
|
||||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, template, template, &privKey.PublicKey, privKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.GenCert))
|
||||
}
|
||||
return certBytes, nil
|
||||
}
|
||||
|
||||
// TargetProxyCertificate represents a proxy certificate for a target
|
||||
type TargetProxyCertificate struct {
|
||||
*store.TargetProxyCertificate
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// newTargetProxyCertificate creates a new in memory TargetProxyCertificate
|
||||
// Supports the options withTargetId to set the target ID
|
||||
// If this is not provided, the TargetId will need to be set before storing the certificate
|
||||
func NewTargetProxyCertificate(ctx context.Context, opt ...Option) (*TargetProxyCertificate, error) {
|
||||
const op = "target.NewTargetProxyCertificate"
|
||||
|
||||
opts := GetOpts(opt...)
|
||||
|
||||
privKey, pubKey, err := generatePrivAndPubKeys(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error generating target proxy certificate key"))
|
||||
}
|
||||
|
||||
notValidAfter := time.Now().AddDate(1, 0, 0) // 1 year from now
|
||||
parsedKey, err := x509.ParseECPrivateKey(privKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error parsing target proxy certificate key"))
|
||||
}
|
||||
certBytes, err := generateTargetCert(ctx, parsedKey, notValidAfter)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error generating target certificate"))
|
||||
}
|
||||
|
||||
return &TargetProxyCertificate{
|
||||
TargetProxyCertificate: &store.TargetProxyCertificate{
|
||||
PrivateKey: privKey,
|
||||
PublicKey: pubKey,
|
||||
Certificate: certBytes,
|
||||
NotValidAfter: timestamp.New(notValidAfter),
|
||||
TargetId: opts.withTargetId,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// encrypt the target cert key before writing it to the db
|
||||
func (t *TargetProxyCertificate) encrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "target.(TargetProxyCertificate).encrypt"
|
||||
if cipher == nil {
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing cipher")
|
||||
}
|
||||
if err := structwrapping.WrapStruct(ctx, cipher, t.TargetProxyCertificate, nil); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt))
|
||||
}
|
||||
keyId, err := cipher.KeyId(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("failed to read cipher key id"))
|
||||
}
|
||||
t.KeyId = keyId
|
||||
return nil
|
||||
}
|
||||
|
||||
// decrypt the target cert key after reading it from the db
|
||||
func (t *TargetProxyCertificate) decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "target.(TargetProxyCertificate).decrypt"
|
||||
if cipher == nil {
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing cipher")
|
||||
}
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, t.TargetProxyCertificate, nil); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func allocTargetProxyCertificate() *TargetProxyCertificate {
|
||||
return &TargetProxyCertificate{
|
||||
TargetProxyCertificate: &store.TargetProxyCertificate{},
|
||||
}
|
||||
}
|
||||
|
||||
// Clone creates a clone of the TargetProxyCertificate
|
||||
func (t *TargetProxyCertificate) Clone() *TargetProxyCertificate {
|
||||
cp := proto.Clone(t.TargetProxyCertificate)
|
||||
return &TargetProxyCertificate{
|
||||
TargetProxyCertificate: cp.(*store.TargetProxyCertificate),
|
||||
}
|
||||
}
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates a target certificate
|
||||
func (t *TargetProxyCertificate) VetForWrite(ctx context.Context, _ db.Reader, opType db.OpType, _ ...db.Option) error {
|
||||
const op = "target.(TargetProxyCertificate).VetForWrite"
|
||||
switch {
|
||||
case t.PrivateKeyEncrypted == nil:
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing private key")
|
||||
case t.PublicKey == nil:
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing public key")
|
||||
case t.KeyId == "":
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing key id")
|
||||
case t.TargetId == "":
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing target id")
|
||||
case len(t.Certificate) == 0:
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing certificate")
|
||||
case t.NotValidAfter == nil:
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing not valid after")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TableName returns the table name.
|
||||
func (t *TargetProxyCertificate) TableName() string {
|
||||
return "target_proxy_certificate"
|
||||
}
|
||||
|
||||
// TargetAliasProxyCertificate represents a certificate for a target accessed with an alias
|
||||
type TargetAliasProxyCertificate struct {
|
||||
*store.TargetAliasProxyCertificate
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// NewTargetAliasProxyCertificate creates a new in memory TargetAliasProxyCertificate
|
||||
func NewTargetAliasProxyCertificate(ctx context.Context, targetId string, alias *talias.Alias) (*TargetAliasProxyCertificate, error) {
|
||||
const op = "target.NewTargetAliasProxyCertificate"
|
||||
switch {
|
||||
case targetId == "":
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing target id")
|
||||
case alias == nil:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing target alias")
|
||||
}
|
||||
|
||||
privKey, pubKey, err := generatePrivAndPubKeys(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error generating target proxy certificate key"))
|
||||
}
|
||||
notValidAfter := time.Now().AddDate(1, 0, 0) // 1 year from now
|
||||
parsedKey, err := x509.ParseECPrivateKey(privKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error parsing target proxy certificate key"))
|
||||
}
|
||||
certBytes, err := generateTargetCert(ctx, parsedKey, notValidAfter, WithAlias(alias))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error generating target certificate"))
|
||||
}
|
||||
|
||||
return &TargetAliasProxyCertificate{
|
||||
TargetAliasProxyCertificate: &store.TargetAliasProxyCertificate{
|
||||
TargetId: targetId,
|
||||
PrivateKey: privKey,
|
||||
PublicKey: pubKey,
|
||||
AliasId: alias.PublicId,
|
||||
Certificate: certBytes,
|
||||
NotValidAfter: timestamp.New(notValidAfter),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// encrypt the target cert key before writing it to the db
|
||||
func (t *TargetAliasProxyCertificate) encrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "target.(TargetAliasProxyCertificate).encrypt"
|
||||
if cipher == nil {
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing cipher")
|
||||
}
|
||||
if err := structwrapping.WrapStruct(ctx, cipher, t.TargetAliasProxyCertificate, nil); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt))
|
||||
}
|
||||
keyId, err := cipher.KeyId(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("failed to read cipher key id"))
|
||||
}
|
||||
t.KeyId = keyId
|
||||
return nil
|
||||
}
|
||||
|
||||
// decrypt the target cert key after reading it from the db
|
||||
func (t *TargetAliasProxyCertificate) decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "target.(TargetAliasProxyCertificate).decrypt"
|
||||
if cipher == nil {
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing cipher")
|
||||
}
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, t.TargetAliasProxyCertificate, nil); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func allocTargetAliasProxyCertificate() *TargetAliasProxyCertificate {
|
||||
return &TargetAliasProxyCertificate{
|
||||
TargetAliasProxyCertificate: &store.TargetAliasProxyCertificate{},
|
||||
}
|
||||
}
|
||||
|
||||
// Clone creates a clone of the TargetAliasProxyCertificate
|
||||
func (t *TargetAliasProxyCertificate) Clone() *TargetAliasProxyCertificate {
|
||||
cp := proto.Clone(t.TargetAliasProxyCertificate)
|
||||
return &TargetAliasProxyCertificate{
|
||||
TargetAliasProxyCertificate: cp.(*store.TargetAliasProxyCertificate),
|
||||
}
|
||||
}
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the target alias certificate
|
||||
func (t *TargetAliasProxyCertificate) VetForWrite(ctx context.Context, _ db.Reader, opType db.OpType, _ ...db.Option) error {
|
||||
const op = "target.(TargetAliasProxyCertificate).VetForWrite"
|
||||
switch {
|
||||
case t.PrivateKeyEncrypted == nil:
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing private key")
|
||||
case t.PublicKey == nil:
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing public key")
|
||||
case t.KeyId == "":
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing key id")
|
||||
case t.TargetId == "":
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing target id")
|
||||
case t.AliasId == "":
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing alias id")
|
||||
case len(t.Certificate) == 0:
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing certificate")
|
||||
case t.NotValidAfter == nil:
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing not valid after")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TableName returns the table name.
|
||||
func (t *TargetAliasProxyCertificate) TableName() string {
|
||||
return "target_alias_proxy_certificate"
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package target
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
talias "github.com/hashicorp/boundary/internal/alias/target"
|
||||
astore "github.com/hashicorp/boundary/internal/alias/target/store"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
||||
"github.com/hashicorp/go-kms-wrapping/v2/aead"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGenerateTargetCert(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
|
||||
privKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
aliasValue := "test-alias-value"
|
||||
alias := &talias.Alias{
|
||||
Alias: &astore.Alias{
|
||||
Value: aliasValue,
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
privKey *ecdsa.PrivateKey
|
||||
exp time.Time
|
||||
opt []Option
|
||||
wantErr bool
|
||||
wantErrContains string
|
||||
}{
|
||||
{
|
||||
name: "valid-target-proxy-cert",
|
||||
privKey: privKey,
|
||||
exp: time.Now().Add(1 * time.Hour),
|
||||
},
|
||||
{
|
||||
name: "valid-with-alias",
|
||||
privKey: privKey,
|
||||
exp: time.Now().Add(1 * time.Hour),
|
||||
opt: []Option{
|
||||
WithAlias(alias),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid-expiration",
|
||||
privKey: privKey,
|
||||
exp: time.Now().Add(-1 * time.Hour),
|
||||
wantErr: true,
|
||||
wantErrContains: "expiration time must be in the future",
|
||||
},
|
||||
{
|
||||
name: "missing-key",
|
||||
exp: time.Now().Add(1 * time.Hour),
|
||||
wantErr: true,
|
||||
wantErrContains: "missing private key",
|
||||
},
|
||||
{
|
||||
name: "missing-expiry",
|
||||
privKey: privKey,
|
||||
wantErr: true,
|
||||
wantErrContains: "missing expiry",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
got, err := generateTargetCert(ctx, tt.privKey, tt.exp, tt.opt...)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
require.NotNil(got)
|
||||
|
||||
pCert, err := x509.ParseCertificate(got)
|
||||
require.NoError(err)
|
||||
require.NotNil(pCert)
|
||||
|
||||
require.Equal("localhost", pCert.Subject.CommonName)
|
||||
// Cert timestamps do not have ms resolution
|
||||
tt.exp = tt.exp.Truncate(time.Second)
|
||||
require.True(tt.exp.Equal(pCert.NotAfter))
|
||||
require.Contains(pCert.DNSNames, "localhost")
|
||||
if tt.opt != nil {
|
||||
opts := GetOpts(tt.opt...)
|
||||
require.Contains(pCert.DNSNames, opts.withAlias.Value)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTargetProxyCertificate(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
|
||||
tId := "test-target-id"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
opt []Option
|
||||
wantErr bool
|
||||
wantErrContains string
|
||||
}{
|
||||
{
|
||||
name: "valid-target-proxy-cert",
|
||||
},
|
||||
{
|
||||
name: "valid-with-options",
|
||||
opt: []Option{
|
||||
WithTargetId(tId),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
gotCert, err := NewTargetProxyCertificate(ctx, tt.opt...)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
require.NotNil(gotCert)
|
||||
|
||||
if tt.opt != nil {
|
||||
assert.Equal(tId, gotCert.TargetId)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_encrypt_decrypt_TargetCert(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rootWrapper := db.TestWrapper(t)
|
||||
kmsCache := kms.TestKms(t, conn, rootWrapper)
|
||||
org, proj := iam.TestScopes(t, iam.TestRepo(t, conn, rootWrapper))
|
||||
databaseWrapper, err := kmsCache.GetWrapper(ctx, org.PublicId, kms.KeyPurposeDatabase)
|
||||
require.NoError(t, err)
|
||||
projDatabaseWrapper, err := kmsCache.GetWrapper(ctx, proj.PublicId, kms.KeyPurposeDatabase)
|
||||
require.NoError(t, err)
|
||||
|
||||
proxyCert, err := NewTargetProxyCertificate(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
certKey *TargetProxyCertificate
|
||||
encryptWrapper wrapping.Wrapper
|
||||
wantEncryptErrMatch *errors.Template
|
||||
decryptWrapper wrapping.Wrapper
|
||||
wantDecryptErrMatch *errors.Template
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
certKey: proxyCert,
|
||||
encryptWrapper: databaseWrapper,
|
||||
decryptWrapper: databaseWrapper,
|
||||
},
|
||||
{
|
||||
name: "encrypt-missing-wrapper",
|
||||
certKey: proxyCert,
|
||||
wantEncryptErrMatch: errors.T(errors.InvalidParameter),
|
||||
},
|
||||
{
|
||||
name: "encrypt-bad-wrapper",
|
||||
certKey: proxyCert,
|
||||
encryptWrapper: &aead.Wrapper{},
|
||||
wantEncryptErrMatch: errors.T(errors.Encrypt),
|
||||
},
|
||||
{
|
||||
name: "encrypt-missing-wrapper",
|
||||
certKey: proxyCert,
|
||||
encryptWrapper: databaseWrapper,
|
||||
wantDecryptErrMatch: errors.T(errors.InvalidParameter),
|
||||
},
|
||||
{
|
||||
name: "decrypt-bad-wrapper",
|
||||
certKey: proxyCert,
|
||||
encryptWrapper: databaseWrapper,
|
||||
decryptWrapper: &aead.Wrapper{},
|
||||
wantDecryptErrMatch: errors.T(errors.Decrypt),
|
||||
},
|
||||
{
|
||||
name: "decrypt-wrong-wrapper",
|
||||
certKey: proxyCert,
|
||||
encryptWrapper: databaseWrapper,
|
||||
decryptWrapper: projDatabaseWrapper,
|
||||
wantDecryptErrMatch: errors.T(errors.Decrypt),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
encryptedCert := tt.certKey.Clone()
|
||||
err = encryptedCert.encrypt(ctx, tt.encryptWrapper)
|
||||
if tt.wantEncryptErrMatch != nil {
|
||||
require.Error(err)
|
||||
assert.Truef(errors.Match(tt.wantEncryptErrMatch, err), "expected %q and got err: %+v", tt.wantEncryptErrMatch.Code, err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.NotEmpty(encryptedCert.PrivateKeyEncrypted)
|
||||
|
||||
decryptedCert := encryptedCert.Clone()
|
||||
decryptedCert.PrivateKey = []byte("")
|
||||
err = decryptedCert.decrypt(ctx, tt.decryptWrapper)
|
||||
if tt.wantDecryptErrMatch != nil {
|
||||
require.Error(err)
|
||||
assert.Truef(errors.Match(tt.wantDecryptErrMatch, err), "expected %q and got err: %+v", tt.wantDecryptErrMatch.Code, err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tt.certKey.PrivateKey, decryptedCert.PrivateKey)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTargetAliasProxyCertificate(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
|
||||
tId := "test-target-id"
|
||||
tAId := "test-alias-id"
|
||||
aliasValue := "test-alias-value"
|
||||
alias := &talias.Alias{
|
||||
Alias: &astore.Alias{
|
||||
PublicId: tAId,
|
||||
Value: aliasValue,
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
targetId string
|
||||
alias *talias.Alias
|
||||
wantErr bool
|
||||
wantErrContains string
|
||||
}{
|
||||
{
|
||||
name: "valid-target-cert",
|
||||
targetId: tId,
|
||||
alias: alias,
|
||||
},
|
||||
{
|
||||
name: "missing-target-id",
|
||||
alias: alias,
|
||||
wantErr: true,
|
||||
wantErrContains: "missing target id",
|
||||
},
|
||||
{
|
||||
name: "missing-alias",
|
||||
targetId: tId,
|
||||
wantErr: true,
|
||||
wantErrContains: "missing alias",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
gotCert, err := NewTargetAliasProxyCertificate(ctx, tt.targetId, tt.alias)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
require.NotNil(gotCert)
|
||||
assert.Equal(tId, gotCert.TargetId)
|
||||
assert.Equal(tAId, gotCert.AliasId)
|
||||
assert.NotNil(gotCert.Certificate)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_encrypt_decrypt_TargetAliasCert(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rootWrapper := db.TestWrapper(t)
|
||||
kmsCache := kms.TestKms(t, conn, rootWrapper)
|
||||
org, proj := iam.TestScopes(t, iam.TestRepo(t, conn, rootWrapper))
|
||||
databaseWrapper, err := kmsCache.GetWrapper(ctx, org.PublicId, kms.KeyPurposeDatabase)
|
||||
require.NoError(t, err)
|
||||
projDatabaseWrapper, err := kmsCache.GetWrapper(ctx, proj.PublicId, kms.KeyPurposeDatabase)
|
||||
require.NoError(t, err)
|
||||
|
||||
tId := "test-target-id"
|
||||
tAId := "test-alias-id"
|
||||
aliasValue := "test-alias-value"
|
||||
alias := &talias.Alias{
|
||||
Alias: &astore.Alias{
|
||||
PublicId: tAId,
|
||||
Value: aliasValue,
|
||||
},
|
||||
}
|
||||
|
||||
proxyCert, err := NewTargetAliasProxyCertificate(ctx, tId, alias)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
certKey *TargetAliasProxyCertificate
|
||||
encryptWrapper wrapping.Wrapper
|
||||
wantEncryptErrMatch *errors.Template
|
||||
decryptWrapper wrapping.Wrapper
|
||||
wantDecryptErrMatch *errors.Template
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
certKey: proxyCert,
|
||||
encryptWrapper: databaseWrapper,
|
||||
decryptWrapper: databaseWrapper,
|
||||
},
|
||||
{
|
||||
name: "encrypt-missing-wrapper",
|
||||
certKey: proxyCert,
|
||||
wantEncryptErrMatch: errors.T(errors.InvalidParameter),
|
||||
},
|
||||
{
|
||||
name: "encrypt-bad-wrapper",
|
||||
certKey: proxyCert,
|
||||
encryptWrapper: &aead.Wrapper{},
|
||||
wantEncryptErrMatch: errors.T(errors.Encrypt),
|
||||
},
|
||||
{
|
||||
name: "encrypt-missing-wrapper",
|
||||
certKey: proxyCert,
|
||||
encryptWrapper: databaseWrapper,
|
||||
wantDecryptErrMatch: errors.T(errors.InvalidParameter),
|
||||
},
|
||||
{
|
||||
name: "decrypt-bad-wrapper",
|
||||
certKey: proxyCert,
|
||||
encryptWrapper: databaseWrapper,
|
||||
decryptWrapper: &aead.Wrapper{},
|
||||
wantDecryptErrMatch: errors.T(errors.Decrypt),
|
||||
},
|
||||
{
|
||||
name: "decrypt-wrong-wrapper",
|
||||
certKey: proxyCert,
|
||||
encryptWrapper: databaseWrapper,
|
||||
decryptWrapper: projDatabaseWrapper,
|
||||
wantDecryptErrMatch: errors.T(errors.Decrypt),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
encryptedCert := tt.certKey.Clone()
|
||||
err = encryptedCert.encrypt(ctx, tt.encryptWrapper)
|
||||
if tt.wantEncryptErrMatch != nil {
|
||||
require.Error(err)
|
||||
assert.Truef(errors.Match(tt.wantEncryptErrMatch, err), "expected %q and got err: %+v", tt.wantEncryptErrMatch.Code, err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.NotEmpty(encryptedCert.PrivateKeyEncrypted)
|
||||
|
||||
decryptedCert := encryptedCert.Clone()
|
||||
decryptedCert.PrivateKey = []byte("")
|
||||
err = decryptedCert.decrypt(ctx, tt.decryptWrapper)
|
||||
if tt.wantDecryptErrMatch != nil {
|
||||
require.Error(err)
|
||||
assert.Truef(errors.Match(tt.wantDecryptErrMatch, err), "expected %q and got err: %+v", tt.wantDecryptErrMatch.Code, err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tt.certKey.PrivateKey, decryptedCert.PrivateKey)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -161,6 +161,10 @@ func (t *Target) GetStorageBucketId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *Target) GetProxyServerCertificate() *target.ServerCertificate {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Target) Clone() target.Target {
|
||||
cp := proto.Clone(t.Target)
|
||||
return &Target{
|
||||
@@ -253,6 +257,8 @@ func (t *Target) SetEnableSessionRecording(_ bool) {}
|
||||
|
||||
func (t *Target) SetStorageBucketId(_ string) {}
|
||||
|
||||
func (t *Target) SetProxyServerCertificate(*target.ServerCertificate) {}
|
||||
|
||||
func (t *Target) Oplog(op oplog.OpType) oplog.Metadata {
|
||||
return oplog.Metadata{
|
||||
"resource-public-id": []string{t.PublicId},
|
||||
|
||||
@@ -164,6 +164,10 @@ func (t *Target) GetStorageBucketId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *Target) GetProxyServerCertificate() *target.ServerCertificate {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Target) SetPublicId(ctx context.Context, publicId string) error {
|
||||
const op = "tcp.(Target).SetPublicId"
|
||||
if !strings.HasPrefix(publicId, TargetPrefix+"_") {
|
||||
@@ -247,5 +251,6 @@ func (t *Target) SetCredentialSources(sources []target.CredentialSource) {
|
||||
t.CredentialSources = sources
|
||||
}
|
||||
|
||||
func (t *Target) SetEnableSessionRecording(_ bool) {}
|
||||
func (t *Target) SetStorageBucketId(_ string) {}
|
||||
func (t *Target) SetEnableSessionRecording(_ bool) {}
|
||||
func (t *Target) SetStorageBucketId(_ string) {}
|
||||
func (t *Target) SetProxyServerCertificate(*target.ServerCertificate) {}
|
||||
Reference in new issue
Block a user