Add some target fields and move some things around (#3879)
In preparation for putting proxy code in the api package this creates some necessary fields in the target output and it pre-moves credential parsing and proxy handshake code to their new places. This will make the diff for the actual proxy code move quite a bit smaller. Co-authored-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
This commit is contained in:
1 parent
82b3f202c0
commit
fb4a5a98dc
27 files changed
+733
-444
No files matched your search
@@ -21,11 +21,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
apiproxy "github.com/hashicorp/boundary/api/proxy"
|
||||
"github.com/hashicorp/boundary/api/targets"
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/proxy"
|
||||
targetspb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/proxy"
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/go-secure-stdlib/base62"
|
||||
"github.com/mitchellh/cli"
|
||||
@@ -824,10 +825,10 @@ func (c *Command) handleExec(passthroughArgs []string) {
|
||||
var envs []string
|
||||
var argsErr error
|
||||
|
||||
var creds credentials
|
||||
var creds apiproxy.Credentials
|
||||
if c.sessionAuthz != nil {
|
||||
var err error
|
||||
creds, err = parseCredentials(c.sessionAuthz.Credentials)
|
||||
creds, err = apiproxy.ParseCredentials(c.sessionAuthz.Credentials)
|
||||
if err != nil {
|
||||
c.PrintCliError(fmt.Errorf("Error interpreting secret: %w", err))
|
||||
c.execCmdReturnValue.Store(int32(3))
|
||||
@@ -884,7 +885,7 @@ func (c *Command) handleExec(passthroughArgs []string) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.printCredentials(creds.unconsumedSessionCredentials()); err != nil {
|
||||
if err := c.printCredentials(creds.UnconsumedSessionCredentials()); err != nil {
|
||||
c.PrintCliError(fmt.Errorf("Failed to print credentials: %w", err))
|
||||
c.execCmdReturnValue.Store(int32(2))
|
||||
return
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package connect
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/hashicorp/boundary/api/targets"
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
type usernamePassword struct {
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
|
||||
raw *targets.SessionCredential
|
||||
consumed bool
|
||||
}
|
||||
|
||||
type sshPrivateKey struct {
|
||||
Username string `mapstructure:"username"`
|
||||
PrivateKey string `mapstructure:"private_key"`
|
||||
Passphrase string `mapstructure:"private_key_passphrase"`
|
||||
|
||||
raw *targets.SessionCredential
|
||||
consumed bool
|
||||
}
|
||||
|
||||
type credentials struct {
|
||||
usernamePassword []usernamePassword
|
||||
sshPrivateKey []sshPrivateKey
|
||||
unspecified []*targets.SessionCredential
|
||||
}
|
||||
|
||||
func (c credentials) unconsumedSessionCredentials() []*targets.SessionCredential {
|
||||
out := make([]*targets.SessionCredential, 0, len(c.sshPrivateKey)+len(c.usernamePassword)+len(c.unspecified))
|
||||
|
||||
// Unspecified credentials cannot be consumed
|
||||
out = append(out, c.unspecified...)
|
||||
|
||||
for _, c := range c.sshPrivateKey {
|
||||
if !c.consumed {
|
||||
out = append(out, c.raw)
|
||||
}
|
||||
}
|
||||
for _, c := range c.usernamePassword {
|
||||
if !c.consumed {
|
||||
out = append(out, c.raw)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func parseCredentials(creds []*targets.SessionCredential) (credentials, error) {
|
||||
if creds == nil {
|
||||
return credentials{}, nil
|
||||
}
|
||||
var out credentials
|
||||
for _, cred := range creds {
|
||||
if cred.CredentialSource == nil {
|
||||
return credentials{}, errors.New("missing credential source")
|
||||
}
|
||||
|
||||
var upCred usernamePassword
|
||||
var spkCred sshPrivateKey
|
||||
switch globals.CredentialType(cred.CredentialSource.CredentialType) {
|
||||
case globals.UsernamePasswordCredentialType:
|
||||
// Decode attributes from credential struct
|
||||
if err := mapstructure.Decode(cred.Credential, &upCred); err != nil {
|
||||
return credentials{}, err
|
||||
}
|
||||
|
||||
if upCred.Username != "" && upCred.Password != "" {
|
||||
upCred.raw = cred
|
||||
out.usernamePassword = append(out.usernamePassword, upCred)
|
||||
continue
|
||||
}
|
||||
|
||||
case globals.SshPrivateKeyCredentialType:
|
||||
// Decode attributes from credential struct
|
||||
if err := mapstructure.Decode(cred.Credential, &spkCred); err != nil {
|
||||
return credentials{}, err
|
||||
}
|
||||
|
||||
if spkCred.Username != "" && spkCred.PrivateKey != "" {
|
||||
spkCred.raw = cred
|
||||
out.sshPrivateKey = append(out.sshPrivateKey, spkCred)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Credential type is unspecified, make a best effort attempt to parse
|
||||
// a credential from the Decoded field if it exists
|
||||
if cred.Secret != nil && cred.Secret.Decoded != nil {
|
||||
// Attempt unmarshaling into username password creds
|
||||
if err := mapstructure.Decode(cred.Secret.Decoded, &upCred); err != nil {
|
||||
return credentials{}, err
|
||||
}
|
||||
if upCred.Username != "" && upCred.Password != "" {
|
||||
upCred.raw = cred
|
||||
out.usernamePassword = append(out.usernamePassword, upCred)
|
||||
continue
|
||||
}
|
||||
|
||||
// Attempt unmarshaling into ssh private key creds
|
||||
if err := mapstructure.Decode(cred.Secret.Decoded, &spkCred); err != nil {
|
||||
return credentials{}, err
|
||||
}
|
||||
if spkCred.Username != "" && spkCred.PrivateKey != "" {
|
||||
spkCred.raw = cred
|
||||
out.sshPrivateKey = append(out.sshPrivateKey, spkCred)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// We could not parse the credential
|
||||
out.unspecified = append(out.unspecified, cred)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
@@ -1,625 +0,0 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package connect
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/api/targets"
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/credential/static"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
typedUsernamePassword = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
CredentialType: string(globals.UsernamePasswordCredentialType),
|
||||
},
|
||||
Credential: map[string]any{
|
||||
"username": "user",
|
||||
"password": "pass",
|
||||
},
|
||||
}
|
||||
|
||||
typedSshPrivateKey = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
||||
},
|
||||
Credential: map[string]any{
|
||||
"username": "user",
|
||||
"private_key": "my-pk",
|
||||
},
|
||||
}
|
||||
|
||||
vaultUsernamePasswordDeprecatedSubtype = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
Type: vault.Subtype.String(),
|
||||
},
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"username": "vault-decoded-user",
|
||||
"password": "vault-decoded-pass",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
vaultSshPrivateKeyDeprecatedSubtype = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
Type: vault.Subtype.String(),
|
||||
},
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"username": "vault-decoded-user",
|
||||
"private_key": "vault-decoded-pk",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
vaultUsernamePassword = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
},
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"username": "vault-decoded-user",
|
||||
"password": "vault-decoded-pass",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
vaultSshPrivateKey = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
},
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"username": "vault-decoded-user",
|
||||
"private_key": "vault-decoded-pk",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
unknownUsernamePassword = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{},
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"username": "unknown-decoded-user",
|
||||
"password": "unknown-decoded-pass",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
unknownSshPrivateKey = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{},
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"username": "unknown-decoded-user",
|
||||
"private_key": "unknown-decoded-pk",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
staticUsernamePassword = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
Type: static.Subtype.String(),
|
||||
},
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"username": "static-decoded-user",
|
||||
"password": "static-decoded-pass",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
staticSshPrivateKey = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
Type: static.Subtype.String(),
|
||||
},
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"username": "static-decoded-user",
|
||||
"private_key": "static-decoded-pk",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
staticKv = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
Type: static.Subtype.String(),
|
||||
CredentialType: "json",
|
||||
CredentialStoreId: "csst_id",
|
||||
Description: "test",
|
||||
Name: "test unspecified json cred",
|
||||
Id: "credjson_id",
|
||||
},
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"secret": map[string]any{
|
||||
"username": "password",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
unspecifiedCred = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
Type: static.Subtype.String(),
|
||||
},
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"username": "decoded-user",
|
||||
"some-value": "decoded-some-value",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
unspecifiedCred1 = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
Type: static.Subtype.String(),
|
||||
},
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"username": "decoded-user",
|
||||
"some-value1": "decoded-some-value1",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func Test_parseCredentials(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
creds []*targets.SessionCredential
|
||||
wantCreds credentials
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no-creds",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no-credential-source",
|
||||
creds: []*targets.SessionCredential{
|
||||
{
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"username": "decoded-user",
|
||||
"private_key": "decoded-pk",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "username-password-typed",
|
||||
creds: []*targets.SessionCredential{
|
||||
typedUsernamePassword,
|
||||
},
|
||||
wantCreds: credentials{
|
||||
usernamePassword: []usernamePassword{
|
||||
{
|
||||
Username: "user",
|
||||
Password: "pass",
|
||||
raw: typedUsernamePassword,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ssh-private-key-typed",
|
||||
creds: []*targets.SessionCredential{
|
||||
typedSshPrivateKey,
|
||||
},
|
||||
wantCreds: credentials{
|
||||
sshPrivateKey: []sshPrivateKey{
|
||||
{
|
||||
Username: "user",
|
||||
PrivateKey: "my-pk",
|
||||
raw: typedSshPrivateKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "vault-username-password-decoded",
|
||||
creds: []*targets.SessionCredential{
|
||||
vaultUsernamePassword,
|
||||
},
|
||||
wantCreds: credentials{
|
||||
usernamePassword: []usernamePassword{
|
||||
{
|
||||
Username: "vault-decoded-user",
|
||||
Password: "vault-decoded-pass",
|
||||
raw: vaultUsernamePassword,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "vault-private-key-decoded",
|
||||
creds: []*targets.SessionCredential{
|
||||
vaultSshPrivateKey,
|
||||
},
|
||||
wantCreds: credentials{
|
||||
sshPrivateKey: []sshPrivateKey{
|
||||
{
|
||||
Username: "vault-decoded-user",
|
||||
PrivateKey: "vault-decoded-pk",
|
||||
raw: vaultSshPrivateKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "vault-deprecated-username-password-decoded",
|
||||
creds: []*targets.SessionCredential{
|
||||
vaultUsernamePasswordDeprecatedSubtype,
|
||||
},
|
||||
wantCreds: credentials{
|
||||
usernamePassword: []usernamePassword{
|
||||
{
|
||||
Username: "vault-decoded-user",
|
||||
Password: "vault-decoded-pass",
|
||||
raw: vaultUsernamePasswordDeprecatedSubtype,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "vault-deprecated-private-key-decoded",
|
||||
creds: []*targets.SessionCredential{
|
||||
vaultSshPrivateKeyDeprecatedSubtype,
|
||||
},
|
||||
wantCreds: credentials{
|
||||
sshPrivateKey: []sshPrivateKey{
|
||||
{
|
||||
Username: "vault-decoded-user",
|
||||
PrivateKey: "vault-decoded-pk",
|
||||
raw: vaultSshPrivateKeyDeprecatedSubtype,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "unknown-username-password-decoded",
|
||||
creds: []*targets.SessionCredential{
|
||||
unknownUsernamePassword,
|
||||
},
|
||||
wantCreds: credentials{
|
||||
usernamePassword: []usernamePassword{
|
||||
{
|
||||
Username: "unknown-decoded-user",
|
||||
Password: "unknown-decoded-pass",
|
||||
raw: unknownUsernamePassword,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "unknown-private-key-decoded",
|
||||
creds: []*targets.SessionCredential{
|
||||
unknownSshPrivateKey,
|
||||
},
|
||||
wantCreds: credentials{
|
||||
sshPrivateKey: []sshPrivateKey{
|
||||
{
|
||||
Username: "unknown-decoded-user",
|
||||
PrivateKey: "unknown-decoded-pk",
|
||||
raw: unknownSshPrivateKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "static-username-password-decoded",
|
||||
creds: []*targets.SessionCredential{
|
||||
staticUsernamePassword,
|
||||
},
|
||||
wantCreds: credentials{
|
||||
usernamePassword: []usernamePassword{
|
||||
{
|
||||
Username: "static-decoded-user",
|
||||
Password: "static-decoded-pass",
|
||||
raw: staticUsernamePassword,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "static-private-key-decoded",
|
||||
creds: []*targets.SessionCredential{
|
||||
staticSshPrivateKey,
|
||||
},
|
||||
wantCreds: credentials{
|
||||
sshPrivateKey: []sshPrivateKey{
|
||||
{
|
||||
Username: "static-decoded-user",
|
||||
PrivateKey: "static-decoded-pk",
|
||||
raw: staticSshPrivateKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "unspecified",
|
||||
creds: []*targets.SessionCredential{
|
||||
unspecifiedCred,
|
||||
},
|
||||
wantCreds: credentials{
|
||||
unspecified: []*targets.SessionCredential{
|
||||
unspecifiedCred,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "unspecified-static-json",
|
||||
creds: []*targets.SessionCredential{
|
||||
staticKv,
|
||||
},
|
||||
wantCreds: credentials{
|
||||
unspecified: []*targets.SessionCredential{
|
||||
staticKv,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "mixed",
|
||||
creds: []*targets.SessionCredential{
|
||||
staticSshPrivateKey, unspecifiedCred1, vaultSshPrivateKey, typedUsernamePassword,
|
||||
unspecifiedCred, vaultUsernamePassword, typedSshPrivateKey, staticUsernamePassword,
|
||||
staticKv,
|
||||
},
|
||||
wantCreds: credentials{
|
||||
sshPrivateKey: []sshPrivateKey{
|
||||
{
|
||||
Username: "static-decoded-user",
|
||||
PrivateKey: "static-decoded-pk",
|
||||
raw: staticSshPrivateKey,
|
||||
},
|
||||
{
|
||||
Username: "vault-decoded-user",
|
||||
PrivateKey: "vault-decoded-pk",
|
||||
raw: vaultSshPrivateKey,
|
||||
},
|
||||
{
|
||||
Username: "user",
|
||||
PrivateKey: "my-pk",
|
||||
raw: typedSshPrivateKey,
|
||||
},
|
||||
},
|
||||
usernamePassword: []usernamePassword{
|
||||
{
|
||||
Username: "static-decoded-user",
|
||||
Password: "static-decoded-pass",
|
||||
raw: staticUsernamePassword,
|
||||
},
|
||||
{
|
||||
Username: "vault-decoded-user",
|
||||
Password: "vault-decoded-pass",
|
||||
raw: vaultUsernamePassword,
|
||||
},
|
||||
{
|
||||
Username: "user",
|
||||
Password: "pass",
|
||||
raw: typedUsernamePassword,
|
||||
},
|
||||
},
|
||||
unspecified: []*targets.SessionCredential{
|
||||
unspecifiedCred, unspecifiedCred1, staticKv,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
creds, err := parseCredentials(tt.creds)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
assert.Empty(creds)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
|
||||
assert.ElementsMatch(tt.wantCreds.usernamePassword, creds.usernamePassword)
|
||||
assert.ElementsMatch(tt.wantCreds.sshPrivateKey, creds.sshPrivateKey)
|
||||
assert.ElementsMatch(tt.wantCreds.unspecified, creds.unspecified)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_unconsumedSessionCredentials(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
creds credentials
|
||||
wantCreds []*targets.SessionCredential
|
||||
}{
|
||||
{
|
||||
name: "no-creds",
|
||||
wantCreds: nil,
|
||||
},
|
||||
{
|
||||
name: "spk-consumed",
|
||||
creds: credentials{
|
||||
sshPrivateKey: []sshPrivateKey{
|
||||
{
|
||||
raw: staticSshPrivateKey,
|
||||
consumed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantCreds: nil,
|
||||
},
|
||||
{
|
||||
name: "spk",
|
||||
creds: credentials{
|
||||
sshPrivateKey: []sshPrivateKey{
|
||||
{
|
||||
raw: staticSshPrivateKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantCreds: []*targets.SessionCredential{staticSshPrivateKey},
|
||||
},
|
||||
{
|
||||
name: "up",
|
||||
creds: credentials{
|
||||
usernamePassword: []usernamePassword{
|
||||
{
|
||||
raw: vaultUsernamePassword,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantCreds: []*targets.SessionCredential{vaultUsernamePassword},
|
||||
},
|
||||
{
|
||||
name: "up-consumed",
|
||||
creds: credentials{
|
||||
usernamePassword: []usernamePassword{
|
||||
{
|
||||
raw: vaultUsernamePassword,
|
||||
consumed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantCreds: nil,
|
||||
},
|
||||
{
|
||||
name: "unspecified",
|
||||
creds: credentials{
|
||||
unspecified: []*targets.SessionCredential{unspecifiedCred},
|
||||
},
|
||||
wantCreds: []*targets.SessionCredential{unspecifiedCred},
|
||||
},
|
||||
{
|
||||
name: "mixed",
|
||||
creds: credentials{
|
||||
sshPrivateKey: []sshPrivateKey{
|
||||
{
|
||||
raw: staticSshPrivateKey,
|
||||
consumed: true,
|
||||
},
|
||||
{
|
||||
raw: vaultSshPrivateKey,
|
||||
},
|
||||
{
|
||||
raw: typedSshPrivateKey,
|
||||
},
|
||||
},
|
||||
usernamePassword: []usernamePassword{
|
||||
{
|
||||
raw: staticUsernamePassword,
|
||||
consumed: true,
|
||||
},
|
||||
{
|
||||
raw: vaultUsernamePassword,
|
||||
},
|
||||
{
|
||||
raw: typedUsernamePassword,
|
||||
consumed: true,
|
||||
},
|
||||
},
|
||||
unspecified: []*targets.SessionCredential{unspecifiedCred, unspecifiedCred1},
|
||||
},
|
||||
wantCreds: []*targets.SessionCredential{
|
||||
vaultSshPrivateKey, typedSshPrivateKey, vaultUsernamePassword, unspecifiedCred, unspecifiedCred1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mixed-all-consumed",
|
||||
creds: credentials{
|
||||
sshPrivateKey: []sshPrivateKey{
|
||||
{
|
||||
raw: staticSshPrivateKey,
|
||||
consumed: true,
|
||||
},
|
||||
{
|
||||
raw: vaultSshPrivateKey,
|
||||
consumed: true,
|
||||
},
|
||||
{
|
||||
raw: typedSshPrivateKey,
|
||||
consumed: true,
|
||||
},
|
||||
},
|
||||
usernamePassword: []usernamePassword{
|
||||
{
|
||||
raw: staticUsernamePassword,
|
||||
consumed: true,
|
||||
},
|
||||
{
|
||||
raw: vaultUsernamePassword,
|
||||
consumed: true,
|
||||
},
|
||||
{
|
||||
raw: typedUsernamePassword,
|
||||
consumed: true,
|
||||
},
|
||||
},
|
||||
unspecified: []*targets.SessionCredential{unspecifiedCred, unspecifiedCred1},
|
||||
},
|
||||
wantCreds: []*targets.SessionCredential{
|
||||
unspecifiedCred1, unspecifiedCred,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mixed-all-unconsumed",
|
||||
creds: credentials{
|
||||
sshPrivateKey: []sshPrivateKey{
|
||||
{
|
||||
raw: staticSshPrivateKey,
|
||||
},
|
||||
{
|
||||
raw: vaultSshPrivateKey,
|
||||
},
|
||||
{
|
||||
raw: typedSshPrivateKey,
|
||||
},
|
||||
},
|
||||
usernamePassword: []usernamePassword{
|
||||
{
|
||||
raw: staticUsernamePassword,
|
||||
},
|
||||
{
|
||||
raw: vaultUsernamePassword,
|
||||
},
|
||||
{
|
||||
raw: typedUsernamePassword,
|
||||
},
|
||||
},
|
||||
unspecified: []*targets.SessionCredential{unspecifiedCred, unspecifiedCred1},
|
||||
},
|
||||
wantCreds: []*targets.SessionCredential{
|
||||
staticSshPrivateKey, unspecifiedCred1, vaultSshPrivateKey, typedUsernamePassword,
|
||||
unspecifiedCred, vaultUsernamePassword, typedSshPrivateKey, staticUsernamePassword,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
creds := tt.creds.unconsumedSessionCredentials()
|
||||
assert.ElementsMatch(tt.wantCreds, creds)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/api/proxy"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
@@ -53,17 +54,17 @@ func (p *postgresFlags) defaultExec() string {
|
||||
return strings.ToLower(p.flagPostgresStyle)
|
||||
}
|
||||
|
||||
func (p *postgresFlags) buildArgs(c *Command, port, ip, _ string, creds credentials) (args, envs []string, retCreds credentials, retErr error) {
|
||||
func (p *postgresFlags) buildArgs(c *Command, port, ip, _ string, creds proxy.Credentials) (args, envs []string, retCreds proxy.Credentials, retErr error) {
|
||||
var username, password string
|
||||
|
||||
retCreds = creds
|
||||
if len(retCreds.usernamePassword) > 0 {
|
||||
if len(retCreds.UsernamePassword) > 0 {
|
||||
// Mark credential as consumed so it is not printed to user
|
||||
retCreds.usernamePassword[0].consumed = true
|
||||
retCreds.UsernamePassword[0].Consumed = true
|
||||
|
||||
// For now just grab the first username password credential brokered
|
||||
username = retCreds.usernamePassword[0].Username
|
||||
password = retCreds.usernamePassword[0].Password
|
||||
username = retCreds.UsernamePassword[0].Username
|
||||
password = retCreds.UsernamePassword[0].Password
|
||||
}
|
||||
|
||||
switch p.flagPostgresStyle {
|
||||
@@ -84,7 +85,7 @@ func (p *postgresFlags) buildArgs(c *Command, port, ip, _ string, creds credenti
|
||||
if password != "" {
|
||||
passfile, err := os.CreateTemp("", "*")
|
||||
if err != nil {
|
||||
return nil, nil, credentials{}, fmt.Errorf("Error saving postgres password to tmp file: %w", err)
|
||||
return nil, nil, proxy.Credentials{}, fmt.Errorf("Error saving postgres password to tmp file: %w", err)
|
||||
}
|
||||
c.cleanupFuncs = append(c.cleanupFuncs, func() error {
|
||||
if err := os.Remove(passfile.Name()); err != nil {
|
||||
@@ -94,10 +95,10 @@ func (p *postgresFlags) buildArgs(c *Command, port, ip, _ string, creds credenti
|
||||
})
|
||||
_, err = passfile.WriteString(fmt.Sprintf("*:*:*:*:%s", password))
|
||||
if err != nil {
|
||||
return nil, nil, credentials{}, fmt.Errorf("Error writing password file to %s: %w", passfile.Name(), err)
|
||||
return nil, nil, proxy.Credentials{}, fmt.Errorf("Error writing password file to %s: %w", passfile.Name(), err)
|
||||
}
|
||||
if err := passfile.Close(); err != nil {
|
||||
return nil, nil, credentials{}, fmt.Errorf("Error closing password file after writing to %s: %w", passfile.Name(), err)
|
||||
return nil, nil, proxy.Credentials{}, fmt.Errorf("Error closing password file after writing to %s: %w", passfile.Name(), err)
|
||||
}
|
||||
envs = append(envs, fmt.Sprintf("PGPASSFILE=%s", passfile.Name()))
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
apiproxy "github.com/hashicorp/boundary/api/proxy"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/posener/complete"
|
||||
@@ -54,7 +55,7 @@ func (s *sshFlags) defaultExec() string {
|
||||
return strings.ToLower(s.flagSshStyle)
|
||||
}
|
||||
|
||||
func (s *sshFlags) buildArgs(c *Command, port, ip, _ string, creds credentials) (args, envs []string, retCreds credentials, retErr error) {
|
||||
func (s *sshFlags) buildArgs(c *Command, port, ip, _ string, creds apiproxy.Credentials) (args, envs []string, retCreds apiproxy.Credentials, retErr error) {
|
||||
var username string
|
||||
retCreds = creds
|
||||
|
||||
@@ -89,20 +90,20 @@ func (s *sshFlags) buildArgs(c *Command, port, ip, _ string, creds credentials)
|
||||
|
||||
case "sshpass":
|
||||
if !tryConsume {
|
||||
return nil, nil, credentials{}, errors.New("Credentials must be consumed when using sshpass")
|
||||
return nil, nil, apiproxy.Credentials{}, errors.New("Credentials must be consumed when using sshpass")
|
||||
}
|
||||
var password string
|
||||
if len(retCreds.usernamePassword) > 0 {
|
||||
if len(retCreds.UsernamePassword) > 0 {
|
||||
// For now just grab the first username password credential brokered
|
||||
// Mark credential as consumed so that it is not printed to user
|
||||
retCreds.usernamePassword[0].consumed = true
|
||||
retCreds.UsernamePassword[0].Consumed = true
|
||||
|
||||
username = retCreds.usernamePassword[0].Username
|
||||
password = retCreds.usernamePassword[0].Password
|
||||
username = retCreds.UsernamePassword[0].Username
|
||||
password = retCreds.UsernamePassword[0].Password
|
||||
}
|
||||
|
||||
if password == "" {
|
||||
return nil, nil, credentials{}, errors.New("Password is required when using sshpass")
|
||||
return nil, nil, apiproxy.Credentials{}, errors.New("Password is required when using sshpass")
|
||||
}
|
||||
|
||||
// sshpass requires that the password is passed as env-var "SSHPASS"
|
||||
@@ -130,24 +131,24 @@ func (s *sshFlags) buildArgs(c *Command, port, ip, _ string, creds credentials)
|
||||
// Do nothing
|
||||
|
||||
// If we want to consume check if we have a private key available first
|
||||
case len(creds.sshPrivateKey) > 0:
|
||||
case len(creds.SshPrivateKey) > 0:
|
||||
// For now just grab the first ssh private key credential brokered
|
||||
cred := retCreds.sshPrivateKey[0]
|
||||
cred := retCreds.SshPrivateKey[0]
|
||||
|
||||
username = cred.Username
|
||||
privateKey := cred.PrivateKey
|
||||
cred.consumed = true
|
||||
cred.Consumed = true
|
||||
if cred.Passphrase != "" {
|
||||
// Don't re-print everything, but print the passphrase they'll need
|
||||
cred.consumed = false
|
||||
delete(cred.raw.Credential, "username")
|
||||
delete(cred.raw.Credential, "private_key")
|
||||
cred.Consumed = false
|
||||
delete(cred.Raw.Credential, "username")
|
||||
delete(cred.Raw.Credential, "private_key")
|
||||
}
|
||||
retCreds.sshPrivateKey[0] = cred
|
||||
retCreds.SshPrivateKey[0] = cred
|
||||
|
||||
pkFile, err := os.CreateTemp("", "*")
|
||||
if err != nil {
|
||||
return nil, nil, credentials{}, fmt.Errorf("Error saving ssh private key to tmp file: %w", err)
|
||||
return nil, nil, apiproxy.Credentials{}, fmt.Errorf("Error saving ssh private key to tmp file: %w", err)
|
||||
}
|
||||
c.cleanupFuncs = append(c.cleanupFuncs, func() error {
|
||||
if err := os.Remove(pkFile.Name()); err != nil {
|
||||
@@ -163,15 +164,15 @@ func (s *sshFlags) buildArgs(c *Command, port, ip, _ string, creds credentials)
|
||||
}
|
||||
_, err = pkFile.WriteString(privateKey)
|
||||
if err != nil {
|
||||
return nil, nil, credentials{}, fmt.Errorf("Error writing private key file to %s: %w", pkFile.Name(), err)
|
||||
return nil, nil, apiproxy.Credentials{}, fmt.Errorf("Error writing private key file to %s: %w", pkFile.Name(), err)
|
||||
}
|
||||
if err := pkFile.Close(); err != nil {
|
||||
return nil, nil, credentials{}, fmt.Errorf("Error closing private key file after writing to %s: %w", pkFile.Name(), err)
|
||||
return nil, nil, apiproxy.Credentials{}, fmt.Errorf("Error closing private key file after writing to %s: %w", pkFile.Name(), err)
|
||||
}
|
||||
args = append(args, "-i", pkFile.Name())
|
||||
|
||||
// Next check if we have a username password credential
|
||||
case len(creds.usernamePassword) > 0:
|
||||
case len(creds.UsernamePassword) > 0:
|
||||
// We cannot use the password of the credential outside of sshpass, but we
|
||||
// can use the username.
|
||||
// N.B. Do not mark credential as consumed, as user will still need enter
|
||||
@@ -180,7 +181,7 @@ func (s *sshFlags) buildArgs(c *Command, port, ip, _ string, creds credentials)
|
||||
if c.flagUsername == "" {
|
||||
// If the user did not actively provide a username flag set the
|
||||
// username to that of the first credential we got.
|
||||
username = retCreds.usernamePassword[0].Username
|
||||
username = retCreds.UsernamePassword[0].Username
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user