Add ssh_private_key support for Vault libraries and targets (#2263)

* Add support for ssh_private_key credential type in Vault libraries

* Add support for ssh_private_key credential type

* Support jsonpointer for Vault library mapping overrides
This commit is contained in:
Louis Ruch authored and GitHub committed 2022-07-19 10:31:53 -07:00
1 parent 011e2e7425
commit ef5ac07f02
33 files changed
+2805 -234

No files matched your search

+3 -11
View File
@@ -132,17 +132,9 @@ type UsernamePassword interface {
Password() Password
}
// KeyPair is a credential containing a username and a private key.
type KeyPair interface {
// SshPrivateKey is a credential containing a username and a SSH private key.
type SshPrivateKey interface {
Credential
Username() string
Private() PrivateKey
}
// Certificate is a credential containing a certificate and the private key
// for the certificate.
type Certificate interface {
Credential
Certificate() []byte
Private() PrivateKey
PrivateKey() PrivateKey
}
@@ -17,18 +17,37 @@ func (r *Repository) Retrieve(ctx context.Context, scopeId string, ids []string)
return nil, errors.New(ctx, errors.InvalidParameter, op, "no ids")
}
var creds []*UsernamePasswordCredential
err := r.reader.SearchWhere(ctx, &creds, "public_id in (?)", []interface{}{ids})
var upCreds []*UsernamePasswordCredential
err := r.reader.SearchWhere(ctx, &upCreds, "public_id in (?)", []interface{}{ids})
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if len(creds) != len(ids) {
var spkCreds []*SshPrivateKeyCredential
err = r.reader.SearchWhere(ctx, &spkCreds, "public_id in (?)", []interface{}{ids})
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if len(upCreds)+len(spkCreds) != len(ids) {
return nil, errors.New(ctx, errors.NotSpecificIntegrity, op,
fmt.Sprintf("mismatch between creds and number of ids requested, expected %d got %d", len(ids), len(creds)))
fmt.Sprintf("mismatch between creds and number of ids requested, expected %d got %d", len(ids), len(upCreds)+len(spkCreds)))
}
out := make([]credential.Static, 0, len(ids))
for _, c := range creds {
for _, c := range upCreds {
// decrypt credential
databaseWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper"))
}
if err := c.decrypt(ctx, databaseWrapper); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
out = append(out, c)
}
for _, c := range spkCreds {
// decrypt credential
databaseWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
if err != nil {
@@ -14,6 +14,7 @@ import (
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/ssh/testdata"
)
func TestRepository_Retrieve(t *testing.T) {
@@ -30,9 +31,10 @@ func TestRepository_Retrieve(t *testing.T) {
org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
staticStore := TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
cred1 := TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", staticStore.GetPublicId(), prj.GetPublicId())
cred2 := TestUsernamePasswordCredential(t, conn, wrapper, "different user", "better password", staticStore.GetPublicId(), prj.GetPublicId())
cred3 := TestUsernamePasswordCredential(t, conn, wrapper, "final user", "horrible password", staticStore.GetPublicId(), prj.GetPublicId())
upCred1 := TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", staticStore.GetPublicId(), prj.GetPublicId())
upCred2 := TestUsernamePasswordCredential(t, conn, wrapper, "different user", "better password", staticStore.GetPublicId(), prj.GetPublicId())
spkCred1 := TestSshPrivateKeyCredential(t, conn, wrapper, "final user", string(testdata.PEMBytes["ed25519"]), staticStore.GetPublicId(), prj.GetPublicId())
spkCred2 := TestSshPrivateKeyCredential(t, conn, wrapper, "last user", string(testdata.PEMBytes["rsa-openssh-format"]), staticStore.GetPublicId(), prj.GetPublicId())
type args struct {
credIds []string
@@ -47,7 +49,7 @@ func TestRepository_Retrieve(t *testing.T) {
{
name: "no-scope",
args: args{
credIds: []string{cred1.GetPublicId()},
credIds: []string{upCred1.GetPublicId()},
},
wantErr: true,
},
@@ -55,28 +57,58 @@ func TestRepository_Retrieve(t *testing.T) {
name: "invalid-scope",
args: args{
scopeId: org.GetPublicId(),
credIds: []string{cred1.GetPublicId()},
credIds: []string{upCred1.GetPublicId()},
},
wantErr: true,
},
{
name: "valid-one-cred",
name: "valid-one-up-cred",
args: args{
scopeId: prj.GetPublicId(),
credIds: []string{cred1.GetPublicId()},
credIds: []string{upCred1.GetPublicId()},
},
wantCreds: []credential.Static{
cred1,
upCred1,
},
},
{
name: "valid-multiple-creds",
name: "valid-multiple-up-creds",
args: args{
scopeId: prj.GetPublicId(),
credIds: []string{cred1.GetPublicId(), cred2.GetPublicId(), cred3.GetPublicId()},
credIds: []string{upCred1.GetPublicId(), upCred2.GetPublicId()},
},
wantCreds: []credential.Static{
cred1, cred2, cred3,
upCred1, upCred2,
},
},
{
name: "valid-ssh-pk-cred",
args: args{
scopeId: prj.GetPublicId(),
credIds: []string{spkCred1.GetPublicId()},
},
wantCreds: []credential.Static{
spkCred1,
},
},
{
name: "valid-multiple-ssh-pk-creds",
args: args{
scopeId: prj.GetPublicId(),
credIds: []string{spkCred1.GetPublicId(), spkCred2.GetPublicId()},
},
wantCreds: []credential.Static{
spkCred1, spkCred2,
},
},
{
name: "valid-mixed-creds",
args: args{
scopeId: prj.GetPublicId(),
credIds: []string{upCred1.GetPublicId(), spkCred1.GetPublicId(), spkCred2.GetPublicId(), upCred2.GetPublicId()},
},
wantCreds: []credential.Static{
upCred1, spkCred1, spkCred2, upCred2,
},
},
}
@@ -94,7 +126,9 @@ func TestRepository_Retrieve(t *testing.T) {
cmp.Diff(
tt.wantCreds,
gotCreds,
cmpopts.IgnoreUnexported(UsernamePasswordCredential{}, store.UsernamePasswordCredential{}),
cmpopts.IgnoreUnexported(
UsernamePasswordCredential{}, store.UsernamePasswordCredential{},
SshPrivateKeyCredential{}, store.SshPrivateKeyCredential{}),
cmpopts.IgnoreTypes(&timestamp.Timestamp{}),
cmpopts.SortSlices(func(x, y credential.Static) bool {
return x.GetPublicId() < y.GetPublicId()
@@ -234,11 +234,14 @@ func TestCredentialLibrary_New(t *testing.T) {
opts: []Option{
WithMethod(MethodGet),
WithCredentialType(credential.UsernamePasswordType),
WithMappingOverride(NewUsernamePasswordOverride(WithOverrideUsernameAttribute("test"))),
WithMappingOverride(NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("test"),
WithOverridePasswordAttribute("testpass")),
),
},
},
want: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(WithOverrideUsernameAttribute("test")),
MappingOverride: NewUsernamePasswordOverride(WithOverrideUsernameAttribute("test"), WithOverridePasswordAttribute("testpass")),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.PublicId,
VaultPath: "vault/path",
@@ -247,6 +250,30 @@ func TestCredentialLibrary_New(t *testing.T) {
},
},
},
{
name: "credential-type-with-ssh-pk-mapping",
args: args{
storeId: cs.PublicId,
vaultPath: "vault/path",
opts: []Option{
WithMethod(MethodGet),
WithCredentialType(credential.SshPrivateKeyType),
WithMappingOverride(NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("test"),
WithOverridePrivateKeyAttribute("testpk")),
),
},
},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(WithOverrideUsernameAttribute("test"), WithOverridePrivateKeyAttribute("testpk")),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.PublicId,
VaultPath: "vault/path",
HttpMethod: string(MethodGet),
CredentialType: string(credential.SshPrivateKeyType),
},
},
},
}
for _, tt := range tests {
@@ -264,6 +291,8 @@ func TestCredentialLibrary_New(t *testing.T) {
switch ct := tt.want.GetCredentialType(); ct {
case string(credential.UsernamePasswordType):
assert.Equal(credential.UsernamePasswordType, got.CredentialType())
case string(credential.SshPrivateKeyType):
assert.Equal(credential.SshPrivateKeyType, got.CredentialType())
case string(credential.UnspecifiedType), "":
assert.Equal(credential.UnspecifiedType, got.CredentialType())
default:
@@ -0,0 +1,3 @@
// Package sshprivatekey provides access to the username and ssh private key
// stored in a Vault secret.
package sshprivatekey
@@ -0,0 +1,135 @@
package sshprivatekey
import (
"strings"
"github.com/hashicorp/boundary/internal/credential"
"github.com/mitchellh/pointerstructure"
)
type (
data map[string]any
// extractFunc attempts to extract the username and private key
// from sd using the provided attribute names, using a known
// Vault data response format.
extractFunc func(sd data, usernameAttr, privateKeyAttr string) (string, credential.PrivateKey)
)
// Extract attempts to extract the values of the username and private key
// stored within the provided data using the given attribute names.
//
// Extract does not return partial results, i.e. if one of the attributes
// were extracted but not the other ("", nil) will be returned.
func Extract(d data, usernameAttr, privateKeyAttr string) (string, credential.PrivateKey) {
for _, f := range []extractFunc{
defaultExtract,
kv2Extract,
} {
username, privateKey := f(d, usernameAttr, privateKeyAttr)
if username != "" && privateKey != nil {
// got valid username and privateKey from secret
return username, privateKey
}
}
return "", nil
}
// defaultExtract looks for the usernameAttr and privateKeyAttr in the data map
func defaultExtract(sd data, usernameAttr, privateKeyAttr string) (username string, privateKey credential.PrivateKey) {
if sd == nil {
// nothing to do return early
return "", nil
}
var u any
switch {
case strings.HasPrefix(usernameAttr, "/"):
var err error
u, err = pointerstructure.Get(sd, usernameAttr)
if err != nil {
return "", nil
}
default:
u = sd[usernameAttr]
}
if u, ok := u.(string); ok {
username = u
}
var p any
switch {
case strings.HasPrefix(privateKeyAttr, "/"):
var err error
p, err = pointerstructure.Get(sd, privateKeyAttr)
if err != nil {
return "", nil
}
default:
p = sd[privateKeyAttr]
}
if p, ok := p.(string); ok {
privateKey = credential.PrivateKey(p)
}
return username, privateKey
}
// kv2Extract looks for the the usernameAttr and privateKeyAttr in the embedded
// 'data' field within the data map.
//
// Additionaly it validates the data is in the expected KV-v2 format:
// {
// "data": {},
// "metadata: {}
// }
// If the format does not match, it returns ("", ""). See:
// https://www.vaultproject.io/api/secret/kv/kv-v2#sample-response-1
func kv2Extract(sd data, usernameAttr, privateKeyAttr string) (username string, privateKey credential.PrivateKey) {
if sd == nil {
// nothing to do return early
return "", nil
}
var data, metadata map[string]any
for k, v := range sd {
switch k {
case "data":
var ok bool
if data, ok = v.(map[string]any); !ok {
// data field should be of type map[string]any in KV-v2
return "", nil
}
case "metadata":
var ok bool
if metadata, ok = v.(map[string]any); !ok {
// metadata field should be of type map[string]any in KV-v2
return "", nil
}
default:
// secretData contains a non valid KV-v2 top level field
return "", nil
}
}
if data == nil || metadata == nil {
// missing required KV-v2 field
return "", nil
}
if u, ok := data[usernameAttr]; ok {
if u, ok := u.(string); ok {
username = u
}
}
if p, ok := data[privateKeyAttr]; ok {
if p, ok := p.(string); ok {
privateKey = credential.PrivateKey(p)
}
}
return username, privateKey
}
@@ -0,0 +1,274 @@
package sshprivatekey
import (
"testing"
"github.com/hashicorp/boundary/internal/credential"
"github.com/stretchr/testify/assert"
"golang.org/x/crypto/ssh/testdata"
)
func TestExtract(t *testing.T) {
t.Parallel()
edKey := testdata.PEMBytes["ed25519"]
rsaKey := testdata.PEMBytes["rsa"]
type args struct {
s data
uAttr string
pAttr string
}
type sshpk struct {
user string
privateKey credential.PrivateKey
}
tests := []struct {
name string
given args
want sshpk
}{
{
name: "nil-input",
want: sshpk{user: "", privateKey: nil},
},
{
name: "no-input",
given: args{},
want: sshpk{user: "", privateKey: nil},
},
{
name: "no-secret",
given: args{
uAttr: "username",
pAttr: "private_key",
},
want: sshpk{user: "", privateKey: nil},
},
{
name: "no-match-username-secret",
given: args{
s: data{
"username-wrong": "user",
"private_key": string(edKey),
},
uAttr: "username",
pAttr: "private_key",
},
want: sshpk{user: "", privateKey: nil},
},
{
name: "no-match-private-key-secret",
given: args{
s: data{
"username": "user",
"private_key-wrong": string(edKey),
},
uAttr: "username",
pAttr: "private_key",
},
want: sshpk{user: "", privateKey: nil},
},
{
name: "valid-default",
given: args{
s: data{
"username": "user",
"private_key": string(edKey),
},
uAttr: "username",
pAttr: "private_key",
},
want: sshpk{user: "user", privateKey: edKey},
},
{
name: "valid-default-private-key-string",
given: args{
s: data{
"username": "user",
"private_key": string(edKey),
},
uAttr: "username",
pAttr: "private_key",
},
want: sshpk{user: "user", privateKey: edKey},
},
{
name: "no-match-username-secret-kv2",
given: args{
s: data{
"metadata": map[string]interface{}{},
"data": map[string]interface{}{
"username-wrong": "user",
"private_key": string(edKey),
},
},
uAttr: "username",
pAttr: "private_key",
},
want: sshpk{user: "", privateKey: nil},
},
{
name: "no-match-private-key-secret-kv2",
given: args{
s: data{
"metadata": map[string]interface{}{},
"data": map[string]interface{}{
"username": "user",
"private_key-wrong": string(edKey),
},
},
uAttr: "username",
pAttr: "private_key",
},
want: sshpk{user: "", privateKey: nil},
},
{
name: "valid-kv2",
given: args{
s: data{
"metadata": map[string]interface{}{},
"data": map[string]interface{}{
"username": "user",
"private_key": string(edKey),
},
},
uAttr: "username",
pAttr: "private_key",
},
want: sshpk{user: "user", privateKey: edKey},
},
{
name: "valid-kv2-private-key-string",
given: args{
s: data{
"metadata": map[string]interface{}{},
"data": map[string]interface{}{
"username": "user",
"private_key": string(edKey),
},
},
uAttr: "username",
pAttr: "private_key",
},
want: sshpk{user: "user", privateKey: edKey},
},
{
name: "no-metadata-kv2",
given: args{
s: data{
"data": map[string]interface{}{
"username": "user",
"private_key": string(edKey),
},
},
uAttr: "username",
pAttr: "private_key",
},
want: sshpk{user: "", privateKey: nil},
},
{
name: "invalid-metadata-kv2",
given: args{
s: data{
"metadata": "string",
"data": map[string]interface{}{
"username": "user",
"private_key": string(edKey),
},
},
uAttr: "username",
pAttr: "private_key",
},
want: sshpk{user: "", privateKey: nil},
},
{
name: "invalid-field-kv2",
given: args{
s: data{
"invalid": map[string]interface{}{},
"metadata": map[string]interface{}{},
"data": map[string]interface{}{
"username": "user",
"private_key": string(edKey),
},
},
uAttr: "username",
pAttr: "private_key",
},
want: sshpk{user: "", privateKey: nil},
},
{
name: "valid-order-default-first",
given: args{
s: data{
"username": "default-user",
"private_key": string(rsaKey),
"metadata": map[string]interface{}{},
"data": map[string]interface{}{
"username": "kv2-user",
"private_key": string(edKey),
},
},
uAttr: "username",
pAttr: "private_key",
},
want: sshpk{user: "default-user", privateKey: rsaKey},
},
{
name: "default-user-json-pointer-pk",
given: args{
s: data{
"username": "default-user",
"testing": map[string]interface{}{
"private_key": string(edKey),
},
},
uAttr: "username",
pAttr: "/testing/private_key",
},
want: sshpk{user: "default-user", privateKey: edKey},
},
{
name: "default-pk-json-pointer-user",
given: args{
s: data{
"private_key": string(edKey),
"testing": map[string]interface{}{
"special": "not-so-special",
},
},
uAttr: "/testing/special",
pAttr: "private_key",
},
want: sshpk{user: "not-so-special", privateKey: edKey},
},
{
name: "both-json-pointer",
given: args{
s: data{
"first-path": map[string]interface{}{
"deeper-path": map[string]interface{}{
"my-special-user": "you-found-me",
},
},
"testing": map[string]interface{}{
"private_key": string(edKey),
},
},
uAttr: "/first-path/deeper-path/my-special-user",
pAttr: "/testing/private_key",
},
want: sshpk{user: "you-found-me", privateKey: edKey},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
user, privateKey := Extract(tt.given.s, tt.given.uAttr, tt.given.pAttr)
assert.Equal(tt.want.user, user)
assert.Equal(tt.want.privateKey, privateKey)
})
}
}
@@ -1,5 +1,11 @@
package usernamepassword
import (
"strings"
"github.com/mitchellh/pointerstructure"
)
type (
data map[string]interface{}
@@ -31,15 +37,42 @@ func Extract(d data, usernameAttr, passwordAttr string) (string, string) {
// defaultExtract looks for the usernameAttr and passwordAttr in the data map
func defaultExtract(sd data, usernameAttr, passwordAttr string) (username string, password string) {
if u, ok := sd[usernameAttr]; ok {
if u, ok := u.(string); ok {
username = u
}
if sd == nil {
// nothing to do return early
return "", ""
}
if p, ok := sd[passwordAttr]; ok {
if p, ok := p.(string); ok {
password = p
var u any
switch {
case strings.HasPrefix(usernameAttr, "/"):
var err error
u, err = pointerstructure.Get(sd, usernameAttr)
if err != nil {
return "", ""
}
default:
u = sd[usernameAttr]
}
if u, ok := u.(string); ok {
username = u
}
var p any
switch {
case strings.HasPrefix(passwordAttr, "/"):
var err error
p, err = pointerstructure.Get(sd, passwordAttr)
if err != nil {
return "", ""
}
default:
p = sd[passwordAttr]
}
if p, ok := p.(string); ok {
password = p
}
return username, password
@@ -55,9 +88,14 @@ func defaultExtract(sd data, usernameAttr, passwordAttr string) (username string
// }
// If the format does not match, it returns ("", ""). See:
// https://www.vaultproject.io/api/secret/kv/kv-v2#sample-response-1
func kv2Extract(d data, usernameAttr, passwordAttr string) (username string, password string) {
func kv2Extract(sd data, usernameAttr, passwordAttr string) (username string, password string) {
if sd == nil {
// nothing to do return early
return "", ""
}
var data, metadata map[string]interface{}
for k, v := range d {
for k, v := range sd {
switch k {
case "data":
var ok bool
@@ -183,6 +183,52 @@ func TestBaseToUsrPass(t *testing.T) {
},
want: usrPass{user: "default-user", pass: "default-pass"},
},
{
name: "default-user-json-pointer-password",
given: args{
s: data{
"username": "default-user",
"testing": map[string]interface{}{
"my-password": "secret",
},
},
uAttr: "username",
pAttr: "/testing/my-password",
},
want: usrPass{user: "default-user", pass: "secret"},
},
{
name: "default-pk-json-pointer-user",
given: args{
s: data{
"password": "default-pass",
"testing": map[string]interface{}{
"a-user-name": "me",
},
},
uAttr: "/testing/a-user-name",
pAttr: "password",
},
want: usrPass{user: "me", pass: "default-pass"},
},
{
name: "both-json-pointer",
given: args{
s: data{
"first-path": map[string]interface{}{
"deeper-path": map[string]interface{}{
"my-special-user": "you-found-me",
},
},
"testing": map[string]interface{}{
"password": "secret",
},
},
uAttr: "/first-path/deeper-path/my-special-user",
pAttr: "/testing/password",
},
want: usrPass{user: "you-found-me", pass: "secret"},
},
}
for _, tt := range tests {
tt := tt
@@ -16,6 +16,8 @@ func validMappingOverride(m MappingOverride, ct credential.Type) bool {
return true // it is always valid to not specify a mapping override
case *UsernamePasswordOverride:
return ct == credential.UsernamePasswordType
case *SshPrivateKeyOverride:
return ct == credential.SshPrivateKeyType
default:
return false // an unknown mapping override type is never valid
}
@@ -96,3 +98,66 @@ func (o *UsernamePasswordOverride) TableName() string {
func (o *UsernamePasswordOverride) SetTableName(n string) {
o.tableName = n
}
// A SshPrivateKeyOverride contains optional values for overriding the
// default mappings used to map a Vault secret to a SshPrivateKey credential
// type for the credential library that owns it.
type SshPrivateKeyOverride struct {
*store.SshPrivateKeyOverride
tableName string `gorm:"-"`
}
var _ MappingOverride = (*SshPrivateKeyOverride)(nil)
// NewSshPrivateKeyOverride creates a new in memory SshPrivateKeyOverride.
// WithOverrideUsernameAttribute and WithOverridePasswordAttribute are the
// only valid options. All other options are ignored.
func NewSshPrivateKeyOverride(opt ...Option) *SshPrivateKeyOverride {
opts := getOpts(opt...)
o := &SshPrivateKeyOverride{
SshPrivateKeyOverride: &store.SshPrivateKeyOverride{
UsernameAttribute: sanitize.String(opts.withOverrideUsernameAttribute),
PrivateKeyAttribute: sanitize.String(opts.withOverridePrivateKeyAttribute),
},
}
return o
}
func allocSshPrivateKeyOverride() *SshPrivateKeyOverride {
return &SshPrivateKeyOverride{
SshPrivateKeyOverride: &store.SshPrivateKeyOverride{},
}
}
func (o *SshPrivateKeyOverride) clone() MappingOverride {
cp := proto.Clone(o.SshPrivateKeyOverride)
return &SshPrivateKeyOverride{
SshPrivateKeyOverride: cp.(*store.SshPrivateKeyOverride),
}
}
func (o *SshPrivateKeyOverride) setLibraryId(i string) {
o.LibraryId = i
}
func (o *SshPrivateKeyOverride) sanitize() {
if sentinel.Is(o.UsernameAttribute) {
o.UsernameAttribute = ""
}
if sentinel.Is(o.PrivateKeyAttribute) {
o.PrivateKeyAttribute = ""
}
}
// TableName returns the table name.
func (o *SshPrivateKeyOverride) TableName() string {
if o.tableName != "" {
return o.tableName
}
return "credential_vault_library_ssh_private_key_mapping_override"
}
// SetTableName sets the table name.
func (o *SshPrivateKeyOverride) SetTableName(n string) {
o.tableName = n
}
@@ -57,6 +57,16 @@ func TestValidMappingOverrides(t *testing.T) {
ct: credential.UsernamePasswordType,
want: true,
},
{
m: allocSshPrivateKeyOverride(),
ct: credential.UnspecifiedType,
want: false,
},
{
m: allocSshPrivateKeyOverride(),
ct: credential.SshPrivateKeyType,
want: true,
},
}
for _, tt := range tests {
tt := tt
+12 -3
View File
@@ -28,9 +28,10 @@ type options struct {
withRequestBody []byte
withCredentialType credential.Type
withOverrideUsernameAttribute string
withOverridePasswordAttribute string
withMappingOverride MappingOverride
withOverrideUsernameAttribute string
withOverridePasswordAttribute string
withOverridePrivateKeyAttribute string
withMappingOverride MappingOverride
}
func getDefaultOptions() options {
@@ -141,6 +142,14 @@ func WithOverridePasswordAttribute(s string) Option {
}
}
// WithOverridePrivateKeyAttribute provides the name of an attribute in the
// Data field of a Vault api.Secret that maps to a private key value.
func WithOverridePrivateKeyAttribute(s string) Option {
return func(o *options) {
o.withOverridePrivateKeyAttribute = s
}
}
// WithMappingOverride provides an optional mapping override to use for
// mapping the Data fields of a Vault api.Secret to a credential.
func WithMappingOverride(m MappingOverride) Option {
@@ -102,6 +102,12 @@ func Test_GetOpts(t *testing.T) {
testOpts.withOverridePasswordAttribute = "test"
assert.Equal(t, opts, testOpts)
})
t.Run("WithOverridePrivateKeyAttribute", func(t *testing.T) {
opts := getOpts(WithOverridePrivateKeyAttribute("test"))
testOpts := getDefaultOptions()
testOpts.withOverridePrivateKeyAttribute = "test"
assert.Equal(t, opts, testOpts)
})
t.Run("WithMappingOverride", func(t *testing.T) {
opts := getOpts(WithMappingOverride(unknownMapper(1)))
testOpts := getDefaultOptions()
+112 -67
View File
@@ -8,6 +8,7 @@ import (
"time"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/credential/vault/internal/sshprivatekey"
"github.com/hashicorp/boundary/internal/credential/vault/internal/usernamepassword"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/errors"
@@ -18,17 +19,6 @@ import (
"google.golang.org/protobuf/proto"
)
var _ credential.UsernamePassword = (*usrPassCred)(nil)
type usrPassCred struct {
*baseCred
username string
password credential.Password
}
func (c *usrPassCred) Username() string { return c.username }
func (c *usrPassCred) Password() credential.Password { return c.password }
var _ credential.Dynamic = (*baseCred)(nil)
type baseCred struct {
@@ -49,10 +39,23 @@ func convert(ctx context.Context, bc *baseCred) (dynamicCred, error) {
switch bc.Library().CredentialType() {
case credential.UsernamePasswordType:
return baseToUsrPass(ctx, bc)
case credential.SshPrivateKeyType:
return baseToSshPriKey(ctx, bc)
}
return bc, nil
}
var _ credential.UsernamePassword = (*usrPassCred)(nil)
type usrPassCred struct {
*baseCred
username string
password credential.Password
}
func (c *usrPassCred) Username() string { return c.username }
func (c *usrPassCred) Password() credential.Password { return c.password }
func baseToUsrPass(ctx context.Context, bc *baseCred) (*usrPassCred, error) {
switch {
case bc == nil:
@@ -82,73 +85,115 @@ func baseToUsrPass(ctx context.Context, bc *baseCred) (*usrPassCred, error) {
}, nil
}
var _ credential.SshPrivateKey = (*sshPrivateKeyCred)(nil)
type sshPrivateKeyCred struct {
*baseCred
username string
privateKey credential.PrivateKey
}
func (c *sshPrivateKeyCred) Username() string { return c.username }
func (c *sshPrivateKeyCred) PrivateKey() credential.PrivateKey { return c.privateKey }
func baseToSshPriKey(ctx context.Context, bc *baseCred) (*sshPrivateKeyCred, error) {
switch {
case bc == nil:
return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("nil baseCred"))
case bc.lib == nil:
return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("nil baseCred.lib"))
case bc.Library().CredentialType() != credential.SshPrivateKeyType:
return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("invalid credential type"))
}
uAttr, pAttr := bc.lib.UsernameAttribute, bc.lib.PrivateKeyAttribute
if uAttr == "" {
uAttr = "username"
}
if pAttr == "" {
pAttr = "private_key"
}
username, pk := sshprivatekey.Extract(bc.secretData, uAttr, pAttr)
if username == "" || pk == nil {
return nil, errors.E(ctx, errors.WithCode(errors.VaultInvalidCredentialMapping))
}
return &sshPrivateKeyCred{
baseCred: bc,
username: username,
privateKey: pk,
}, nil
}
var _ credential.Library = (*privateLibrary)(nil)
// A privateLibrary contains all the values needed to connect to Vault and
// retrieve credentials.
type privateLibrary struct {
PublicId string `gorm:"primary_key"`
StoreId string
CredType string `gorm:"column:credential_type"`
UsernameAttribute string
PasswordAttribute string
Name string
Description string
CreateTime *timestamp.Timestamp
UpdateTime *timestamp.Timestamp
Version uint32
ScopeId string
VaultPath string
HttpMethod string
HttpRequestBody []byte
VaultAddress string
Namespace string
CaCert []byte
TlsServerName string
TlsSkipVerify bool
TokenHmac []byte
Token TokenSecret
CtToken []byte
TokenKeyId string
ClientCert []byte
ClientKey KeySecret
CtClientKey []byte
ClientKeyId string
Purpose credential.Purpose `gorm:"-"`
PublicId string `gorm:"primary_key"`
StoreId string
CredType string `gorm:"column:credential_type"`
UsernameAttribute string
PasswordAttribute string
PrivateKeyAttribute string
Name string
Description string
CreateTime *timestamp.Timestamp
UpdateTime *timestamp.Timestamp
Version uint32
ScopeId string
VaultPath string
HttpMethod string
HttpRequestBody []byte
VaultAddress string
Namespace string
CaCert []byte
TlsServerName string
TlsSkipVerify bool
TokenHmac []byte
Token TokenSecret
CtToken []byte
TokenKeyId string
ClientCert []byte
ClientKey KeySecret
CtClientKey []byte
ClientKeyId string
Purpose credential.Purpose `gorm:"-"`
}
func (pl *privateLibrary) clone() *privateLibrary {
// The 'append(a[:0:0], a...)' comes from
// https://github.com/go101/go101/wiki/How-to-perfectly-clone-a-slice%3F
return &privateLibrary{
PublicId: pl.PublicId,
StoreId: pl.StoreId,
CredType: pl.CredType,
UsernameAttribute: pl.UsernameAttribute,
PasswordAttribute: pl.PasswordAttribute,
Name: pl.Name,
Description: pl.Description,
CreateTime: proto.Clone(pl.CreateTime).(*timestamp.Timestamp),
UpdateTime: proto.Clone(pl.UpdateTime).(*timestamp.Timestamp),
Version: pl.Version,
ScopeId: pl.ScopeId,
VaultPath: pl.VaultPath,
HttpMethod: pl.HttpMethod,
HttpRequestBody: append(pl.HttpRequestBody[:0:0], pl.HttpRequestBody...),
VaultAddress: pl.VaultAddress,
Namespace: pl.Namespace,
CaCert: append(pl.CaCert[:0:0], pl.CaCert...),
TlsServerName: pl.TlsServerName,
TlsSkipVerify: pl.TlsSkipVerify,
TokenHmac: append(pl.TokenHmac[:0:0], pl.TokenHmac...),
Token: append(pl.Token[:0:0], pl.Token...),
CtToken: append(pl.CtToken[:0:0], pl.CtToken...),
TokenKeyId: pl.TokenKeyId,
ClientCert: append(pl.ClientCert[:0:0], pl.ClientCert...),
ClientKey: append(pl.ClientKey[:0:0], pl.ClientKey...),
CtClientKey: append(pl.CtClientKey[:0:0], pl.CtClientKey...),
ClientKeyId: pl.ClientKeyId,
Purpose: pl.Purpose,
PublicId: pl.PublicId,
StoreId: pl.StoreId,
CredType: pl.CredType,
UsernameAttribute: pl.UsernameAttribute,
PasswordAttribute: pl.PasswordAttribute,
PrivateKeyAttribute: pl.PrivateKeyAttribute,
Name: pl.Name,
Description: pl.Description,
CreateTime: proto.Clone(pl.CreateTime).(*timestamp.Timestamp),
UpdateTime: proto.Clone(pl.UpdateTime).(*timestamp.Timestamp),
Version: pl.Version,
ScopeId: pl.ScopeId,
VaultPath: pl.VaultPath,
HttpMethod: pl.HttpMethod,
HttpRequestBody: append(pl.HttpRequestBody[:0:0], pl.HttpRequestBody...),
VaultAddress: pl.VaultAddress,
Namespace: pl.Namespace,
CaCert: append(pl.CaCert[:0:0], pl.CaCert...),
TlsServerName: pl.TlsServerName,
TlsSkipVerify: pl.TlsSkipVerify,
TokenHmac: append(pl.TokenHmac[:0:0], pl.TokenHmac...),
Token: append(pl.Token[:0:0], pl.Token...),
CtToken: append(pl.CtToken[:0:0], pl.CtToken...),
TokenKeyId: pl.TokenKeyId,
ClientCert: append(pl.ClientCert[:0:0], pl.ClientCert...),
ClientKey: append(pl.ClientKey[:0:0], pl.ClientKey...),
CtClientKey: append(pl.CtClientKey[:0:0], pl.CtClientKey...),
ClientKeyId: pl.ClientKeyId,
Purpose: pl.Purpose,
}
}
@@ -179,6 +179,72 @@ func TestRepository_getPrivateLibraries(t *testing.T) {
req := credential.Request{SourceId: lib.GetPublicId(), Purpose: credential.ApplicationPurpose}
requests = append(requests, req)
}
{
opts := []Option{
WithCredentialType(credential.SshPrivateKeyType),
}
libIn, err := NewCredentialLibrary(origStore.GetPublicId(), "/vault/path", opts...)
assert.NoError(err)
require.NotNil(libIn)
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
assert.NoError(err)
require.NotNil(lib)
libs[lib.GetPublicId()] = lib
req := credential.Request{SourceId: lib.GetPublicId(), Purpose: credential.ApplicationPurpose}
requests = append(requests, req)
}
{
opts := []Option{
WithCredentialType(credential.SshPrivateKeyType),
WithMappingOverride(NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("test-username"),
)),
}
libIn, err := NewCredentialLibrary(origStore.GetPublicId(), "/vault/path", opts...)
assert.NoError(err)
require.NotNil(libIn)
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
assert.NoError(err)
require.NotNil(lib)
libs[lib.GetPublicId()] = lib
req := credential.Request{SourceId: lib.GetPublicId(), Purpose: credential.ApplicationPurpose}
requests = append(requests, req)
}
{
opts := []Option{
WithCredentialType(credential.SshPrivateKeyType),
WithMappingOverride(NewSshPrivateKeyOverride(
WithOverridePrivateKeyAttribute("test-private-key"),
)),
}
libIn, err := NewCredentialLibrary(origStore.GetPublicId(), "/vault/path", opts...)
assert.NoError(err)
require.NotNil(libIn)
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
assert.NoError(err)
require.NotNil(lib)
libs[lib.GetPublicId()] = lib
req := credential.Request{SourceId: lib.GetPublicId(), Purpose: credential.ApplicationPurpose}
requests = append(requests, req)
}
{
opts := []Option{
WithCredentialType(credential.SshPrivateKeyType),
WithMappingOverride(NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("test-username"),
WithOverridePrivateKeyAttribute("test-private-key"),
)),
}
libIn, err := NewCredentialLibrary(origStore.GetPublicId(), "/vault/path", opts...)
assert.NoError(err)
require.NotNil(libIn)
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
assert.NoError(err)
require.NotNil(lib)
libs[lib.GetPublicId()] = lib
req := credential.Request{SourceId: lib.GetPublicId(), Purpose: credential.ApplicationPurpose}
requests = append(requests, req)
}
gotLibs, err := repo.getPrivateLibraries(ctx, requests)
assert.NoError(err)
@@ -204,6 +270,9 @@ func TestRepository_getPrivateLibraries(t *testing.T) {
case *UsernamePasswordOverride:
assert.Equal(w.UsernameAttribute, got.UsernameAttribute)
assert.Equal(w.PasswordAttribute, got.PasswordAttribute)
case *SshPrivateKeyOverride:
assert.Equal(w.UsernameAttribute, got.UsernameAttribute)
assert.Equal(w.PrivateKeyAttribute, got.PrivateKeyAttribute)
default:
assert.Fail("unknown mapping override type")
}
@@ -669,3 +738,368 @@ func TestBaseToUsrPass(t *testing.T) {
})
}
}
func TestBaseToSshPriKey(t *testing.T) {
t.Parallel()
tests := []struct {
name string
given *baseCred
want *sshPrivateKeyCred
wantErr errors.Code
}{
{
name: "nil-input",
wantErr: errors.InvalidParameter,
},
{
name: "nil-library",
given: &baseCred{},
wantErr: errors.InvalidParameter,
},
{
name: "library-not-ssh-private-key-type",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UnspecifiedType),
},
},
wantErr: errors.InvalidParameter,
},
{
name: "invalid-no-username-default-pk-attribute",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
},
secretData: map[string]interface{}{
"private_key": "my-pk",
},
},
wantErr: errors.VaultInvalidCredentialMapping,
},
{
name: "invalid-no-pk-default-username-attribute",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
},
secretData: map[string]interface{}{
"username": "my-username",
},
},
wantErr: errors.VaultInvalidCredentialMapping,
},
{
name: "valid-default-attributes",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
},
secretData: map[string]interface{}{
"username": "my-username",
"private_key": "my-pk",
},
},
want: &sshPrivateKeyCred{
username: "my-username",
privateKey: credential.PrivateKey("my-pk"),
},
},
{
name: "valid-override-attributes",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
UsernameAttribute: "test-username",
PrivateKeyAttribute: "test-pk",
},
secretData: map[string]interface{}{
"username": "default-username",
"private_key": "default-pk",
"test-username": "override-username",
"test-pk": "override-pk",
},
},
want: &sshPrivateKeyCred{
username: "override-username",
privateKey: credential.PrivateKey("override-pk"),
},
},
{
name: "valid-default-username-override-pk",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
PrivateKeyAttribute: "test-pk",
},
secretData: map[string]interface{}{
"username": "default-username",
"private_key": "default-pk",
"test-username": "override-username",
"test-pk": "override-pk",
},
},
want: &sshPrivateKeyCred{
username: "default-username",
privateKey: credential.PrivateKey("override-pk"),
},
},
{
name: "valid-override-username-default-pk",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
UsernameAttribute: "test-username",
},
secretData: map[string]interface{}{
"username": "default-username",
"private_key": "default-pk",
"test-username": "override-username",
"test-pk": "override-pk",
},
},
want: &sshPrivateKeyCred{
username: "override-username",
privateKey: credential.PrivateKey("default-pk"),
},
},
{
name: "invalid-username-override",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
UsernameAttribute: "missing-username",
},
secretData: map[string]interface{}{
"username": "default-username",
"private_key": "default-pk",
"test-username": "override-username",
"test-pk": "override-pk",
},
},
wantErr: errors.VaultInvalidCredentialMapping,
},
{
name: "invalid-pk-override",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
UsernameAttribute: "missing-pk",
},
secretData: map[string]interface{}{
"username": "default-username",
"private_key": "default-pk",
"test-username": "override-username",
"test-pk": "override-pk",
},
},
wantErr: errors.VaultInvalidCredentialMapping,
},
{
name: "invalid-kv2-no-metadata-field",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
},
secretData: map[string]interface{}{
"data": map[string]interface{}{
"username": "default-username",
"private_key": "default-pk",
},
},
},
wantErr: errors.VaultInvalidCredentialMapping,
},
{
name: "invalid-kv2-no-data-field",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
},
secretData: map[string]interface{}{
"metadata": map[string]interface{}{},
},
},
wantErr: errors.VaultInvalidCredentialMapping,
},
{
name: "invalid-kv2-no-username-default-pk-attribute",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
},
secretData: map[string]interface{}{
"metadata": map[string]interface{}{},
"data": map[string]interface{}{
"private_key": "default-pk",
},
},
},
wantErr: errors.VaultInvalidCredentialMapping,
},
{
name: "invalid-kv2-no-pk-default-username-attribute",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
},
secretData: map[string]interface{}{
"metadata": map[string]interface{}{},
"data": map[string]interface{}{
"username": "default-username",
},
},
},
wantErr: errors.VaultInvalidCredentialMapping,
},
{
name: "invalid-kv2-invalid-metadata-type",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
},
secretData: map[string]interface{}{
"metadata": "hello",
"data": map[string]interface{}{
"username": "default-username",
"private_key": "default-pk",
},
},
},
wantErr: errors.VaultInvalidCredentialMapping,
},
{
name: "invalid-kv2-invalid-data-type",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
},
secretData: map[string]interface{}{
"metadata": map[string]interface{}{},
"data": "hello",
},
},
wantErr: errors.VaultInvalidCredentialMapping,
},
{
name: "invalid-kv2-additional-field",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
},
secretData: map[string]interface{}{
"bad-field": "hello",
"metadata": map[string]interface{}{},
"data": map[string]interface{}{
"username": "default-username",
"private_key": "default-pk",
},
},
},
wantErr: errors.VaultInvalidCredentialMapping,
},
{
name: "valid-kv2-default-attributes",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
},
secretData: map[string]interface{}{
"metadata": map[string]interface{}{},
"data": map[string]interface{}{
"username": "default-username",
"private_key": "default-pk",
},
},
},
want: &sshPrivateKeyCred{
username: "default-username",
privateKey: credential.PrivateKey("default-pk"),
},
},
{
name: "valid-kv2-override-attributes",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
UsernameAttribute: "test-username",
PrivateKeyAttribute: "test-pk",
},
secretData: map[string]interface{}{
"metadata": map[string]interface{}{},
"data": map[string]interface{}{
"username": "default-username",
"private_key": "default-pk",
"test-username": "override-username",
"test-pk": "override-pk",
},
},
},
want: &sshPrivateKeyCred{
username: "override-username",
privateKey: credential.PrivateKey("override-pk"),
},
},
{
name: "valid-kv2-default-username-override-pk",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
PrivateKeyAttribute: "test-pk",
},
secretData: map[string]interface{}{
"metadata": map[string]interface{}{},
"data": map[string]interface{}{
"username": "default-username",
"private_key": "default-pk",
"test-username": "override-username",
"test-pk": "override-pk",
},
},
},
want: &sshPrivateKeyCred{
username: "default-username",
privateKey: credential.PrivateKey("override-pk"),
},
},
{
name: "valid-kv2-override-username-default-pk",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.SshPrivateKeyType),
UsernameAttribute: "test-username",
},
secretData: map[string]interface{}{
"metadata": map[string]interface{}{},
"data": map[string]interface{}{
"username": "default-username",
"private_key": "default-pk",
"test-username": "override-username",
"test-pk": "override-pk",
},
},
},
want: &sshPrivateKeyCred{
username: "override-username",
privateKey: credential.PrivateKey("default-pk"),
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, err := baseToSshPriKey(context.Background(), tt.given)
if tt.wantErr != 0 {
assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err)
assert.Nil(got)
return
}
require.NoError(err)
want := tt.want
want.baseCred = tt.given
assert.Equal(want, got)
})
}
}
@@ -5,6 +5,7 @@ import (
"fmt"
"strings"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/db"
dbcommon "github.com/hashicorp/boundary/internal/db/common"
"github.com/hashicorp/boundary/internal/db/timestamp"
@@ -344,19 +345,20 @@ func (r *Repository) LookupCredentialLibrary(ctx context.Context, publicId strin
// mapping overrides. It does not include encrypted data and is safe to
// return external to boundary.
type publicLibrary struct {
PublicId string `gorm:"primary_key"`
StoreId string
Name string
Description string
CreateTime *timestamp.Timestamp
UpdateTime *timestamp.Timestamp
Version uint32
VaultPath string
HttpMethod string
HttpRequestBody []byte
CredentialType string
UsernameAttribute string
PasswordAttribute string
PublicId string `gorm:"primary_key"`
StoreId string
Name string
Description string
CreateTime *timestamp.Timestamp
UpdateTime *timestamp.Timestamp
Version uint32
VaultPath string
HttpMethod string
HttpRequestBody []byte
CredentialType string
UsernameAttribute string
PasswordAttribute string
PrivateKeyAttribute string
}
func allocPublicLibrary() *publicLibrary {
@@ -377,13 +379,25 @@ func (pl *publicLibrary) toCredentialLibrary() *CredentialLibrary {
cl.HttpRequestBody = pl.HttpRequestBody
cl.CredentialLibrary.CredentialType = pl.CredentialType
if pl.UsernameAttribute != "" || pl.PasswordAttribute != "" {
up := allocUsernamePasswordOverride()
up.LibraryId = pl.PublicId
up.UsernameAttribute = pl.UsernameAttribute
up.PasswordAttribute = pl.PasswordAttribute
up.sanitize()
cl.MappingOverride = up
switch pl.CredentialType {
case string(credential.UsernamePasswordType):
if pl.UsernameAttribute != "" || pl.PasswordAttribute != "" {
up := allocUsernamePasswordOverride()
up.LibraryId = pl.PublicId
up.UsernameAttribute = pl.UsernameAttribute
up.PasswordAttribute = pl.PasswordAttribute
up.sanitize()
cl.MappingOverride = up
}
case string(credential.SshPrivateKeyType):
if pl.UsernameAttribute != "" || pl.PrivateKeyAttribute != "" {
pk := allocSshPrivateKeyOverride()
pk.LibraryId = pl.PublicId
pk.UsernameAttribute = pl.UsernameAttribute
pk.PrivateKeyAttribute = pl.PrivateKeyAttribute
pk.sanitize()
cl.MappingOverride = pk
}
}
return cl
}
@@ -285,6 +285,128 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
},
},
},
{
name: "valid-ssh-private-key-credential-type",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
},
{
name: "unknown-ssh-private-key-mapping-override-type",
in: &CredentialLibrary{
MappingOverride: unknownMapper(1),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
wantErr: errors.VaultInvalidMappingOverride,
},
{
name: "invalid-ssh-private-key-mapping-override-type",
in: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(WithOverrideUsernameAttribute("test")),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
wantErr: errors.VaultInvalidMappingOverride,
},
{
name: "valid-ssh-prviate-key-credential-type-with-username-override",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("utest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("utest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
},
{
name: "valid-ssh-private-key-credential-type-with-private-key-override",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
},
{
name: "valid-ssh-private-keyt-credential-type-with-username-and-private-key-override",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("utest"),
WithOverridePrivateKeyAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("utest"),
WithOverridePrivateKeyAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
},
}
for _, tt := range tests {
@@ -324,13 +446,24 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
require.True(ok)
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
// verify it was persisted in the database
override := allocUsernamePasswordOverride()
assert.NoError(rw.LookupWhere(ctx, &override, "library_id = ?", []interface{}{got.GetPublicId()}))
case *SshPrivateKeyOverride:
g, ok := got.MappingOverride.(*SshPrivateKeyOverride)
require.True(ok)
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
assert.Equal(w.PrivateKeyAttribute, g.PrivateKeyAttribute)
// verify it was persisted in the database
override := allocSshPrivateKeyOverride()
assert.NoError(rw.LookupWhere(ctx, &override, "library_id = ?", []interface{}{got.GetPublicId()}))
default:
assert.Fail("Unknown mapping override")
}
// verify it was persisted in the database
override := allocUsernamePasswordOverride()
assert.NoError(rw.LookupWhere(ctx, &override, "library_id = ?", []interface{}{got.GetPublicId()}))
}
})
}
@@ -1104,6 +1237,162 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
masks: []string{"MappingOverride"},
wantErr: errors.VaultInvalidMappingOverride,
},
{
name: "ssh-private-key-attributes-change-username-attribute",
orig: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("orig-username"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(credential.SshPrivateKeyType),
},
},
chgFn: changeMappingOverride(
NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("changed-username"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("changed-username"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(credential.SshPrivateKeyType),
},
},
wantCount: 1,
},
{
name: "ssh-private-key-attributes-change-private-key-attribute",
orig: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyAttribute("orig-private-key"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(credential.SshPrivateKeyType),
},
},
chgFn: changeMappingOverride(
NewSshPrivateKeyOverride(
WithOverridePrivateKeyAttribute("changed-private-key"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyAttribute("changed-private-key"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(credential.SshPrivateKeyType),
},
},
wantCount: 1,
},
{
name: "ssh-private-key-attributes-change-username-and-private-key-attributes",
orig: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("orig-username"),
WithOverridePrivateKeyAttribute("orig-private-key"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(credential.SshPrivateKeyType),
},
},
chgFn: changeMappingOverride(
NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("changed-username"),
WithOverridePrivateKeyAttribute("changed-private-key"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("changed-username"),
WithOverridePrivateKeyAttribute("changed-private-key"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(credential.SshPrivateKeyType),
},
},
wantCount: 1,
},
{
name: "no-mapping-override-change-username-and-private-key-attributes",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(credential.SshPrivateKeyType),
},
},
chgFn: changeMappingOverride(
NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("changed-username"),
WithOverridePrivateKeyAttribute("changed-private-key"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("changed-username"),
WithOverridePrivateKeyAttribute("changed-private-key"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(credential.SshPrivateKeyType),
},
},
wantCount: 1,
},
{
name: "ssh-private-key-attributes-delete-mapping-override",
orig: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("orig-username"),
WithOverridePrivateKeyAttribute("orig-private-key"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(credential.SshPrivateKeyType),
},
},
chgFn: changeMappingOverride(nil),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(credential.SshPrivateKeyType),
},
},
wantCount: 1,
},
}
for _, tt := range tests {
@@ -1172,6 +1461,11 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
require.True(ok)
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
case *SshPrivateKeyOverride:
g, ok := got.MappingOverride.(*SshPrivateKeyOverride)
require.True(ok)
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
assert.Equal(w.PrivateKeyAttribute, g.PrivateKeyAttribute)
default:
assert.Fail("Unknown mapping override")
}
@@ -1371,6 +1665,60 @@ func TestRepository_LookupCredentialLibrary(t *testing.T) {
},
},
},
{
name: "valid-ssh-private-key-credential-type",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
},
{
name: "valid-ssh-private-key-credential-type-with-username-override",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("utest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
},
{
name: "valid-ssh-private-key-credential-type-with-private-key-override",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
},
{
name: "valid-ssh-private-key-credential-type-with-username-and-private-key-override",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("utest"),
WithOverridePrivateKeyAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.SshPrivateKeyType),
},
},
},
}
for _, tt := range tests {
@@ -1403,6 +1751,11 @@ func TestRepository_LookupCredentialLibrary(t *testing.T) {
require.True(ok)
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
case *SshPrivateKeyOverride:
g, ok := got.MappingOverride.(*SshPrivateKeyOverride)
require.True(ok)
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
assert.Equal(w.PrivateKeyAttribute, g.PrivateKeyAttribute)
default:
assert.Fail("Unknown mapping override")
}
@@ -46,7 +46,10 @@ func TestRepository_IssueCredentials(t *testing.T) {
_, token := v.CreateToken(t, vault.WithPolicies([]string{"default", "boundary-controller", "database", "pki", "secret"}))
// Create valid username password KV secret
v.CreateKVSecret(t, "my-secret", []byte(`{"data":{"username":"user","password":"pass"}}`))
v.CreateKVSecret(t, "my-up-secret", []byte(`{"data":{"username":"user","password":"pass"}}`))
// Create valid ssh private key KV secret
v.CreateKVSecret(t, "my-sshpk-secret", []byte(`{"data":{"username":"user","private_key":"pk data"}}`))
var opts []vault.Option
opts = append(opts, vault.WithCACert(v.CaCert))
@@ -71,6 +74,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
libKV
libErrKV
libUsrPassKV
libSshPkKV
)
libs := make(map[libT]string)
@@ -106,7 +110,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
libs[libErrPKI] = lib.GetPublicId()
}
{
libPath := path.Join("secret", "data", "my-secret")
libPath := path.Join("secret", "data", "my-up-secret")
libIn, err := vault.NewCredentialLibrary(origStore.GetPublicId(), libPath, opts...)
assert.NoError(t, err)
require.NotNil(t, libIn)
@@ -156,7 +160,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
libs[libErrUsrPassDB] = lib.GetPublicId()
}
{
libPath := path.Join("secret", "data", "my-secret")
libPath := path.Join("secret", "data", "my-up-secret")
opts := []vault.Option{
vault.WithCredentialType(credential.UsernamePasswordType),
}
@@ -168,6 +172,19 @@ func TestRepository_IssueCredentials(t *testing.T) {
require.NotNil(t, lib)
libs[libUsrPassKV] = lib.GetPublicId()
}
{
libPath := path.Join("secret", "data", "my-sshpk-secret")
opts := []vault.Option{
vault.WithCredentialType(credential.SshPrivateKeyType),
}
libIn, err := vault.NewCredentialLibrary(origStore.GetPublicId(), libPath, opts...)
assert.NoError(t, err)
require.NotNil(t, libIn)
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
assert.NoError(t, err)
require.NotNil(t, lib)
libs[libSshPkKV] = lib.GetPublicId()
}
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
uId := at.GetIamUserId()
@@ -300,6 +317,16 @@ func TestRepository_IssueCredentials(t *testing.T) {
},
},
},
{
name: "valid-kv-ssh-private-key-library",
convertFn: rc2dc,
requests: []credential.Request{
{
SourceId: libs[libSshPkKV],
Purpose: credential.ApplicationPurpose,
},
},
},
}
for _, tt := range tests {
tt := tt
+125 -22
View File
@@ -868,6 +868,87 @@ func (x *UsernamePasswordOverride) GetPasswordAttribute() string {
return ""
}
type SshPrivateKeyOverride struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// library_id of the owning vault credential library.
// @inject_tag: `gorm:"primary_key"`
LibraryId string `protobuf:"bytes,1,opt,name=library_id,json=libraryId,proto3" json:"library_id,omitempty" gorm:"primary_key"`
// username_attribute is the name of the attribute in the Data field of a
// Vault api.Secret that maps to a username.
// If set, it overrides any default attribute names the system uses to
// find a username attribute.
//
// See https://github.com/hashicorp/vault/blob/5e505ec039177e8212cbbab74ccb644c46e62e63/api/secret.go#L25
//
// @inject_tag: `gorm:"default:null"`
UsernameAttribute string `protobuf:"bytes,2,opt,name=username_attribute,json=usernameAttribute,proto3" json:"username_attribute,omitempty" gorm:"default:null"`
// private_key_attribute is the name of the attribute in the Data field of a
// Vault api.Secret that maps to a private key.
// If set, it overrides any default attribute names the system uses to
// find a private_key attribute.
//
// See https://github.com/hashicorp/vault/blob/5e505ec039177e8212cbbab74ccb644c46e62e63/api/secret.go#L25
//
// @inject_tag: `gorm:"default:null"`
PrivateKeyAttribute string `protobuf:"bytes,3,opt,name=private_key_attribute,json=privateKeyAttribute,proto3" json:"private_key_attribute,omitempty" gorm:"default:null"`
}
func (x *SshPrivateKeyOverride) Reset() {
*x = SshPrivateKeyOverride{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SshPrivateKeyOverride) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SshPrivateKeyOverride) ProtoMessage() {}
func (x *SshPrivateKeyOverride) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[6]
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 SshPrivateKeyOverride.ProtoReflect.Descriptor instead.
func (*SshPrivateKeyOverride) Descriptor() ([]byte, []int) {
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{6}
}
func (x *SshPrivateKeyOverride) GetLibraryId() string {
if x != nil {
return x.LibraryId
}
return ""
}
func (x *SshPrivateKeyOverride) GetUsernameAttribute() string {
if x != nil {
return x.UsernameAttribute
}
return ""
}
func (x *SshPrivateKeyOverride) GetPrivateKeyAttribute() string {
if x != nil {
return x.PrivateKeyAttribute
}
return ""
}
var File_controller_storage_credential_vault_store_v1_vault_proto protoreflect.FileDescriptor
var file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc = []byte{
@@ -1075,12 +1156,21 @@ var file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc = []by
0x75, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f,
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x11, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
0x74, 0x65, 0x42, 0x45, 0x5a, 0x43, 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, 0x63, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x74,
0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
0x74, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x15, 0x53, 0x73, 0x68, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74,
0x65, 0x4b, 0x65, 0x79, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a,
0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x75,
0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d,
0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72,
0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62,
0x75, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x69, 0x76, 0x61,
0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x42, 0x45,
0x5a, 0x43, 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, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b,
0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -1095,7 +1185,7 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP()
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescData
}
var file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_controller_storage_credential_vault_store_v1_vault_proto_goTypes = []interface{}{
(*CredentialStore)(nil), // 0: controller.storage.credential.vault.store.v1.CredentialStore
(*Token)(nil), // 1: controller.storage.credential.vault.store.v1.Token
@@ -1103,22 +1193,23 @@ var file_controller_storage_credential_vault_store_v1_vault_proto_goTypes = []in
(*CredentialLibrary)(nil), // 3: controller.storage.credential.vault.store.v1.CredentialLibrary
(*Credential)(nil), // 4: controller.storage.credential.vault.store.v1.Credential
(*UsernamePasswordOverride)(nil), // 5: controller.storage.credential.vault.store.v1.UsernamePasswordOverride
(*timestamp.Timestamp)(nil), // 6: controller.storage.timestamp.v1.Timestamp
(*SshPrivateKeyOverride)(nil), // 6: controller.storage.credential.vault.store.v1.SshPrivateKeyOverride
(*timestamp.Timestamp)(nil), // 7: controller.storage.timestamp.v1.Timestamp
}
var file_controller_storage_credential_vault_store_v1_vault_proto_depIdxs = []int32{
6, // 0: controller.storage.credential.vault.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 1: controller.storage.credential.vault.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 2: controller.storage.credential.vault.store.v1.CredentialStore.delete_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 3: controller.storage.credential.vault.store.v1.Token.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 4: controller.storage.credential.vault.store.v1.Token.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 5: controller.storage.credential.vault.store.v1.Token.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 6: controller.storage.credential.vault.store.v1.Token.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 7: controller.storage.credential.vault.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 8: controller.storage.credential.vault.store.v1.CredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 9: controller.storage.credential.vault.store.v1.Credential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 10: controller.storage.credential.vault.store.v1.Credential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 11: controller.storage.credential.vault.store.v1.Credential.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 12: controller.storage.credential.vault.store.v1.Credential.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 0: controller.storage.credential.vault.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 1: controller.storage.credential.vault.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 2: controller.storage.credential.vault.store.v1.CredentialStore.delete_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 3: controller.storage.credential.vault.store.v1.Token.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 4: controller.storage.credential.vault.store.v1.Token.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 5: controller.storage.credential.vault.store.v1.Token.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 6: controller.storage.credential.vault.store.v1.Token.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 7: controller.storage.credential.vault.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 8: controller.storage.credential.vault.store.v1.CredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 9: controller.storage.credential.vault.store.v1.Credential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 10: controller.storage.credential.vault.store.v1.Credential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 11: controller.storage.credential.vault.store.v1.Credential.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 12: controller.storage.credential.vault.store.v1.Credential.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
13, // [13:13] is the sub-list for method output_type
13, // [13:13] is the sub-list for method input_type
13, // [13:13] is the sub-list for extension type_name
@@ -1204,6 +1295,18 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() {
return nil
}
}
file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SshPrivateKeyOverride); 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{
@@ -1211,7 +1314,7 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc,
NumEnums: 0,
NumMessages: 6,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
@@ -38,8 +38,9 @@ const (
// Credential mapping override attributes
const (
usernameAttribute string = "username_attribute"
passwordAttribute string = "password_attribute"
usernameAttribute string = "username_attribute"
passwordAttribute string = "password_attribute"
privateKeyAttribute string = "private_key_attribute"
)
var (
@@ -531,6 +532,14 @@ func toProto(in credential.Library, opt ...handlers.Option) (*pb.CredentialLibra
if mapping.PasswordAttribute != "" {
m[passwordAttribute] = mapping.PasswordAttribute
}
case *vault.SshPrivateKeyOverride:
if mapping.UsernameAttribute != "" {
m[usernameAttribute] = mapping.UsernameAttribute
}
if mapping.PrivateKeyAttribute != "" {
m[privateKeyAttribute] = mapping.PrivateKeyAttribute
}
}
if len(m) > 0 {
mp, err := structpb.NewStruct(m)
@@ -592,6 +601,20 @@ func toStorageVaultLibrary(storeId string, in *pb.CredentialLibrary) (out *vault
if len(mapOpts) > 0 {
opts = append(opts, vault.WithMappingOverride(vault.NewUsernamePasswordOverride(mapOpts...)))
}
case credential.SshPrivateKeyType:
opts = append(opts, vault.WithCredentialType(credentialType))
overrides := in.CredentialMappingOverrides.AsMap()
var mapOpts []vault.Option
if username := overrides[usernameAttribute]; username != nil {
mapOpts = append(mapOpts, vault.WithOverrideUsernameAttribute(username.(string)))
}
if pk := overrides[privateKeyAttribute]; pk != nil {
mapOpts = append(mapOpts, vault.WithOverridePrivateKeyAttribute(pk.(string)))
}
if len(mapOpts) > 0 {
opts = append(opts, vault.WithMappingOverride(vault.NewSshPrivateKeyOverride(mapOpts...)))
}
}
cs, err := vault.NewCredentialLibrary(storeId, attrs.GetPath().GetValue(), opts...)
@@ -697,6 +720,9 @@ func validateMapping(badFields map[string]string, credentialType credential.Type
case credential.UsernamePasswordType:
validFields[usernameAttribute] = true
validFields[passwordAttribute] = true
case credential.SshPrivateKeyType:
validFields[usernameAttribute] = true
validFields[privateKeyAttribute] = true
default:
badFields[globals.CredentialTypeField] = fmt.Sprintf("Unknown credential type %q", credentialType)
return
@@ -754,6 +780,27 @@ func getMappingUpdates(credentialType credential.Type, current vault.MappingOver
default:
ret[passwordAttribute] = currentPass
}
case credential.SshPrivateKeyType:
var currentUser, currentPk interface{}
if overrides, ok := current.(*vault.SshPrivateKeyOverride); ok {
currentUser = overrides.UsernameAttribute
currentPk = overrides.PrivateKeyAttribute
}
switch {
case masks[usernameAttribute]:
ret[usernameAttribute] = new[usernameAttribute]
default:
ret[usernameAttribute] = currentUser
}
switch {
case masks[privateKeyAttribute]:
ret[privateKeyAttribute] = new[privateKeyAttribute]
default:
ret[privateKeyAttribute] = currentPk
}
}
return ret, true
@@ -606,6 +606,7 @@ func TestGet(t *testing.T) {
repo, err := repoFn()
require.NoError(t, err)
lib, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path",
vault.WithCredentialType("username_password"),
vault.WithMappingOverride(
@@ -617,6 +618,17 @@ func TestGet(t *testing.T) {
userPassLib, err := repo.CreateCredentialLibrary(context.Background(), prj.GetPublicId(), lib)
require.NoError(t, err)
lib1, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path/ssh",
vault.WithCredentialType("ssh_private_key"),
vault.WithMappingOverride(
vault.NewSshPrivateKeyOverride(
vault.WithOverrideUsernameAttribute("user"),
vault.WithOverridePrivateKeyAttribute("pk"),
)))
require.NoError(t, err)
sshPkLib, err := repo.CreateCredentialLibrary(context.Background(), prj.GetPublicId(), lib1)
require.NoError(t, err)
cases := []struct {
name string
id string
@@ -677,6 +689,38 @@ func TestGet(t *testing.T) {
},
},
},
{
name: "success-ssh-private-key",
id: sshPkLib.GetPublicId(),
res: &pbs.GetCredentialLibraryResponse{
Item: &pb.CredentialLibrary{
Id: sshPkLib.GetPublicId(),
CredentialStoreId: sshPkLib.GetStoreId(),
Scope: &scopepb.ScopeInfo{Id: store.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
Type: vault.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
CreatedTime: sshPkLib.CreateTime.GetTimestamp(),
UpdatedTime: sshPkLib.UpdateTime.GetTimestamp(),
Version: 1,
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
VaultCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
Path: wrapperspb.String(sshPkLib.GetVaultPath()),
HttpMethod: wrapperspb.String(sshPkLib.GetHttpMethod()),
},
},
CredentialType: "ssh_private_key",
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
usernameAttribute: "user",
privateKeyAttribute: "pk",
}
ret, err := structpb.NewStruct(v)
require.NoError(t, err)
return ret
}(),
},
},
},
{
name: "not found error",
id: fmt.Sprintf("%s_1234567890", vault.CredentialLibraryPrefix),
@@ -819,6 +863,7 @@ func TestUpdate(t *testing.T) {
usernameAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, usernameAttribute)
passwordAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, passwordAttribute)
privateKeyAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, privateKeyAttribute)
successCases := []struct {
name string
@@ -1114,6 +1159,220 @@ func TestUpdate(t *testing.T) {
return out
},
},
{
name: "ssh-private-key-attributes-change-username-attribute",
opts: []vault.Option{
vault.WithCredentialType("ssh_private_key"),
vault.WithMappingOverride(
vault.NewSshPrivateKeyOverride(
vault.WithOverrideUsernameAttribute("orig-user"),
vault.WithOverridePrivateKeyAttribute("orig-pk"),
)),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(usernameAttrField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
usernameAttribute: "changed-user",
}
ret, err := structpb.NewStruct(v)
require.NoError(t, err)
return ret
}(),
},
},
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
out := proto.Clone(in).(*pb.CredentialLibrary)
out.CredentialMappingOverrides.Fields[usernameAttribute] = structpb.NewStringValue("changed-user")
return out
},
},
{
name: "ssh-private-key-attributes-change-private-key-attribute",
opts: []vault.Option{
vault.WithCredentialType("ssh_private_key"),
vault.WithMappingOverride(
vault.NewSshPrivateKeyOverride(
vault.WithOverrideUsernameAttribute("orig-user"),
vault.WithOverridePrivateKeyAttribute("orig-pk"),
)),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(privateKeyAttrField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
privateKeyAttribute: "changed-pk",
}
ret, err := structpb.NewStruct(v)
require.NoError(t, err)
return ret
}(),
},
},
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
out := proto.Clone(in).(*pb.CredentialLibrary)
out.CredentialMappingOverrides.Fields[privateKeyAttribute] = structpb.NewStringValue("changed-pk")
return out
},
},
{
name: "ssh-private-key-attributes-change-username-and-private-key-attributes",
opts: []vault.Option{
vault.WithCredentialType("ssh_private_key"),
vault.WithMappingOverride(
vault.NewSshPrivateKeyOverride(
vault.WithOverrideUsernameAttribute("orig-user"),
vault.WithOverridePrivateKeyAttribute("orig-pk"),
)),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
usernameAttribute: "changed-user",
privateKeyAttribute: "changed-pk",
}
ret, err := structpb.NewStruct(v)
require.NoError(t, err)
return ret
}(),
},
},
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
out := proto.Clone(in).(*pb.CredentialLibrary)
out.CredentialMappingOverrides.Fields[usernameAttribute] = structpb.NewStringValue("changed-user")
out.CredentialMappingOverrides.Fields[privateKeyAttribute] = structpb.NewStringValue("changed-pk")
return out
},
},
{
name: "no-mapping-override-change-username-and-private-key-attributes",
opts: []vault.Option{
vault.WithCredentialType("ssh_private_key"),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
usernameAttribute: "new-user",
privateKeyAttribute: "new-pk",
}
ret, err := structpb.NewStruct(v)
require.NoError(t, err)
return ret
}(),
},
},
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
out := proto.Clone(in).(*pb.CredentialLibrary)
v := map[string]interface{}{
usernameAttribute: "new-user",
privateKeyAttribute: "new-pk",
}
var err error
out.CredentialMappingOverrides, err = structpb.NewStruct(v)
require.NoError(t, err)
return out
},
},
{
name: "ssh-private-key-attributes-delete-mapping-override",
opts: []vault.Option{
vault.WithCredentialType("ssh_private_key"),
vault.WithMappingOverride(
vault.NewSshPrivateKeyOverride(
vault.WithOverrideUsernameAttribute("orig-user"),
vault.WithOverridePrivateKeyAttribute("orig-pk"),
)),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(credentialMappingPathField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: nil,
},
},
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
out := proto.Clone(in).(*pb.CredentialLibrary)
out.CredentialMappingOverrides = nil
return out
},
},
{
name: "ssh-private-key-no-mapping-override-delete-mapping-override",
opts: []vault.Option{
vault.WithCredentialType("ssh_private_key"),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(credentialMappingPathField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: nil,
},
},
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
out := proto.Clone(in).(*pb.CredentialLibrary)
out.CredentialMappingOverrides = nil
return out
},
},
{
name: "ssh-private-key-attributes-delete-mapping-override-field-specific",
opts: []vault.Option{
vault.WithCredentialType("ssh_private_key"),
vault.WithMappingOverride(
vault.NewSshPrivateKeyOverride(
vault.WithOverrideUsernameAttribute("orig-user"),
vault.WithOverridePrivateKeyAttribute("orig-pk"),
)),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
usernameAttribute: nil,
privateKeyAttribute: nil,
}
ret, err := structpb.NewStruct(v)
require.NoError(t, err)
return ret
}(),
},
},
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
out := proto.Clone(in).(*pb.CredentialLibrary)
out.CredentialMappingOverrides = nil
return out
},
},
{
name: "ssh-private-key-no-mapping-override-delete-mapping-override-field-specific",
opts: []vault.Option{
vault.WithCredentialType("ssh_private_key"),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
usernameAttribute: nil,
privateKeyAttribute: nil,
}
ret, err := structpb.NewStruct(v)
require.NoError(t, err)
return ret
}(),
},
},
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
out := proto.Clone(in).(*pb.CredentialLibrary)
out.CredentialMappingOverrides = nil
return out
},
},
}
for _, tc := range successCases {
@@ -34,6 +34,16 @@ func dynamicToWorkerCredential(ctx context.Context, cred credential.Dynamic) (se
},
}
case credential.SshPrivateKey:
workerCred = &serverpb.Credential{
Credential: &serverpb.Credential_SshPrivateKey{
SshPrivateKey: &serverpb.SshPrivateKey{
Username: c.Username(),
PrivateKey: string(c.PrivateKey()),
},
},
}
default:
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
}
@@ -90,6 +100,17 @@ func dynamicToSessionCredential(ctx context.Context, cred credential.Dynamic) (*
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
}
case credential.SshPrivateKey:
credData, err = handlers.ProtoToStruct(
&pb.SshPrivateKeyCredential{
Username: c.Username(),
PrivateKey: string(c.PrivateKey()),
},
)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
}
default:
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
}
@@ -128,6 +149,16 @@ func staticToWorkerCredential(ctx context.Context, cred credential.Static) (sess
},
}
case *credstatic.SshPrivateKeyCredential:
workerCred = &serverpb.Credential{
Credential: &serverpb.Credential_SshPrivateKey{
SshPrivateKey: &serverpb.SshPrivateKey{
Username: c.GetUsername(),
PrivateKey: string(c.GetPrivateKey()),
},
},
}
default:
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
}
@@ -160,7 +191,24 @@ func staticToSessionCredential(ctx context.Context, cred credential.Static) (*pb
"password": string(c.GetPassword()),
}
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for static credential"))
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for username password credential"))
}
case *credstatic.SshPrivateKeyCredential:
var err error
credType = string(credential.SshPrivateKeyType)
credData, err = handlers.ProtoToStruct(
&pb.SshPrivateKeyCredential{
Username: c.GetUsername(),
PrivateKey: string(c.GetPrivateKey()),
},
)
secret = map[string]interface{}{
"username": c.GetUsername(),
"private_key": string(c.GetPrivateKey()),
}
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for ssh private key credential"))
}
default:
@@ -172,6 +220,9 @@ func staticToSessionCredential(ctx context.Context, cred credential.Static) (*pb
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling static secret to json"))
}
sSecret, err := structpb.NewStruct(secret)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for secret"))
}
return &pb.SessionCredential{
CredentialSource: &pb.CredentialSource{
@@ -1796,13 +1796,21 @@ func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequ
badFields[globals.EgressCredentialSourceIdsField] = "Application or Egress Credential Source IDs must be provided."
}
for _, cl := range req.GetApplicationCredentialSourceIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credential.UsernamePasswordCredentialPrefix, credential.PreviousUsernamePasswordCredentialPrefix) {
if !handlers.ValidId(handlers.Id(cl),
vault.CredentialLibraryPrefix,
credential.UsernamePasswordCredentialPrefix,
credential.PreviousUsernamePasswordCredentialPrefix,
credential.SshPrivateKeyCredentialPrefix) {
badFields[globals.ApplicationCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
break
}
}
for _, cl := range req.GetEgressCredentialSourceIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credential.UsernamePasswordCredentialPrefix, credential.PreviousUsernamePasswordCredentialPrefix) {
if !handlers.ValidId(handlers.Id(cl),
vault.CredentialLibraryPrefix,
credential.UsernamePasswordCredentialPrefix,
credential.PreviousUsernamePasswordCredentialPrefix,
credential.SshPrivateKeyCredentialPrefix) {
badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
break
}
@@ -1822,13 +1830,21 @@ func validateSetCredentialSourcesRequest(req *pbs.SetTargetCredentialSourcesRequ
badFields[globals.VersionField] = "Required field."
}
for _, cl := range req.GetApplicationCredentialSourceIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credential.UsernamePasswordCredentialPrefix, credential.PreviousUsernamePasswordCredentialPrefix) {
if !handlers.ValidId(handlers.Id(cl),
vault.CredentialLibraryPrefix,
credential.UsernamePasswordCredentialPrefix,
credential.PreviousUsernamePasswordCredentialPrefix,
credential.SshPrivateKeyCredentialPrefix) {
badFields[globals.ApplicationCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
break
}
}
for _, cl := range req.GetEgressCredentialSourceIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credential.UsernamePasswordCredentialPrefix, credential.PreviousUsernamePasswordCredentialPrefix) {
if !handlers.ValidId(handlers.Id(cl),
vault.CredentialLibraryPrefix,
credential.UsernamePasswordCredentialPrefix,
credential.PreviousUsernamePasswordCredentialPrefix,
credential.SshPrivateKeyCredentialPrefix) {
badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
break
}
@@ -1852,13 +1868,21 @@ func validateRemoveCredentialSourcesRequest(req *pbs.RemoveTargetCredentialSourc
badFields[globals.EgressCredentialSourceIdsField] = "Application or Egress Credential Source IDs must be provided."
}
for _, cl := range req.GetApplicationCredentialSourceIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credential.UsernamePasswordCredentialPrefix, credential.PreviousUsernamePasswordCredentialPrefix) {
if !handlers.ValidId(handlers.Id(cl),
vault.CredentialLibraryPrefix,
credential.UsernamePasswordCredentialPrefix,
credential.PreviousUsernamePasswordCredentialPrefix,
credential.SshPrivateKeyCredentialPrefix) {
badFields[globals.ApplicationCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
break
}
}
for _, cl := range req.GetEgressCredentialSourceIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credential.UsernamePasswordCredentialPrefix, credential.PreviousUsernamePasswordCredentialPrefix) {
if !handlers.ValidId(handlers.Id(cl),
vault.CredentialLibraryPrefix,
credential.UsernamePasswordCredentialPrefix,
credential.PreviousUsernamePasswordCredentialPrefix,
credential.SshPrivateKeyCredentialPrefix) {
badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
break
}
@@ -43,6 +43,7 @@ import (
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/ssh/testdata"
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@@ -2732,7 +2733,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
staticStore := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId())
credService, err := credentials.NewService(staticCredRepoFn, iamRepoFn)
require.NoError(t, err)
credResp, err := credService.CreateCredential(ctx, &pbs.CreateCredentialRequest{Item: &credpb.Credential{
upCredResp, err := credService.CreateCredential(ctx, &pbs.CreateCredentialRequest{Item: &credpb.Credential{
CredentialStoreId: staticStore.GetPublicId(),
Type: credential.UsernamePasswordSubtype.String(),
Name: wrapperspb.String("Cred Name"),
@@ -2745,7 +2746,62 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
},
}})
require.NoError(t, err)
require.NotNil(t, credResp)
require.NotNil(t, upCredResp)
sshPkCredResp, err := credService.CreateCredential(ctx, &pbs.CreateCredentialRequest{Item: &credpb.Credential{
CredentialStoreId: staticStore.GetPublicId(),
Type: credential.SshPrivateKeySubtype.String(),
Name: wrapperspb.String("Cred SSH Name"),
Description: wrapperspb.String("Cred SSH Description"),
Attrs: &credpb.Credential_SshPrivateKeyAttributes{
SshPrivateKeyAttributes: &credpb.SshPrivateKeyAttributes{
Username: wrapperspb.String("static-username"),
PrivateKey: wrapperspb.String(string(testdata.PEMBytes["ed25519"])),
},
},
}})
require.NoError(t, err)
require.NotNil(t, sshPkCredResp)
// Create secret in vault with default username and password fields
defaultSshPrivateKey := v.CreateKVSecret(t, "default-sshpk", []byte(`{"data": {"username": "my-user", "private_key": "my-pk"}}`))
require.NotNil(t, defaultSshPrivateKey)
clsRespSshPrivateKey, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
CredentialStoreId: vaultStore.GetPublicId(),
Name: wrapperspb.String("SSH Private Key Library"),
Description: wrapperspb.String("SSH Private Key Library Description"),
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
Path: wrapperspb.String(path.Join("secret", "data", "default-sshpk")),
HttpMethod: wrapperspb.String("GET"),
},
},
CredentialType: "ssh_private_key",
}})
require.NoError(t, err)
// Create secret in vault with non default username and password fields
nonDefaultSshPrivateKey := v.CreateKVSecret(t, "non-default-sshpk", []byte(`{"data": {"non-default-user": "my-user", "non-default-pk": "my-special-pk"}}`))
require.NotNil(t, nonDefaultSshPrivateKey)
clsRespSshPrivateKeyWithMapping, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
CredentialStoreId: vaultStore.GetPublicId(),
Name: wrapperspb.String("SSH Private Key Mapping Library"),
Description: wrapperspb.String("SSH Private Key Mapping Library Description"),
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
Path: wrapperspb.String(path.Join("secret", "data", "non-default-sshpk")),
HttpMethod: wrapperspb.String("GET"),
},
},
CredentialType: "ssh_private_key",
CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{
"username_attribute": structpb.NewStringValue("non-default-user"),
"private_key_attribute": structpb.NewStringValue("non-default-pk"),
}},
}})
require.NoError(t, err)
cases := []struct {
name string
@@ -2826,14 +2882,14 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
{
name: "static-UsernamePassword",
hostSourceId: shs.GetPublicId(),
credSourceId: credResp.GetItem().GetId(),
credSourceId: upCredResp.GetItem().GetId(),
wantedHostId: h.GetPublicId(),
wantedEndpoint: h.GetAddress(),
wantedCred: &pb.SessionCredential{
CredentialSource: &pb.CredentialSource{
Id: credResp.GetItem().GetId(),
Name: credResp.GetItem().GetName().GetValue(),
Description: credResp.GetItem().GetDescription().GetValue(),
Id: upCredResp.GetItem().GetId(),
Name: upCredResp.GetItem().GetName().GetValue(),
Description: upCredResp.GetItem().GetDescription().GetValue(),
CredentialStoreId: staticStore.GetPublicId(),
Type: credstatic.Subtype.String(),
CredentialType: string(credential.UsernamePasswordType),
@@ -2849,6 +2905,84 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
}(),
},
},
{
name: "static-ssh-private-key",
hostSourceId: shs.GetPublicId(),
credSourceId: sshPkCredResp.GetItem().GetId(),
wantedHostId: h.GetPublicId(),
wantedEndpoint: h.GetAddress(),
wantedCred: &pb.SessionCredential{
CredentialSource: &pb.CredentialSource{
Id: sshPkCredResp.GetItem().GetId(),
Name: sshPkCredResp.GetItem().GetName().GetValue(),
Description: sshPkCredResp.GetItem().GetDescription().GetValue(),
CredentialStoreId: staticStore.GetPublicId(),
Type: credstatic.Subtype.String(),
CredentialType: string(credential.SshPrivateKeyType),
},
Credential: func() *structpb.Struct {
data := map[string]interface{}{
"private_key": string(testdata.PEMBytes["ed25519"]),
"username": "static-username",
}
st, err := structpb.NewStruct(data)
require.NoError(t, err)
return st
}(),
},
},
{
name: "vault-ssh-private-key",
hostSourceId: shs.GetPublicId(),
credSourceId: clsRespSshPrivateKey.GetItem().GetId(),
wantedHostId: h.GetPublicId(),
wantedEndpoint: h.GetAddress(),
wantedCred: &pb.SessionCredential{
CredentialSource: &pb.CredentialSource{
Id: clsRespSshPrivateKey.GetItem().GetId(),
Name: clsRespSshPrivateKey.GetItem().GetName().GetValue(),
Description: clsRespSshPrivateKey.GetItem().GetDescription().GetValue(),
CredentialStoreId: vaultStore.GetPublicId(),
Type: vault.Subtype.String(),
CredentialType: string(credential.SshPrivateKeyType),
},
Credential: func() *structpb.Struct {
data := map[string]interface{}{
"private_key": "my-pk",
"username": "my-user",
}
st, err := structpb.NewStruct(data)
require.NoError(t, err)
return st
}(),
},
},
{
name: "vault-ssh-private-key-with-mapping",
hostSourceId: shs.GetPublicId(),
credSourceId: clsRespSshPrivateKeyWithMapping.GetItem().GetId(),
wantedHostId: h.GetPublicId(),
wantedEndpoint: h.GetAddress(),
wantedCred: &pb.SessionCredential{
CredentialSource: &pb.CredentialSource{
Id: clsRespSshPrivateKeyWithMapping.GetItem().GetId(),
Name: clsRespSshPrivateKeyWithMapping.GetItem().GetName().GetValue(),
Description: clsRespSshPrivateKeyWithMapping.GetItem().GetDescription().GetValue(),
CredentialStoreId: vaultStore.GetPublicId(),
Type: vault.Subtype.String(),
CredentialType: string(credential.SshPrivateKeyType),
},
Credential: func() *structpb.Struct {
data := map[string]interface{}{
"username": "my-user",
"private_key": "my-special-pk",
}
st, err := structpb.NewStruct(data)
require.NoError(t, err)
return st
}(),
},
},
}
for _, tc := range cases {
@@ -1,9 +1,9 @@
begin;
-- Replaces view from 22/05_vault_private_library.up.sql
drop view credential_vault_library_public;
drop view credential_vault_library_private;
-- Replaced in 39/02_vault_ssh_private_key_override.up.sql
create view credential_vault_library_private as
with
password_override (library_id, username_attribute, password_attribute) as (
@@ -47,6 +47,7 @@ begin;
'credential_vault_library_private is a view where each row contains a credential library and the credential library''s data needed to connect to Vault. '
'Each row may contain encrypted data. This view should not be used to retrieve data which will be returned external to boundary.';
-- Replaced in 39/02_vault_ssh_private_key_override.up.sql
create view credential_vault_library_public as
select public_id,
store_id,
@@ -0,0 +1,114 @@
begin;
-- add ssh_private_key type
insert into credential_type_enm (name)
values ('ssh_private_key');
create table credential_vault_library_ssh_private_key_mapping_override (
library_id wt_public_id primary key
constraint credential_vault_library_fkey
references credential_vault_library (public_id)
on delete cascade
on update cascade
constraint credential_vault_library_mapping_override_fkey
references credential_vault_library_mapping_override (library_id)
on delete cascade
on update cascade,
username_attribute wt_sentinel
default wt_to_sentinel('no override')
not null,
private_key_attribute wt_sentinel
default wt_to_sentinel('no override')
not null
);
comment on table credential_vault_library_ssh_private_key_mapping_override is
'credential_vault_library_ssh_private_key_mapping_override is a table '
'where each row represents a mapping that overrides the default mapping '
'from a generic vault secret to a ssh private key credential type '
'for a vault credential library.';
create trigger insert_credential_vault_library_mapping_override_subtype before insert on credential_vault_library_ssh_private_key_mapping_override
for each row execute procedure insert_credential_vault_library_mapping_override_subtype();
create trigger delete_credential_vault_library_mapping_override_subtype after delete on credential_vault_library_ssh_private_key_mapping_override
for each row execute procedure delete_credential_vault_library_mapping_override_subtype();
-- Replaces view from 36/02_vault_private_library.up.sql
drop view credential_vault_library_public;
drop view credential_vault_library_private;
create view credential_vault_library_private as
with
password_override (library_id, username_attribute, password_attribute) as (
select library_id,
nullif(username_attribute, wt_to_sentinel('no override')),
nullif(password_attribute, wt_to_sentinel('no override'))
from credential_vault_library_username_password_mapping_override
),
ssh_private_key_override (library_id, username_attribute, private_key_attribute) as (
select library_id,
nullif(username_attribute, wt_to_sentinel('no override')),
nullif(private_key_attribute, wt_to_sentinel('no override'))
from credential_vault_library_ssh_private_key_mapping_override
)
select library.public_id as public_id,
library.store_id as store_id,
library.name as name,
library.description as description,
library.create_time as create_time,
library.update_time as update_time,
library.version as version,
library.vault_path as vault_path,
library.http_method as http_method,
library.http_request_body as http_request_body,
library.credential_type as credential_type,
store.scope_id as scope_id,
store.vault_address as vault_address,
store.namespace as namespace,
store.ca_cert as ca_cert,
store.tls_server_name as tls_server_name,
store.tls_skip_verify as tls_skip_verify,
store.token_hmac as token_hmac,
store.ct_token as ct_token, -- encrypted
store.token_key_id as token_key_id,
store.client_cert as client_cert,
store.ct_client_key as ct_client_key, -- encrypted
store.client_key_id as client_key_id,
coalesce(upasso.username_attribute,sshpk.username_attribute)
as username_attribute,
upasso.password_attribute as password_attribute,
sshpk.private_key_attribute as private_key_attribute
from credential_vault_library library
join credential_vault_store_private store
on library.store_id = store.public_id
left join password_override upasso
on library.public_id = upasso.library_id
and store.token_status = 'current'
left join ssh_private_key_override sshpk
on library.public_id = sshpk.library_id
and store.token_status = 'current';
comment on view credential_vault_library_private is
'credential_vault_library_private is a view where each row contains a credential library and the credential library''s data needed to connect to Vault. '
'Each row may contain encrypted data. This view should not be used to retrieve data which will be returned external to boundary.';
-- Replaces view from 36/02_vault_private_library.up.sql
create view credential_vault_library_public as
select public_id,
store_id,
name,
description,
create_time,
update_time,
version,
vault_path,
http_method,
http_request_body,
credential_type,
username_attribute,
password_attribute,
private_key_attribute
from credential_vault_library_private;
comment on view credential_vault_library_public is
'credential_vault_library_public is a view where each row contains a credential library and any of library''s credential mapping overrides. '
'No encrypted data is returned. This view can be used to retrieve data which will be returned external to boundary.';
commit;
@@ -342,15 +342,20 @@ begin;
('vs_______wvs', 'kdkv___widget', 'current', 'hmac-value', 'token-value', now(), now() + interval '1 hour');
insert into credential_vault_library
(store_id, public_id, name, description, vault_path, http_method, credential_type)
(store_id, public_id, name, description, vault_path, http_method, credential_type)
values
('vs_______wvs', 'vl______wvl1', 'widget vault library', 'None', '/secrets', 'GET', 'unspecified'),
('vs_______wvs', 'vl______wvl2', 'widget vault ssh', 'None', '/secrets/ssh/admin', 'GET', 'unspecified'),
('vs_______wvs', 'vl______wvl3', 'widget vault kv one', 'None', '/secrets/kv/one', 'GET', 'username_password'),
('vs_______wvs', 'vl______wvl4', 'widget vault kv two', 'None', '/secrets/kv/two', 'GET', 'username_password'),
('vs_______wvs', 'vl______wvl5', 'widget vault kv three', 'None', '/secrets/kv/three', 'GET', 'username_password'),
('vs_______wvs', 'vl______wvl6', 'widget vault kv four', 'None', '/secrets/kv/four', 'GET', 'username_password'),
('vs_______wvs', 'vl______wvl7', 'widget vault kv five', 'None', '/secrets/kv/five', 'GET', 'username_password');
('vs_______wvs', 'vl______wvl1', 'widget vault library', 'None', '/secrets', 'GET', 'unspecified'),
('vs_______wvs', 'vl______wvl2', 'widget vault ssh', 'None', '/secrets/ssh/admin', 'GET', 'unspecified'),
('vs_______wvs', 'vl______wvl3', 'widget vault kv one', 'None', '/secrets/kv/one', 'GET', 'username_password'),
('vs_______wvs', 'vl______wvl4', 'widget vault kv two', 'None', '/secrets/kv/two', 'GET', 'username_password'),
('vs_______wvs', 'vl______wvl5', 'widget vault kv three', 'None', '/secrets/kv/three', 'GET', 'username_password'),
('vs_______wvs', 'vl______wvl6', 'widget vault kv four', 'None', '/secrets/kv/four', 'GET', 'username_password'),
('vs_______wvs', 'vl______wvl7', 'widget vault kv five', 'None', '/secrets/kv/five', 'GET', 'username_password'),
('vs_______wvs', 'vl______wvl8', 'widget vault kv six', 'None', '/secrets/kv/six', 'GET', 'ssh_private_key'),
('vs_______wvs', 'vl______wvl9', 'widget vault kv seven', 'None', '/secrets/kv/seven', 'GET', 'ssh_private_key'),
('vs_______wvs', 'vl______wvl10', 'widget vault kv eight', 'None', '/secrets/kv/eight', 'GET', 'ssh_private_key'),
('vs_______wvs', 'vl______wvl11', 'widget vault kv nine', 'None', '/secrets/kv/nine', 'GET', 'ssh_private_key'),
('vs_______wvs', 'vl______wvl12', 'widget vault kv ten', 'None', '/secrets/kv/n', 'GET', 'ssh_private_key');
insert into credential_vault_library_username_password_mapping_override
(library_id)
@@ -372,6 +377,26 @@ begin;
values
('vl______wvl7', 'my_username', 'my_password');
insert into credential_vault_library_ssh_private_key_mapping_override
(library_id)
values
('vl______wvl9');
insert into credential_vault_library_ssh_private_key_mapping_override
(library_id, username_attribute)
values
('vl______wvl10', 'my_username');
insert into credential_vault_library_ssh_private_key_mapping_override
(library_id, private_key_attribute)
values
('vl______wvl11', 'my_private_key');
insert into credential_vault_library_ssh_private_key_mapping_override
(library_id, username_attribute, private_key_attribute)
values
('vl______wvl12', 'my_username', 'my_private_key');
insert into target_credential_library
(target_id, credential_library_id, credential_purpose)
values
@@ -0,0 +1,61 @@
-- credential_vault_library_ssh_private_key_mapping_override tests:
-- the following triggers
-- insert_credential_vault_library_mapping_override_subtype
-- delete_credential_vault_library_mapping_override_subtype
begin;
select plan(11);
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'credentials');
-- validate the setup data
select is(count(*), 4::bigint)
from credential_vault_library_ssh_private_key_mapping_override
where library_id in ('vl______wvl9', 'vl______wvl10', 'vl______wvl11', 'vl______wvl12');
select is(count(*), 4::bigint)
from credential_vault_library_mapping_override
where library_id in ('vl______wvl9', 'vl______wvl10', 'vl______wvl11', 'vl______wvl12');
prepare select_private_libraries as
select public_id::text, credential_type::text, username_attribute::text, private_key_attribute::text
from credential_vault_library_private
where public_id in ('vl______wvl2', 'vl______wvl8', 'vl______wvl9', 'vl______wvl10', 'vl______wvl11', 'vl______wvl12')
order by public_id;
select results_eq(
'select_private_libraries',
$$VALUES
('vl______wvl10', 'ssh_private_key', 'my_username', null),
('vl______wvl11', 'ssh_private_key', null, 'my_private_key'),
('vl______wvl12', 'ssh_private_key', 'my_username', 'my_private_key'),
('vl______wvl2', 'unspecified', null, null),
('vl______wvl8', 'ssh_private_key', null, null),
('vl______wvl9', 'ssh_private_key', null, null)$$
);
-- validate the insert triggers
select is(count(*), 0::bigint) from credential_vault_library_ssh_private_key_mapping_override where library_id = 'vl______wvl8';
select is(count(*), 0::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl8';
prepare insert_credential_vault_library_ssh_private_key_mapping_override as
insert into credential_vault_library_ssh_private_key_mapping_override
(library_id, username_attribute, private_key_attribute)
values
('vl______wvl8', 'my_username', 'my_private_key');
select lives_ok('insert_credential_vault_library_ssh_private_key_mapping_override');
select is(count(*), 1::bigint) from credential_vault_library_ssh_private_key_mapping_override where library_id = 'vl______wvl8';
select is(count(*), 1::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl8';
-- validate the delete triggers
prepare delete_credential_vault_library_ssh_private_key_mapping_override as
delete
from credential_vault_library_ssh_private_key_mapping_override
where library_id = 'vl______wvl8';
select lives_ok('delete_credential_vault_library_ssh_private_key_mapping_override');
select is(count(*), 0::bigint) from credential_vault_library_ssh_private_key_mapping_override where library_id = 'vl______wvl8';
select is(count(*), 0::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl8';
select * from finish();
rollback;
@@ -27,6 +27,7 @@ type Credential struct {
// Types that are assignable to Credential:
// *Credential_UsernamePassword
// *Credential_SshPrivateKey
Credential isCredential_Credential `protobuf_oneof:"credential"`
}
@@ -76,6 +77,13 @@ func (x *Credential) GetUsernamePassword() *UsernamePassword {
return nil
}
func (x *Credential) GetSshPrivateKey() *SshPrivateKey {
if x, ok := x.GetCredential().(*Credential_SshPrivateKey); ok {
return x.SshPrivateKey
}
return nil
}
type isCredential_Credential interface {
isCredential_Credential()
}
@@ -84,8 +92,14 @@ type Credential_UsernamePassword struct {
UsernamePassword *UsernamePassword `protobuf:"bytes,2,opt,name=username_password,json=usernamePassword,proto3,oneof"`
}
type Credential_SshPrivateKey struct {
SshPrivateKey *SshPrivateKey `protobuf:"bytes,3,opt,name=ssh_private_key,json=sshPrivateKey,proto3,oneof"`
}
func (*Credential_UsernamePassword) isCredential_Credential() {}
func (*Credential_SshPrivateKey) isCredential_Credential() {}
// UsernamePassword is a credential containing a username and a password.
type UsernamePassword struct {
state protoimpl.MessageState
@@ -144,6 +158,64 @@ func (x *UsernamePassword) GetPassword() string {
return ""
}
// SshPrivateKey is a credential containing a username and a private key.
type SshPrivateKey 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 private key of the credential
PrivateKey string `protobuf:"bytes,20,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` // @gotags: `class:"secret"`
}
func (x *SshPrivateKey) Reset() {
*x = SshPrivateKey{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_servers_services_v1_credential_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SshPrivateKey) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SshPrivateKey) ProtoMessage() {}
func (x *SshPrivateKey) ProtoReflect() protoreflect.Message {
mi := &file_controller_servers_services_v1_credential_proto_msgTypes[2]
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 SshPrivateKey.ProtoReflect.Descriptor instead.
func (*SshPrivateKey) Descriptor() ([]byte, []int) {
return file_controller_servers_services_v1_credential_proto_rawDescGZIP(), []int{2}
}
func (x *SshPrivateKey) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *SshPrivateKey) GetPrivateKey() string {
if x != nil {
return x.PrivateKey
}
return ""
}
var File_controller_servers_services_v1_credential_proto protoreflect.FileDescriptor
var file_controller_servers_services_v1_credential_proto_rawDesc = []byte{
@@ -152,26 +224,37 @@ var file_controller_servers_services_v1_credential_proto_rawDesc = []byte{
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, 0x90, 0x01, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x31, 0x22, 0xe9, 0x01, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x12, 0x5f, 0x0a, 0x11, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x73,
0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 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, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x00, 0x52,
0x10, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
0x64, 0x42, 0x0c, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4a,
0x04, 0x08, 0x01, 0x10, 0x02, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x73, 0x73,
0x77, 0x6f, 0x72, 0x64, 0x22, 0x4a, 0x0a, 0x10, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65,
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,
0x64, 0x12, 0x57, 0x0a, 0x0f, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65,
0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 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, 0x53, 0x73, 0x68, 0x50,
0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x48, 0x00, 0x52, 0x0d, 0x73, 0x73, 0x68,
0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x0c, 0x0a, 0x0a, 0x63, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x52, 0x0d,
0x75, 0x73, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x4a, 0x0a,
0x10, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 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, 0x22, 0x4c, 0x0a, 0x0d, 0x53, 0x73, 0x68,
0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 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, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,
0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69,
0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 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 (
@@ -186,18 +269,20 @@ func file_controller_servers_services_v1_credential_proto_rawDescGZIP() []byte {
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_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_controller_servers_services_v1_credential_proto_goTypes = []interface{}{
(*Credential)(nil), // 0: controller.servers.services.v1.Credential
(*UsernamePassword)(nil), // 1: controller.servers.services.v1.UsernamePassword
(*SshPrivateKey)(nil), // 2: controller.servers.services.v1.SshPrivateKey
}
var file_controller_servers_services_v1_credential_proto_depIdxs = []int32{
1, // 0: controller.servers.services.v1.Credential.username_password:type_name -> controller.servers.services.v1.UsernamePassword
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
2, // 1: controller.servers.services.v1.Credential.ssh_private_key:type_name -> controller.servers.services.v1.SshPrivateKey
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_servers_services_v1_credential_proto_init() }
@@ -230,9 +315,22 @@ func file_controller_servers_services_v1_credential_proto_init() {
return nil
}
}
file_controller_servers_services_v1_credential_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SshPrivateKey); 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_UsernamePassword)(nil),
(*Credential_SshPrivateKey)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
@@ -240,7 +338,7 @@ func file_controller_servers_services_v1_credential_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_servers_services_v1_credential_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
@@ -288,3 +288,12 @@ message UsernamePasswordCredential {
// Password of the credential
string password = 2; // @gotags: `class:"secret"`
}
// The layout of the struct for "credential" field in SessionCredential for a ssh_private_key credential type.
message SshPrivateKeyCredential {
// Username of the credential
string username = 1; // @gotags: `class:"sensitive"`
// Private key of the credential
string private_key = 2; // @gotags: `class:"secret"`
}
@@ -10,6 +10,7 @@ message Credential {
oneof credential {
UsernamePassword username_password = 2;
SshPrivateKey ssh_private_key = 3;
}
}
@@ -21,3 +22,12 @@ message UsernamePassword {
// The password of the credential
string password = 20; // @gotags: `class:"secret"`
}
// SshPrivateKey is a credential containing a username and a private key.
message SshPrivateKey {
// The username of the credential
string username = 10; // @gotags: `class:"public"`
// The private key of the credential
string private_key = 20; // @gotags: `class:"secret"`
}
@@ -344,3 +344,29 @@ message UsernamePasswordOverride {
// @inject_tag: `gorm:"default:null"`
string password_attribute = 3;
}
message SshPrivateKeyOverride {
// library_id of the owning vault credential library.
// @inject_tag: `gorm:"primary_key"`
string library_id = 1;
// username_attribute is the name of the attribute in the Data field of a
// Vault api.Secret that maps to a username.
// If set, it overrides any default attribute names the system uses to
// find a username attribute.
//
// See https://github.com/hashicorp/vault/blob/5e505ec039177e8212cbbab74ccb644c46e62e63/api/secret.go#L25
//
// @inject_tag: `gorm:"default:null"`
string username_attribute = 2;
// private_key_attribute is the name of the attribute in the Data field of a
// Vault api.Secret that maps to a private key.
// If set, it overrides any default attribute names the system uses to
// find a private_key attribute.
//
// See https://github.com/hashicorp/vault/blob/5e505ec039177e8212cbbab74ccb644c46e62e63/api/secret.go#L25
//
// @inject_tag: `gorm:"default:null"`
string private_key_attribute = 3;
}
@@ -1066,6 +1066,64 @@ func (x *UsernamePasswordCredential) GetPassword() string {
return ""
}
// The layout of the struct for "credential" field in SessionCredential for a ssh_private_key credential type.
type SshPrivateKeyCredential struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Username of the credential
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty" class:"sensitive"` // @gotags: `class:"sensitive"`
// Private key of the credential
PrivateKey string `protobuf:"bytes,2,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty" class:"secret"` // @gotags: `class:"secret"`
}
func (x *SshPrivateKeyCredential) Reset() {
*x = SshPrivateKeyCredential{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SshPrivateKeyCredential) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SshPrivateKeyCredential) ProtoMessage() {}
func (x *SshPrivateKeyCredential) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[11]
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 SshPrivateKeyCredential.ProtoReflect.Descriptor instead.
func (*SshPrivateKeyCredential) Descriptor() ([]byte, []int) {
return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{11}
}
func (x *SshPrivateKeyCredential) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *SshPrivateKeyCredential) GetPrivateKey() string {
if x != nil {
return x.PrivateKey
}
return ""
}
var File_controller_api_resources_targets_v1_target_proto protoreflect.FileDescriptor
var file_controller_api_resources_targets_v1_target_proto_rawDesc = []byte{
@@ -1326,13 +1384,18 @@ var file_controller_api_resources_targets_v1_target_proto_rawDesc = []byte{
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61,
0x6d, 0x65, 0x18, 0x01, 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, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x42, 0x50,
0x5a, 0x4e, 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,
0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x3b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x56,
0x0a, 0x17, 0x53, 0x73, 0x68, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65,
0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65,
0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65,
0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76,
0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x50, 0x5a, 0x4e, 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, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73,
0x3b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -1347,7 +1410,7 @@ func file_controller_api_resources_targets_v1_target_proto_rawDescGZIP() []byte
return file_controller_api_resources_targets_v1_target_proto_rawDescData
}
var file_controller_api_resources_targets_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
var file_controller_api_resources_targets_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
var file_controller_api_resources_targets_v1_target_proto_goTypes = []interface{}{
(*HostSource)(nil), // 0: controller.api.resources.targets.v1.HostSource
(*HostSet)(nil), // 1: controller.api.resources.targets.v1.HostSet
@@ -1360,38 +1423,39 @@ var file_controller_api_resources_targets_v1_target_proto_goTypes = []interface{
(*SessionAuthorizationData)(nil), // 8: controller.api.resources.targets.v1.SessionAuthorizationData
(*SessionAuthorization)(nil), // 9: controller.api.resources.targets.v1.SessionAuthorization
(*UsernamePasswordCredential)(nil), // 10: controller.api.resources.targets.v1.UsernamePasswordCredential
(*structpb.Struct)(nil), // 11: google.protobuf.Struct
(*scopes.ScopeInfo)(nil), // 12: controller.api.resources.scopes.v1.ScopeInfo
(*wrapperspb.StringValue)(nil), // 13: google.protobuf.StringValue
(*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp
(*wrapperspb.UInt32Value)(nil), // 15: google.protobuf.UInt32Value
(*wrapperspb.Int32Value)(nil), // 16: google.protobuf.Int32Value
(*SshPrivateKeyCredential)(nil), // 11: controller.api.resources.targets.v1.SshPrivateKeyCredential
(*structpb.Struct)(nil), // 12: google.protobuf.Struct
(*scopes.ScopeInfo)(nil), // 13: controller.api.resources.scopes.v1.ScopeInfo
(*wrapperspb.StringValue)(nil), // 14: google.protobuf.StringValue
(*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp
(*wrapperspb.UInt32Value)(nil), // 16: google.protobuf.UInt32Value
(*wrapperspb.Int32Value)(nil), // 17: google.protobuf.Int32Value
}
var file_controller_api_resources_targets_v1_target_proto_depIdxs = []int32{
11, // 0: controller.api.resources.targets.v1.SessionSecret.decoded:type_name -> google.protobuf.Struct
12, // 0: controller.api.resources.targets.v1.SessionSecret.decoded:type_name -> google.protobuf.Struct
2, // 1: controller.api.resources.targets.v1.SessionCredential.credential_source:type_name -> controller.api.resources.targets.v1.CredentialSource
3, // 2: controller.api.resources.targets.v1.SessionCredential.secret:type_name -> controller.api.resources.targets.v1.SessionSecret
11, // 3: controller.api.resources.targets.v1.SessionCredential.credential:type_name -> google.protobuf.Struct
12, // 4: controller.api.resources.targets.v1.Target.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
13, // 5: controller.api.resources.targets.v1.Target.name:type_name -> google.protobuf.StringValue
13, // 6: controller.api.resources.targets.v1.Target.description:type_name -> google.protobuf.StringValue
14, // 7: controller.api.resources.targets.v1.Target.created_time:type_name -> google.protobuf.Timestamp
14, // 8: controller.api.resources.targets.v1.Target.updated_time:type_name -> google.protobuf.Timestamp
12, // 3: controller.api.resources.targets.v1.SessionCredential.credential:type_name -> google.protobuf.Struct
13, // 4: controller.api.resources.targets.v1.Target.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
14, // 5: controller.api.resources.targets.v1.Target.name:type_name -> google.protobuf.StringValue
14, // 6: controller.api.resources.targets.v1.Target.description:type_name -> google.protobuf.StringValue
15, // 7: controller.api.resources.targets.v1.Target.created_time:type_name -> google.protobuf.Timestamp
15, // 8: controller.api.resources.targets.v1.Target.updated_time:type_name -> google.protobuf.Timestamp
1, // 9: controller.api.resources.targets.v1.Target.host_sets:type_name -> controller.api.resources.targets.v1.HostSet
0, // 10: controller.api.resources.targets.v1.Target.host_sources:type_name -> controller.api.resources.targets.v1.HostSource
15, // 11: controller.api.resources.targets.v1.Target.session_max_seconds:type_name -> google.protobuf.UInt32Value
16, // 12: controller.api.resources.targets.v1.Target.session_connection_limit:type_name -> google.protobuf.Int32Value
13, // 13: controller.api.resources.targets.v1.Target.worker_filter:type_name -> google.protobuf.StringValue
16, // 11: controller.api.resources.targets.v1.Target.session_max_seconds:type_name -> google.protobuf.UInt32Value
17, // 12: controller.api.resources.targets.v1.Target.session_connection_limit:type_name -> google.protobuf.Int32Value
14, // 13: controller.api.resources.targets.v1.Target.worker_filter:type_name -> google.protobuf.StringValue
2, // 14: controller.api.resources.targets.v1.Target.application_credential_sources:type_name -> controller.api.resources.targets.v1.CredentialSource
2, // 15: controller.api.resources.targets.v1.Target.egress_credential_sources:type_name -> controller.api.resources.targets.v1.CredentialSource
11, // 16: controller.api.resources.targets.v1.Target.attributes:type_name -> google.protobuf.Struct
12, // 16: controller.api.resources.targets.v1.Target.attributes:type_name -> google.protobuf.Struct
6, // 17: controller.api.resources.targets.v1.Target.tcp_target_attributes:type_name -> controller.api.resources.targets.v1.TcpTargetAttributes
15, // 18: controller.api.resources.targets.v1.TcpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
12, // 19: controller.api.resources.targets.v1.SessionAuthorizationData.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
14, // 20: controller.api.resources.targets.v1.SessionAuthorizationData.created_time:type_name -> google.protobuf.Timestamp
16, // 18: controller.api.resources.targets.v1.TcpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
13, // 19: controller.api.resources.targets.v1.SessionAuthorizationData.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
15, // 20: controller.api.resources.targets.v1.SessionAuthorizationData.created_time:type_name -> google.protobuf.Timestamp
7, // 21: controller.api.resources.targets.v1.SessionAuthorizationData.worker_info:type_name -> controller.api.resources.targets.v1.WorkerInfo
12, // 22: controller.api.resources.targets.v1.SessionAuthorization.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
14, // 23: controller.api.resources.targets.v1.SessionAuthorization.created_time:type_name -> google.protobuf.Timestamp
13, // 22: controller.api.resources.targets.v1.SessionAuthorization.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
15, // 23: controller.api.resources.targets.v1.SessionAuthorization.created_time:type_name -> google.protobuf.Timestamp
4, // 24: controller.api.resources.targets.v1.SessionAuthorization.credentials:type_name -> controller.api.resources.targets.v1.SessionCredential
25, // [25:25] is the sub-list for method output_type
25, // [25:25] is the sub-list for method input_type
@@ -1538,6 +1602,18 @@ func file_controller_api_resources_targets_v1_target_proto_init() {
return nil
}
}
file_controller_api_resources_targets_v1_target_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SshPrivateKeyCredential); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_controller_api_resources_targets_v1_target_proto_msgTypes[5].OneofWrappers = []interface{}{
(*Target_Attributes)(nil),
@@ -1549,7 +1625,7 @@ func file_controller_api_resources_targets_v1_target_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_api_resources_targets_v1_target_proto_rawDesc,
NumEnums: 0,
NumMessages: 11,
NumMessages: 12,
NumExtensions: 0,
NumServices: 0,
},