mirror of https://github.com/hashicorp/boundary
Egress credentials Issued during AuthorizeSession are stored in the session repo. These credentials are retrieved and stored in-memory on the worker during the LookupSession call. Each proxy handler will need to handle the credentials as required.pull/1804/head
parent
5fe23ab14d
commit
425a56179f
@ -0,0 +1,253 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.17.3
|
||||
// source: controller/servers/services/v1/credential.proto
|
||||
|
||||
package services
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
type Credential struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Types that are assignable to Credential:
|
||||
// *Credential_UserPassword
|
||||
Credential isCredential_Credential `protobuf_oneof:"credential"`
|
||||
}
|
||||
|
||||
func (x *Credential) Reset() {
|
||||
*x = Credential{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_servers_services_v1_credential_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Credential) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Credential) ProtoMessage() {}
|
||||
|
||||
func (x *Credential) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_servers_services_v1_credential_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 Credential.ProtoReflect.Descriptor instead.
|
||||
func (*Credential) Descriptor() ([]byte, []int) {
|
||||
return file_controller_servers_services_v1_credential_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (m *Credential) GetCredential() isCredential_Credential {
|
||||
if m != nil {
|
||||
return m.Credential
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Credential) GetUserPassword() *UserPassword {
|
||||
if x, ok := x.GetCredential().(*Credential_UserPassword); ok {
|
||||
return x.UserPassword
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isCredential_Credential interface {
|
||||
isCredential_Credential()
|
||||
}
|
||||
|
||||
type Credential_UserPassword struct {
|
||||
UserPassword *UserPassword `protobuf:"bytes,1,opt,name=user_password,json=userPassword,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*Credential_UserPassword) isCredential_Credential() {}
|
||||
|
||||
// UserPassword is a credential containing a username and a password.
|
||||
type UserPassword struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// The username of the credential
|
||||
Username string `protobuf:"bytes,10,opt,name=username,proto3" json:"username,omitempty"` // @gotags: `class:"public"`
|
||||
// The password of the credential
|
||||
Password string `protobuf:"bytes,20,opt,name=password,proto3" json:"password,omitempty"` // @gotags: `class:"secret"`
|
||||
}
|
||||
|
||||
func (x *UserPassword) Reset() {
|
||||
*x = UserPassword{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_servers_services_v1_credential_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *UserPassword) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UserPassword) ProtoMessage() {}
|
||||
|
||||
func (x *UserPassword) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_servers_services_v1_credential_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 UserPassword.ProtoReflect.Descriptor instead.
|
||||
func (*UserPassword) Descriptor() ([]byte, []int) {
|
||||
return file_controller_servers_services_v1_credential_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *UserPassword) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *UserPassword) GetPassword() string {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_controller_servers_services_v1_credential_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_controller_servers_services_v1_credential_proto_rawDesc = []byte{
|
||||
0x0a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72,
|
||||
0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x76, 0x31,
|
||||
0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x12, 0x1e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65,
|
||||
0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76,
|
||||
0x31, 0x22, 0x6f, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12,
|
||||
0x53, 0x0a, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
|
||||
0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x50, 0x61, 0x73, 0x73,
|
||||
0x77, 0x6f, 0x72, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x75, 0x73, 0x65, 0x72, 0x50, 0x61, 0x73, 0x73,
|
||||
0x77, 0x6f, 0x72, 0x64, 0x42, 0x0c, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
|
||||
0x61, 0x6c, 0x22, 0x46, 0x0a, 0x0c, 0x55, 0x73, 0x65, 0x72, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f,
|
||||
0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a,
|
||||
0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x42, 0x51, 0x5a, 0x4f, 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, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
|
||||
0x6c, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_controller_servers_services_v1_credential_proto_rawDescOnce sync.Once
|
||||
file_controller_servers_services_v1_credential_proto_rawDescData = file_controller_servers_services_v1_credential_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_controller_servers_services_v1_credential_proto_rawDescGZIP() []byte {
|
||||
file_controller_servers_services_v1_credential_proto_rawDescOnce.Do(func() {
|
||||
file_controller_servers_services_v1_credential_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_servers_services_v1_credential_proto_rawDescData)
|
||||
})
|
||||
return file_controller_servers_services_v1_credential_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_servers_services_v1_credential_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_controller_servers_services_v1_credential_proto_goTypes = []interface{}{
|
||||
(*Credential)(nil), // 0: controller.servers.services.v1.Credential
|
||||
(*UserPassword)(nil), // 1: controller.servers.services.v1.UserPassword
|
||||
}
|
||||
var file_controller_servers_services_v1_credential_proto_depIdxs = []int32{
|
||||
1, // 0: controller.servers.services.v1.Credential.user_password:type_name -> controller.servers.services.v1.UserPassword
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_servers_services_v1_credential_proto_init() }
|
||||
func file_controller_servers_services_v1_credential_proto_init() {
|
||||
if File_controller_servers_services_v1_credential_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_controller_servers_services_v1_credential_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Credential); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_controller_servers_services_v1_credential_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*UserPassword); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_controller_servers_services_v1_credential_proto_msgTypes[0].OneofWrappers = []interface{}{
|
||||
(*Credential_UserPassword)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_controller_servers_services_v1_credential_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_controller_servers_services_v1_credential_proto_goTypes,
|
||||
DependencyIndexes: file_controller_servers_services_v1_credential_proto_depIdxs,
|
||||
MessageInfos: file_controller_servers_services_v1_credential_proto_msgTypes,
|
||||
}.Build()
|
||||
File_controller_servers_services_v1_credential_proto = out.File
|
||||
file_controller_servers_services_v1_credential_proto_rawDesc = nil
|
||||
file_controller_servers_services_v1_credential_proto_goTypes = nil
|
||||
file_controller_servers_services_v1_credential_proto_depIdxs = nil
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package controller.servers.services.v1;
|
||||
|
||||
option go_package = "github.com/hashicorp/boundary/internal/gen/controller/servers/services;services";
|
||||
|
||||
message Credential {
|
||||
oneof credential {
|
||||
UserPassword user_password = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// UserPassword is a credential containing a username and a password.
|
||||
message UserPassword {
|
||||
// The username of the credential
|
||||
string username = 10; // @gotags: `class:"public"`
|
||||
|
||||
// The password of the credential
|
||||
string password = 20; // @gotags: `class:"secret"`
|
||||
}
|
||||
@ -0,0 +1,173 @@
|
||||
package workers_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/servers"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/workers"
|
||||
"github.com/hashicorp/boundary/internal/session"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/internal/target/tcp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestLookupSession(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
|
||||
serversRepoFn := func() (*servers.Repository, error) {
|
||||
return servers.NewRepository(rw, rw, kms)
|
||||
}
|
||||
sessionRepoFn := func() (*session.Repository, error) {
|
||||
return session.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
uId := at.GetIamUserId()
|
||||
hc := static.TestCatalogs(t, conn, prj.GetPublicId(), 1)[0]
|
||||
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
||||
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
||||
static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h})
|
||||
tar := tcp.TestTarget(ctx, t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()}))
|
||||
|
||||
sess := session.TestSession(t, conn, wrapper, session.ComposedOf{
|
||||
UserId: uId,
|
||||
HostId: h.GetPublicId(),
|
||||
TargetId: tar.GetPublicId(),
|
||||
HostSetId: hs.GetPublicId(),
|
||||
AuthTokenId: at.GetPublicId(),
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Endpoint: "tcp://127.0.0.1:22",
|
||||
})
|
||||
|
||||
egressSess := session.TestSession(t, conn, wrapper, session.ComposedOf{
|
||||
UserId: uId,
|
||||
HostId: h.GetPublicId(),
|
||||
TargetId: tar.GetPublicId(),
|
||||
HostSetId: hs.GetPublicId(),
|
||||
AuthTokenId: at.GetPublicId(),
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Endpoint: "tcp://127.0.0.1:22",
|
||||
})
|
||||
|
||||
repo, err := sessionRepoFn()
|
||||
require.NoError(t, err)
|
||||
|
||||
creds := []*pbs.Credential{
|
||||
{
|
||||
Credential: &pbs.Credential_UserPassword{
|
||||
UserPassword: &pbs.UserPassword{
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Credential: &pbs.Credential_UserPassword{
|
||||
UserPassword: &pbs.UserPassword{
|
||||
Username: "another-username",
|
||||
Password: "a different password",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
workerCreds := make([]session.Credential, 0, len(creds))
|
||||
for _, c := range creds {
|
||||
data, err := proto.Marshal(c)
|
||||
require.NoError(t, err)
|
||||
workerCreds = append(workerCreds, data)
|
||||
}
|
||||
err = repo.AddSessionCredentials(ctx, egressSess.ScopeId, egressSess.GetPublicId(), workerCreds)
|
||||
require.NoError(t, err)
|
||||
|
||||
s := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, new(sync.Map), kms)
|
||||
require.NotNil(t, s)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
wantErr bool
|
||||
wantErrMsg string
|
||||
want *pbs.LookupSessionResponse
|
||||
sessionId string
|
||||
}{
|
||||
{
|
||||
name: "Invalid session id",
|
||||
sessionId: "s_fakesession",
|
||||
wantErr: true,
|
||||
wantErrMsg: "rpc error: code = PermissionDenied desc = Unknown session ID.",
|
||||
},
|
||||
{
|
||||
name: "Valid",
|
||||
sessionId: sess.PublicId,
|
||||
want: &pbs.LookupSessionResponse{
|
||||
ConnectionLimit: 1,
|
||||
ConnectionsLeft: 1,
|
||||
Version: 1,
|
||||
Endpoint: sess.Endpoint,
|
||||
HostId: sess.HostId,
|
||||
HostSetId: sess.HostSetId,
|
||||
TargetId: sess.TargetId,
|
||||
UserId: sess.UserId,
|
||||
Status: pbs.SESSIONSTATUS_SESSIONSTATUS_PENDING,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Valid-with-egress-creds",
|
||||
sessionId: egressSess.PublicId,
|
||||
want: &pbs.LookupSessionResponse{
|
||||
ConnectionLimit: 1,
|
||||
ConnectionsLeft: 1,
|
||||
Version: 1,
|
||||
Endpoint: egressSess.Endpoint,
|
||||
HostId: egressSess.HostId,
|
||||
HostSetId: egressSess.HostSetId,
|
||||
TargetId: egressSess.TargetId,
|
||||
UserId: egressSess.UserId,
|
||||
Status: pbs.SESSIONSTATUS_SESSIONSTATUS_PENDING,
|
||||
Credentials: creds,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
req := &pbs.LookupSessionRequest{
|
||||
SessionId: tc.sessionId,
|
||||
}
|
||||
|
||||
got, err := s.LookupSession(ctx, req)
|
||||
if tc.wantErr {
|
||||
require.Error(err)
|
||||
assert.Nil(got)
|
||||
assert.Equal(tc.wantErrMsg, err.Error())
|
||||
return
|
||||
}
|
||||
assert.Empty(
|
||||
cmp.Diff(
|
||||
tc.want,
|
||||
got,
|
||||
cmpopts.IgnoreUnexported(pbs.LookupSessionResponse{}, pbs.Credential{}, pbs.UserPassword{}),
|
||||
cmpopts.IgnoreFields(pbs.LookupSessionResponse{}, "Expiration", "Authorization"),
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue