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:
1 parent
011e2e7425
commit
ef5ac07f02
33 files changed
+2805
-234
No files matched your search
@@ -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(×tamp.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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
+49
-2
@@ -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
|
||||
|
||||
+259
@@ -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,
|
||||
|
||||
+114
@@ -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
|
||||
|
||||
+61
@@ -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,
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user