Add support for SSH private key passphrases (#2331)
This adds support for encrypted SSH keys via passphrase. Co-authored-by: Louis Ruch <louisruch@gmail.com> Co-authored-by: Michael Gaffney <mgaffney@users.noreply.github.com>
This commit is contained in:
45 files changed
+1921
-382
No files matched your search
+20
-4
@@ -38,9 +38,10 @@ const (
|
||||
|
||||
// Credential mapping override attributes
|
||||
const (
|
||||
usernameAttribute string = "username_attribute"
|
||||
passwordAttribute string = "password_attribute"
|
||||
privateKeyAttribute string = "private_key_attribute"
|
||||
usernameAttribute string = "username_attribute"
|
||||
passwordAttribute string = "password_attribute"
|
||||
privateKeyAttribute string = "private_key_attribute"
|
||||
pkPassphraseAttribute string = "private_key_passphrase_attribute"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -540,6 +541,9 @@ func toProto(in credential.Library, opt ...handlers.Option) (*pb.CredentialLibra
|
||||
if mapping.PrivateKeyAttribute != "" {
|
||||
m[privateKeyAttribute] = mapping.PrivateKeyAttribute
|
||||
}
|
||||
if mapping.PrivateKeyPassphraseAttribute != "" {
|
||||
m[pkPassphraseAttribute] = mapping.PrivateKeyPassphraseAttribute
|
||||
}
|
||||
}
|
||||
if len(m) > 0 {
|
||||
mp, err := structpb.NewStruct(m)
|
||||
@@ -612,6 +616,9 @@ func toStorageVaultLibrary(storeId string, in *pb.CredentialLibrary) (out *vault
|
||||
if pk := overrides[privateKeyAttribute]; pk != nil {
|
||||
mapOpts = append(mapOpts, vault.WithOverridePrivateKeyAttribute(pk.(string)))
|
||||
}
|
||||
if pass := overrides[pkPassphraseAttribute]; pass != nil {
|
||||
mapOpts = append(mapOpts, vault.WithOverridePrivateKeyPassphraseAttribute(pass.(string)))
|
||||
}
|
||||
if len(mapOpts) > 0 {
|
||||
opts = append(opts, vault.WithMappingOverride(vault.NewSshPrivateKeyOverride(mapOpts...)))
|
||||
}
|
||||
@@ -723,6 +730,7 @@ func validateMapping(badFields map[string]string, credentialType credential.Type
|
||||
case credential.SshPrivateKeyType:
|
||||
validFields[usernameAttribute] = true
|
||||
validFields[privateKeyAttribute] = true
|
||||
validFields[pkPassphraseAttribute] = true
|
||||
default:
|
||||
badFields[globals.CredentialTypeField] = fmt.Sprintf("Unknown credential type %q", credentialType)
|
||||
return
|
||||
@@ -782,10 +790,11 @@ func getMappingUpdates(credentialType credential.Type, current vault.MappingOver
|
||||
}
|
||||
|
||||
case credential.SshPrivateKeyType:
|
||||
var currentUser, currentPk interface{}
|
||||
var currentUser, currentpPass, currentPk interface{}
|
||||
if overrides, ok := current.(*vault.SshPrivateKeyOverride); ok {
|
||||
currentUser = overrides.UsernameAttribute
|
||||
currentPk = overrides.PrivateKeyAttribute
|
||||
currentpPass = overrides.PrivateKeyPassphraseAttribute
|
||||
}
|
||||
|
||||
switch {
|
||||
@@ -801,6 +810,13 @@ func getMappingUpdates(credentialType credential.Type, current vault.MappingOver
|
||||
default:
|
||||
ret[privateKeyAttribute] = currentPk
|
||||
}
|
||||
|
||||
switch {
|
||||
case masks[pkPassphraseAttribute]:
|
||||
ret[pkPassphraseAttribute] = new[pkPassphraseAttribute]
|
||||
default:
|
||||
ret[pkPassphraseAttribute] = currentpPass
|
||||
}
|
||||
}
|
||||
|
||||
return ret, true
|
||||
|
||||
+144
-15
@@ -542,6 +542,92 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create a valid vault CredentialLibrary ssh_private_key type",
|
||||
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("something"),
|
||||
},
|
||||
},
|
||||
CredentialType: string(credential.SshPrivateKeyType),
|
||||
}},
|
||||
idPrefix: vault.CredentialLibraryPrefix + "_",
|
||||
res: &pbs.CreateCredentialLibraryResponse{
|
||||
Uri: fmt.Sprintf("credential-libraries/%s_", vault.CredentialLibraryPrefix),
|
||||
Item: &pb.CredentialLibrary{
|
||||
Id: store.GetPublicId(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
||||
Version: 1,
|
||||
Type: vault.Subtype.String(),
|
||||
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("something"),
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: string(credential.SshPrivateKeyType),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create a valid vault CredentialLibrary ssh_private_key type with mapping",
|
||||
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("something"),
|
||||
},
|
||||
},
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]interface{}{
|
||||
usernameAttribute: "user-test",
|
||||
privateKeyAttribute: "pk-test",
|
||||
pkPassphraseAttribute: "pass-test",
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}(),
|
||||
CredentialType: string(credential.SshPrivateKeyType),
|
||||
}},
|
||||
idPrefix: vault.CredentialLibraryPrefix + "_",
|
||||
res: &pbs.CreateCredentialLibraryResponse{
|
||||
Uri: fmt.Sprintf("credential-libraries/%s_", vault.CredentialLibraryPrefix),
|
||||
Item: &pb.CredentialLibrary{
|
||||
Id: store.GetPublicId(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
||||
Version: 1,
|
||||
Type: vault.Subtype.String(),
|
||||
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("something"),
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: string(credential.SshPrivateKeyType),
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]interface{}{
|
||||
usernameAttribute: "user-test",
|
||||
privateKeyAttribute: "pk-test",
|
||||
pkPassphraseAttribute: "pass-test",
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -624,6 +710,7 @@ func TestGet(t *testing.T) {
|
||||
vault.NewSshPrivateKeyOverride(
|
||||
vault.WithOverrideUsernameAttribute("user"),
|
||||
vault.WithOverridePrivateKeyAttribute("pk"),
|
||||
vault.WithOverridePrivateKeyPassphraseAttribute("pass"),
|
||||
)))
|
||||
require.NoError(t, err)
|
||||
sshPkLib, err := repo.CreateCredentialLibrary(context.Background(), prj.GetPublicId(), lib1)
|
||||
@@ -711,8 +798,9 @@ func TestGet(t *testing.T) {
|
||||
CredentialType: "ssh_private_key",
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]interface{}{
|
||||
usernameAttribute: "user",
|
||||
privateKeyAttribute: "pk",
|
||||
usernameAttribute: "user",
|
||||
privateKeyAttribute: "pk",
|
||||
pkPassphraseAttribute: "pass",
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
@@ -864,6 +952,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)
|
||||
passphraseAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, pkPassphraseAttribute)
|
||||
|
||||
successCases := []struct {
|
||||
name string
|
||||
@@ -1167,6 +1256,7 @@ func TestUpdate(t *testing.T) {
|
||||
vault.NewSshPrivateKeyOverride(
|
||||
vault.WithOverrideUsernameAttribute("orig-user"),
|
||||
vault.WithOverridePrivateKeyAttribute("orig-pk"),
|
||||
vault.WithOverridePrivateKeyPassphraseAttribute("orig-pass"),
|
||||
)),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
@@ -1196,6 +1286,7 @@ func TestUpdate(t *testing.T) {
|
||||
vault.NewSshPrivateKeyOverride(
|
||||
vault.WithOverrideUsernameAttribute("orig-user"),
|
||||
vault.WithOverridePrivateKeyAttribute("orig-pk"),
|
||||
vault.WithOverridePrivateKeyPassphraseAttribute("orig-pass"),
|
||||
)),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
@@ -1218,22 +1309,54 @@ func TestUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ssh-private-key-attributes-change-username-and-private-key-attributes",
|
||||
name: "ssh-private-key-attributes-change-passphrase-attribute",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("ssh_private_key"),
|
||||
vault.WithMappingOverride(
|
||||
vault.NewSshPrivateKeyOverride(
|
||||
vault.WithOverrideUsernameAttribute("orig-user"),
|
||||
vault.WithOverridePrivateKeyAttribute("orig-pk"),
|
||||
vault.WithOverridePrivateKeyPassphraseAttribute("orig-pass"),
|
||||
)),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField),
|
||||
UpdateMask: fieldmask(passphraseAttrField),
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]interface{}{
|
||||
usernameAttribute: "changed-user",
|
||||
privateKeyAttribute: "changed-pk",
|
||||
pkPassphraseAttribute: "changed-pass",
|
||||
}
|
||||
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[pkPassphraseAttribute] = structpb.NewStringValue("changed-pass")
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ssh-private-key-attributes-change-all-attributes",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("ssh_private_key"),
|
||||
vault.WithMappingOverride(
|
||||
vault.NewSshPrivateKeyOverride(
|
||||
vault.WithOverrideUsernameAttribute("orig-user"),
|
||||
vault.WithOverridePrivateKeyAttribute("orig-pk"),
|
||||
vault.WithOverridePrivateKeyPassphraseAttribute("orig-pass"),
|
||||
)),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField, passphraseAttrField),
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]interface{}{
|
||||
usernameAttribute: "changed-user",
|
||||
privateKeyAttribute: "changed-pk",
|
||||
pkPassphraseAttribute: "changed-pass",
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
@@ -1245,21 +1368,23 @@ func TestUpdate(t *testing.T) {
|
||||
out := proto.Clone(in).(*pb.CredentialLibrary)
|
||||
out.CredentialMappingOverrides.Fields[usernameAttribute] = structpb.NewStringValue("changed-user")
|
||||
out.CredentialMappingOverrides.Fields[privateKeyAttribute] = structpb.NewStringValue("changed-pk")
|
||||
out.CredentialMappingOverrides.Fields[pkPassphraseAttribute] = structpb.NewStringValue("changed-pass")
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no-mapping-override-change-username-and-private-key-attributes",
|
||||
name: "no-mapping-override-change-all-attributes",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("ssh_private_key"),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField),
|
||||
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField, passphraseAttrField),
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]interface{}{
|
||||
usernameAttribute: "new-user",
|
||||
privateKeyAttribute: "new-pk",
|
||||
usernameAttribute: "new-user",
|
||||
privateKeyAttribute: "new-pk",
|
||||
pkPassphraseAttribute: "new-pass",
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
@@ -1270,8 +1395,9 @@ func TestUpdate(t *testing.T) {
|
||||
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
out := proto.Clone(in).(*pb.CredentialLibrary)
|
||||
v := map[string]interface{}{
|
||||
usernameAttribute: "new-user",
|
||||
privateKeyAttribute: "new-pk",
|
||||
usernameAttribute: "new-user",
|
||||
privateKeyAttribute: "new-pk",
|
||||
pkPassphraseAttribute: "new-pass",
|
||||
}
|
||||
var err error
|
||||
out.CredentialMappingOverrides, err = structpb.NewStruct(v)
|
||||
@@ -1287,6 +1413,7 @@ func TestUpdate(t *testing.T) {
|
||||
vault.NewSshPrivateKeyOverride(
|
||||
vault.WithOverrideUsernameAttribute("orig-user"),
|
||||
vault.WithOverridePrivateKeyAttribute("orig-pk"),
|
||||
vault.WithOverridePrivateKeyPassphraseAttribute("orig-pass"),
|
||||
)),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
@@ -1326,15 +1453,17 @@ func TestUpdate(t *testing.T) {
|
||||
vault.NewSshPrivateKeyOverride(
|
||||
vault.WithOverrideUsernameAttribute("orig-user"),
|
||||
vault.WithOverridePrivateKeyAttribute("orig-pk"),
|
||||
vault.WithOverridePrivateKeyPassphraseAttribute("orig-pass"),
|
||||
)),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField),
|
||||
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField, passphraseAttrField),
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]interface{}{
|
||||
usernameAttribute: nil,
|
||||
privateKeyAttribute: nil,
|
||||
usernameAttribute: nil,
|
||||
privateKeyAttribute: nil,
|
||||
pkPassphraseAttribute: nil,
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -2,6 +2,7 @@ package credentials
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
@@ -27,10 +28,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
usernameField = "attributes.username"
|
||||
passwordField = "attributes.password"
|
||||
privateKeyField = "attributes.private_key"
|
||||
domain = "credential"
|
||||
usernameField = "attributes.username"
|
||||
passwordField = "attributes.password"
|
||||
privateKeyField = "attributes.private_key"
|
||||
privateKeyPassphraseField = "attributes.private_key_passphrase"
|
||||
domain = "credential"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -409,6 +411,16 @@ func (s Service) updateInRepo(
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to convert to ssh private key storage credential"))
|
||||
}
|
||||
if cred.PassphraseUnneeded {
|
||||
// This happens when we have a private key given and no passphrase
|
||||
// given and everything parses correctly. In that case we want to
|
||||
// ensure that if a passphrase was in the database for the previous
|
||||
// key that we get rid of it. We'll have nilled out several values
|
||||
// above. Note that adding the passphrase field will, once we get to
|
||||
// the repo, result in the mask for the other two related fields as
|
||||
// well.
|
||||
dbMasks = append(dbMasks, static.PrivateKeyPassphraseField)
|
||||
}
|
||||
cred.PublicId = id
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
@@ -553,8 +565,9 @@ func toProto(in credential.Static, opt ...handlers.Option) (*pb.Credential, erro
|
||||
if outputFields.Has(globals.AttributesField) {
|
||||
out.Attrs = &pb.Credential_SshPrivateKeyAttributes{
|
||||
SshPrivateKeyAttributes: &pb.SshPrivateKeyAttributes{
|
||||
Username: wrapperspb.String(cred.GetUsername()),
|
||||
PrivateKeyHmac: base64.RawURLEncoding.EncodeToString(cred.GetPrivateKeyHmac()),
|
||||
Username: wrapperspb.String(cred.GetUsername()),
|
||||
PrivateKeyHmac: base64.RawURLEncoding.EncodeToString(cred.GetPrivateKeyHmac()),
|
||||
PrivateKeyPassphraseHmac: base64.RawURLEncoding.EncodeToString(cred.GetPrivateKeyPassphraseHmac()),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -596,6 +609,9 @@ func toSshPrivateKeyStorageCredential(ctx context.Context, storeId string, in *p
|
||||
}
|
||||
|
||||
attrs := in.GetSshPrivateKeyAttributes()
|
||||
if attrs.GetPrivateKeyPassphrase() != nil {
|
||||
opts = append(opts, static.WithPrivateKeyPassphrase([]byte(attrs.GetPrivateKeyPassphrase().GetValue())))
|
||||
}
|
||||
cs, err := static.NewSshPrivateKeyCredential(
|
||||
ctx,
|
||||
storeId,
|
||||
@@ -645,16 +661,27 @@ func validateCreateRequest(req *pbs.CreateCredentialRequest) error {
|
||||
badFields[usernameField] = "Field required for creating an SSH private key credential."
|
||||
}
|
||||
privateKey := req.Item.GetSshPrivateKeyAttributes().GetPrivateKey().GetValue()
|
||||
passphrase := req.Item.GetSshPrivateKeyAttributes().GetPrivateKeyPassphrase().GetValue()
|
||||
if privateKey == "" {
|
||||
badFields[privateKeyField] = "Field required for creating an SSH private key credential."
|
||||
} else {
|
||||
_, err := ssh.ParsePrivateKey([]byte(privateKey))
|
||||
switch {
|
||||
case err == nil:
|
||||
case err.Error() == (&ssh.PassphraseMissingError{}).Error():
|
||||
// This is okay, if it's brokered and the client can use it, no worries
|
||||
switch passphrase {
|
||||
case "":
|
||||
if _, err := ssh.ParsePrivateKey([]byte(privateKey)); err != nil {
|
||||
badFields[privateKeyField] = fmt.Sprintf("Unable to parse given private key value: %v.", err)
|
||||
}
|
||||
default:
|
||||
badFields[privateKeyField] = "Unable to parse given private key value."
|
||||
if _, err := ssh.ParsePrivateKeyWithPassphrase([]byte(privateKey), []byte(passphrase)); err != nil {
|
||||
if errors.Is(err, x509.IncorrectPasswordError) {
|
||||
badFields[privateKeyPassphraseField] = "Incorrect private key passphrase."
|
||||
} else {
|
||||
if _, err := ssh.ParsePrivateKey([]byte(privateKey)); err == nil {
|
||||
badFields[privateKeyPassphraseField] = "Passphrase supplied for unencrypted key."
|
||||
} else {
|
||||
badFields[privateKeyField] = fmt.Sprintf("Unable to parse given private key value: %v.", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -669,9 +696,8 @@ func validateCreateRequest(req *pbs.CreateCredentialRequest) error {
|
||||
func validateUpdateRequest(req *pbs.UpdateCredentialRequest) error {
|
||||
return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string {
|
||||
badFields := map[string]string{}
|
||||
switch req.GetItem().GetType() {
|
||||
case "":
|
||||
case credential.UsernamePasswordSubtype.String():
|
||||
switch subtypes.SubtypeFromId(domain, req.GetId()) {
|
||||
case credential.UsernamePasswordSubtype:
|
||||
attrs := req.GetItem().GetUsernamePasswordAttributes()
|
||||
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), usernameField) && attrs.GetUsername().GetValue() == "" {
|
||||
badFields[usernameField] = "This is a required field and cannot be set to empty."
|
||||
@@ -680,27 +706,40 @@ func validateUpdateRequest(req *pbs.UpdateCredentialRequest) error {
|
||||
badFields[passwordField] = "This is a required field and cannot be set to empty."
|
||||
}
|
||||
|
||||
case credential.SshPrivateKeySubtype.String():
|
||||
case credential.SshPrivateKeySubtype:
|
||||
attrs := req.GetItem().GetSshPrivateKeyAttributes()
|
||||
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), usernameField) && attrs.GetUsername().GetValue() == "" {
|
||||
badFields[usernameField] = "This is a required field and cannot be set to empty."
|
||||
}
|
||||
privateKey := attrs.GetPrivateKey().GetValue()
|
||||
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), privateKeyField) && privateKey == "" {
|
||||
badFields[privateKeyField] = "This is a required field and cannot be set to empty."
|
||||
} else {
|
||||
_, err := ssh.ParsePrivateKey([]byte(privateKey))
|
||||
switch {
|
||||
case err == nil:
|
||||
case err.Error() == (&ssh.PassphraseMissingError{}).Error():
|
||||
// This is okay, if it's brokered and the client can use it, no worries
|
||||
default:
|
||||
badFields[privateKeyField] = "Unable to parse given private key value."
|
||||
passphrase := attrs.GetPrivateKeyPassphrase().GetValue()
|
||||
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), privateKeyField) {
|
||||
if privateKey == "" {
|
||||
badFields[privateKeyField] = "This is a required field and cannot be set to empty."
|
||||
} else {
|
||||
switch passphrase {
|
||||
case "":
|
||||
if _, err := ssh.ParsePrivateKey([]byte(privateKey)); err != nil {
|
||||
badFields[privateKeyField] = fmt.Sprintf("Unable to parse given private key value: %v.", err)
|
||||
}
|
||||
default:
|
||||
if _, err := ssh.ParsePrivateKeyWithPassphrase([]byte(privateKey), []byte(passphrase)); err != nil {
|
||||
if errors.Is(err, x509.IncorrectPasswordError) {
|
||||
badFields[privateKeyPassphraseField] = "Incorrect private key passphrase."
|
||||
} else {
|
||||
if _, err := ssh.ParsePrivateKey([]byte(privateKey)); err == nil {
|
||||
badFields[privateKeyPassphraseField] = "Passphrase supplied for unencrypted key."
|
||||
} else {
|
||||
badFields[privateKeyField] = fmt.Sprintf("Unable to parse given private key value: %v.", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
badFields[globals.TypeField] = "Cannot modify resource type."
|
||||
badFields[globals.IdField] = "Unknown credential type."
|
||||
}
|
||||
|
||||
return badFields
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
scopepb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/crypto/ssh/testdata"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
@@ -205,6 +206,15 @@ func TestGet(t *testing.T) {
|
||||
spkHm, err := crypto.HmacSha256(context.Background(), []byte(static.TestSshPrivateKeyPem), databaseWrapper, []byte(store.GetPublicId()), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
spkCredWithPass := static.TestSshPrivateKeyCredential(t, conn, wrapper,
|
||||
"user", string(testdata.PEMEncryptedKeys[0].PEMBytes),
|
||||
store.GetPublicId(), prj.GetPublicId(),
|
||||
static.WithPrivateKeyPassphrase([]byte(testdata.PEMEncryptedKeys[0].EncryptionKey)))
|
||||
spkWithPassHm, err := crypto.HmacSha256(context.Background(), []byte(testdata.PEMEncryptedKeys[0].PEMBytes), databaseWrapper, []byte(store.GetPublicId()), nil)
|
||||
require.NoError(t, err)
|
||||
passHm, err := crypto.HmacSha256(context.Background(), []byte(testdata.PEMEncryptedKeys[0].EncryptionKey), databaseWrapper, []byte(store.GetPublicId()), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
id string
|
||||
@@ -277,6 +287,29 @@ func TestGet(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success-spk-with-pass",
|
||||
id: spkCredWithPass.GetPublicId(),
|
||||
res: &pbs.GetCredentialResponse{
|
||||
Item: &pb.Credential{
|
||||
Id: spkCredWithPass.GetPublicId(),
|
||||
CredentialStoreId: spkCredWithPass.GetStoreId(),
|
||||
Scope: &scopepb.ScopeInfo{Id: store.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
||||
Type: credential.SshPrivateKeySubtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
CreatedTime: spkCredWithPass.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: spkCredWithPass.UpdateTime.GetTimestamp(),
|
||||
Version: 1,
|
||||
Attrs: &pb.Credential_SshPrivateKeyAttributes{
|
||||
SshPrivateKeyAttributes: &pb.SshPrivateKeyAttributes{
|
||||
Username: wrapperspb.String("user"),
|
||||
PrivateKeyHmac: base64.RawURLEncoding.EncodeToString([]byte(spkWithPassHm)),
|
||||
PrivateKeyPassphraseHmac: base64.RawURLEncoding.EncodeToString([]byte(passHm)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "not found error",
|
||||
id: fmt.Sprintf("%s_1234567890", credential.UsernamePasswordCredentialPrefix),
|
||||
@@ -576,6 +609,34 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-spk-with-passphrase",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: credential.SshPrivateKeySubtype.String(),
|
||||
Attrs: &pb.Credential_SshPrivateKeyAttributes{
|
||||
SshPrivateKeyAttributes: &pb.SshPrivateKeyAttributes{
|
||||
Username: wrapperspb.String("username"),
|
||||
PrivateKey: wrapperspb.String(string(testdata.PEMEncryptedKeys[0].PEMBytes)),
|
||||
PrivateKeyPassphrase: wrapperspb.String(testdata.PEMEncryptedKeys[0].EncryptionKey),
|
||||
},
|
||||
},
|
||||
}},
|
||||
idPrefix: credential.SshPrivateKeyCredentialPrefix + "_",
|
||||
res: &pbs.CreateCredentialResponse{
|
||||
Uri: fmt.Sprintf("credentials/%s_", credential.SshPrivateKeyCredentialPrefix),
|
||||
Item: &pb.Credential{
|
||||
Id: store.GetPublicId(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
||||
Version: 1,
|
||||
Type: credential.SshPrivateKeySubtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -623,7 +684,8 @@ func TestCreate(t *testing.T) {
|
||||
assert.Empty(got.GetItem().GetUsernamePasswordAttributes().GetPassword())
|
||||
|
||||
case credential.SshPrivateKeySubtype.String():
|
||||
hm, err := crypto.HmacSha256(context.Background(), []byte(static.TestSshPrivateKeyPem), databaseWrapper, []byte(store.GetPublicId()), nil)
|
||||
pk := tc.req.GetItem().GetSshPrivateKeyAttributes().GetPrivateKey().GetValue()
|
||||
hm, err := crypto.HmacSha256(context.Background(), []byte(pk), databaseWrapper, []byte(store.GetPublicId()), nil)
|
||||
require.NoError(err)
|
||||
|
||||
// Validate attributes equal
|
||||
@@ -632,6 +694,14 @@ func TestCreate(t *testing.T) {
|
||||
assert.Equal(base64.RawURLEncoding.EncodeToString([]byte(hm)), got.GetItem().GetSshPrivateKeyAttributes().GetPrivateKeyHmac())
|
||||
assert.Empty(got.GetItem().GetSshPrivateKeyAttributes().GetPrivateKey())
|
||||
|
||||
if pass := tc.req.GetItem().GetSshPrivateKeyAttributes().GetPrivateKeyPassphrase().GetValue(); pass != "" {
|
||||
hm, err := crypto.HmacSha256(context.Background(), []byte(pass), databaseWrapper, []byte(store.GetPublicId()), nil)
|
||||
require.NoError(err)
|
||||
|
||||
assert.Equal(base64.RawURLEncoding.EncodeToString([]byte(hm)), got.GetItem().GetSshPrivateKeyAttributes().GetPrivateKeyPassphraseHmac())
|
||||
assert.Empty(got.GetItem().GetSshPrivateKeyAttributes().GetPrivateKeyPassphrase())
|
||||
}
|
||||
|
||||
default:
|
||||
require.Fail("unknown type")
|
||||
}
|
||||
@@ -708,10 +778,11 @@ func TestUpdate(t *testing.T) {
|
||||
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.GetPublicId(), kms.KeyPurposeDatabase)
|
||||
require.NoError(t, err)
|
||||
|
||||
successCases := []struct {
|
||||
name string
|
||||
req *pbs.UpdateCredentialRequest
|
||||
res func(cred *pb.Credential) *pb.Credential
|
||||
successFailCases := []struct {
|
||||
name string
|
||||
req *pbs.UpdateCredentialRequest
|
||||
res func(cred *pb.Credential) *pb.Credential
|
||||
expErrorContains string
|
||||
}{
|
||||
{
|
||||
name: "name",
|
||||
@@ -897,9 +968,87 @@ func TestUpdate(t *testing.T) {
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "update-spk-with-passphrase",
|
||||
req: &pbs.UpdateCredentialRequest{
|
||||
UpdateMask: fieldmask("attributes.private_key", "attributes.private_key_passphrase"),
|
||||
Item: &pb.Credential{
|
||||
Attrs: &pb.Credential_SshPrivateKeyAttributes{
|
||||
SshPrivateKeyAttributes: &pb.SshPrivateKeyAttributes{
|
||||
PrivateKey: wrapperspb.String(string(testdata.PEMEncryptedKeys[0].PEMBytes)),
|
||||
PrivateKeyPassphrase: wrapperspb.String(testdata.PEMEncryptedKeys[0].EncryptionKey),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: func(in *pb.Credential) *pb.Credential {
|
||||
out := proto.Clone(in).(*pb.Credential)
|
||||
|
||||
hm, err := crypto.HmacSha256(context.Background(), testdata.PEMEncryptedKeys[0].PEMBytes, databaseWrapper, []byte(store.GetPublicId()), nil)
|
||||
require.NoError(t, err)
|
||||
out.GetSshPrivateKeyAttributes().PrivateKeyHmac = base64.RawURLEncoding.EncodeToString([]byte(hm))
|
||||
|
||||
hm, err = crypto.HmacSha256(context.Background(), []byte(testdata.PEMEncryptedKeys[0].EncryptionKey), databaseWrapper, []byte(store.GetPublicId()), nil)
|
||||
require.NoError(t, err)
|
||||
out.GetSshPrivateKeyAttributes().PrivateKeyPassphraseHmac = base64.RawURLEncoding.EncodeToString([]byte(hm))
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "update-spk-with-bad-passphrase",
|
||||
req: &pbs.UpdateCredentialRequest{
|
||||
UpdateMask: fieldmask("attributes.private_key", "attributes.private_key_passphrase"),
|
||||
Item: &pb.Credential{
|
||||
Attrs: &pb.Credential_SshPrivateKeyAttributes{
|
||||
SshPrivateKeyAttributes: &pb.SshPrivateKeyAttributes{
|
||||
PrivateKey: wrapperspb.String(string(testdata.PEMEncryptedKeys[0].PEMBytes)),
|
||||
PrivateKeyPassphrase: wrapperspb.String(strings.ToLower(testdata.PEMEncryptedKeys[0].EncryptionKey)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expErrorContains: "Incorrect private key passphrase",
|
||||
},
|
||||
{
|
||||
name: "update-non-passphrase-spk-with-passphrase",
|
||||
req: &pbs.UpdateCredentialRequest{
|
||||
UpdateMask: fieldmask("attributes.private_key"),
|
||||
Item: &pb.Credential{
|
||||
Attrs: &pb.Credential_SshPrivateKeyAttributes{
|
||||
SshPrivateKeyAttributes: &pb.SshPrivateKeyAttributes{
|
||||
PrivateKey: wrapperspb.String(static.TestSshPrivateKeyPem),
|
||||
PrivateKeyPassphrase: wrapperspb.String(testdata.PEMEncryptedKeys[0].EncryptionKey),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expErrorContains: "Passphrase supplied for unencrypted key",
|
||||
},
|
||||
{
|
||||
name: "update-non-passphrase-spk-with-no-passphrase",
|
||||
req: &pbs.UpdateCredentialRequest{
|
||||
UpdateMask: fieldmask("attributes.private_key"),
|
||||
Item: &pb.Credential{
|
||||
Attrs: &pb.Credential_SshPrivateKeyAttributes{
|
||||
SshPrivateKeyAttributes: &pb.SshPrivateKeyAttributes{
|
||||
PrivateKey: wrapperspb.String(static.TestSshPrivateKeyPem),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: func(in *pb.Credential) *pb.Credential {
|
||||
out := proto.Clone(in).(*pb.Credential)
|
||||
|
||||
hm, err := crypto.HmacSha256(context.Background(), []byte(static.TestSshPrivateKeyPem), databaseWrapper, []byte(store.GetPublicId()), nil)
|
||||
require.NoError(t, err)
|
||||
out.GetSshPrivateKeyAttributes().PrivateKeyHmac = base64.RawURLEncoding.EncodeToString([]byte(hm))
|
||||
out.GetSshPrivateKeyAttributes().PrivateKeyPassphraseHmac = ""
|
||||
return out
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range successCases {
|
||||
for _, tc := range successFailCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
var cred credential.Static
|
||||
@@ -920,12 +1069,18 @@ func TestUpdate(t *testing.T) {
|
||||
}
|
||||
resToChange, err := s.GetCredential(ctx, &pbs.GetCredentialRequest{Id: cred.GetPublicId()})
|
||||
require.NoError(err)
|
||||
want := &pbs.UpdateCredentialResponse{Item: tc.res(resToChange.GetItem())}
|
||||
|
||||
got, gErr := s.UpdateCredential(ctx, tc.req)
|
||||
if tc.expErrorContains != "" {
|
||||
require.NotNil(gErr)
|
||||
assert.Contains(gErr.Error(), tc.expErrorContains)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
require.NotNil(got)
|
||||
|
||||
want := &pbs.UpdateCredentialResponse{Item: tc.res(resToChange.GetItem())}
|
||||
|
||||
gotUpdateTime := got.GetItem().GetUpdatedTime()
|
||||
created := cred.GetCreateTime().GetTimestamp()
|
||||
assert.True(gotUpdateTime.AsTime().After(created.AsTime()), "Should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created)
|
||||
|
||||
@@ -38,8 +38,9 @@ func dynamicToWorkerCredential(ctx context.Context, cred credential.Dynamic) (se
|
||||
workerCred = &serverpb.Credential{
|
||||
Credential: &serverpb.Credential_SshPrivateKey{
|
||||
SshPrivateKey: &serverpb.SshPrivateKey{
|
||||
Username: c.Username(),
|
||||
PrivateKey: string(c.PrivateKey()),
|
||||
Username: c.Username(),
|
||||
PrivateKey: string(c.PrivateKey()),
|
||||
PrivateKeyPassphrase: string(c.PrivateKeyPassphrase()),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -103,8 +104,9 @@ func dynamicToSessionCredential(ctx context.Context, cred credential.Dynamic) (*
|
||||
case credential.SshPrivateKey:
|
||||
credData, err = handlers.ProtoToStruct(
|
||||
&pb.SshPrivateKeyCredential{
|
||||
Username: c.Username(),
|
||||
PrivateKey: string(c.PrivateKey()),
|
||||
Username: c.Username(),
|
||||
PrivateKey: string(c.PrivateKey()),
|
||||
PrivateKeyPassphrase: string(c.PrivateKeyPassphrase()),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -153,8 +155,9 @@ func staticToWorkerCredential(ctx context.Context, cred credential.Static) (sess
|
||||
workerCred = &serverpb.Credential{
|
||||
Credential: &serverpb.Credential_SshPrivateKey{
|
||||
SshPrivateKey: &serverpb.SshPrivateKey{
|
||||
Username: c.GetUsername(),
|
||||
PrivateKey: string(c.GetPrivateKey()),
|
||||
Username: c.GetUsername(),
|
||||
PrivateKey: string(c.GetPrivateKey()),
|
||||
PrivateKeyPassphrase: string(c.GetPrivateKeyPassphrase()),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -199,14 +202,18 @@ func staticToSessionCredential(ctx context.Context, cred credential.Static) (*pb
|
||||
credType = string(credential.SshPrivateKeyType)
|
||||
credData, err = handlers.ProtoToStruct(
|
||||
&pb.SshPrivateKeyCredential{
|
||||
Username: c.GetUsername(),
|
||||
PrivateKey: string(c.GetPrivateKey()),
|
||||
Username: c.GetUsername(),
|
||||
PrivateKey: string(c.GetPrivateKey()),
|
||||
PrivateKeyPassphrase: string(c.GetPrivateKeyPassphrase()),
|
||||
},
|
||||
)
|
||||
secret = map[string]interface{}{
|
||||
"username": c.GetUsername(),
|
||||
"private_key": string(c.GetPrivateKey()),
|
||||
}
|
||||
if len(c.GetPrivateKeyPassphrase()) > 0 {
|
||||
secret["private_key_passphrase"] = string(c.GetPrivateKeyPassphrase())
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for ssh private key credential"))
|
||||
}
|
||||
|
||||
@@ -2772,7 +2772,23 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, sshPkCredResp)
|
||||
|
||||
// Create secret in vault with default username and password fields
|
||||
sshPkWithPassCredResp, err := credService.CreateCredential(ctx, &pbs.CreateCredentialRequest{Item: &credpb.Credential{
|
||||
CredentialStoreId: staticStore.GetPublicId(),
|
||||
Type: credential.SshPrivateKeySubtype.String(),
|
||||
Name: wrapperspb.String("Cred SSH With Pass Name"),
|
||||
Description: wrapperspb.String("Cred SSH Description"),
|
||||
Attrs: &credpb.Credential_SshPrivateKeyAttributes{
|
||||
SshPrivateKeyAttributes: &credpb.SshPrivateKeyAttributes{
|
||||
Username: wrapperspb.String("static-username"),
|
||||
PrivateKey: wrapperspb.String(string(testdata.PEMEncryptedKeys[0].PEMBytes)),
|
||||
PrivateKeyPassphrase: wrapperspb.String(testdata.PEMEncryptedKeys[0].EncryptionKey),
|
||||
},
|
||||
},
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, sshPkWithPassCredResp)
|
||||
|
||||
// Create secret in vault with default username and private key fields
|
||||
defaultSshPrivateKey := v.CreateKVSecret(t, "default-sshpk", []byte(`{"data": {"username": "my-user", "private_key": "my-pk"}}`))
|
||||
require.NotNil(t, defaultSshPrivateKey)
|
||||
|
||||
@@ -2790,7 +2806,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create secret in vault with non default username and password fields
|
||||
// Create secret in vault with non default username and private key 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)
|
||||
|
||||
@@ -2812,6 +2828,49 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create secret in vault with default username, private key and passphrase fields
|
||||
defaultSshPrivateKeyWithPass := v.CreateKVSecret(t, "default-sshpk-with-pass", []byte(`{"data": {"username": "my-user", "private_key": "my-pk", "private_key_passphrase": "my-pass"}}`))
|
||||
require.NotNil(t, defaultSshPrivateKeyWithPass)
|
||||
|
||||
clsRespSshPrivateKeyWithPass, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("SSH Private Key With Passphrase 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-with-pass")),
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: "ssh_private_key",
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, clsRespSshPrivateKeyWithPass)
|
||||
|
||||
// Create secret in vault with non default username, private key and private key passphrase fields
|
||||
nonDefaultSshPrivateKeyWithPass := v.CreateKVSecret(t, "non-default-sshpk-with-pass", []byte(`{"data": {"non-default-user": "my-user", "non-default-pk": "my-special-pk", "embedded": {"secret-pass": "my-special-pass"}}}}`))
|
||||
require.NotNil(t, nonDefaultSshPrivateKeyWithPass)
|
||||
|
||||
clsRespSshPrivateKeyWithPassWithMapping, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("SSH Private Key With Passphrase 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-with-pass")),
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: "ssh_private_key",
|
||||
CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{
|
||||
"username_attribute": structpb.NewStringValue("/data/non-default-user"),
|
||||
"private_key_attribute": structpb.NewStringValue("/data/non-default-pk"),
|
||||
"private_key_passphrase_attribute": structpb.NewStringValue("/data/embedded/secret-pass"),
|
||||
}},
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, clsRespSshPrivateKeyWithPassWithMapping)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
hostSourceId string
|
||||
@@ -2992,6 +3051,87 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "static-ssh-private-key-with-pass",
|
||||
hostSourceId: shs.GetPublicId(),
|
||||
credSourceId: sshPkWithPassCredResp.GetItem().GetId(),
|
||||
wantedHostId: h.GetPublicId(),
|
||||
wantedEndpoint: h.GetAddress(),
|
||||
wantedCred: &pb.SessionCredential{
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: sshPkWithPassCredResp.GetItem().GetId(),
|
||||
Name: sshPkWithPassCredResp.GetItem().GetName().GetValue(),
|
||||
Description: sshPkWithPassCredResp.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: staticStore.GetPublicId(),
|
||||
Type: credstatic.Subtype.String(),
|
||||
CredentialType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
Credential: func() *structpb.Struct {
|
||||
data := map[string]interface{}{
|
||||
"private_key_passphrase": testdata.PEMEncryptedKeys[0].EncryptionKey,
|
||||
"private_key": string(testdata.PEMEncryptedKeys[0].PEMBytes),
|
||||
"username": "static-username",
|
||||
}
|
||||
st, err := structpb.NewStruct(data)
|
||||
require.NoError(t, err)
|
||||
return st
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vault-ssh-private-key-with-pass",
|
||||
hostSourceId: shs.GetPublicId(),
|
||||
credSourceId: clsRespSshPrivateKeyWithPass.GetItem().GetId(),
|
||||
wantedHostId: h.GetPublicId(),
|
||||
wantedEndpoint: h.GetAddress(),
|
||||
wantedCred: &pb.SessionCredential{
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: clsRespSshPrivateKeyWithPass.GetItem().GetId(),
|
||||
Name: clsRespSshPrivateKeyWithPass.GetItem().GetName().GetValue(),
|
||||
Description: clsRespSshPrivateKeyWithPass.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-pk",
|
||||
"private_key_passphrase": "my-pass",
|
||||
}
|
||||
st, err := structpb.NewStruct(data)
|
||||
require.NoError(t, err)
|
||||
return st
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vault-ssh-private-key-with-pass-with-mapping",
|
||||
hostSourceId: shs.GetPublicId(),
|
||||
credSourceId: clsRespSshPrivateKeyWithPassWithMapping.GetItem().GetId(),
|
||||
wantedHostId: h.GetPublicId(),
|
||||
wantedEndpoint: h.GetAddress(),
|
||||
wantedCred: &pb.SessionCredential{
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: clsRespSshPrivateKeyWithPassWithMapping.GetItem().GetId(),
|
||||
Name: clsRespSshPrivateKeyWithPassWithMapping.GetItem().GetName().GetValue(),
|
||||
Description: clsRespSshPrivateKeyWithPassWithMapping.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",
|
||||
"private_key_passphrase": "my-special-pass",
|
||||
}
|
||||
st, err := structpb.NewStruct(data)
|
||||
require.NoError(t, err)
|
||||
return st
|
||||
}(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
|
||||
Reference in new issue
Block a user