Skip to content

Commit

Permalink
Read env var values in at login time, add extra tests
Browse files Browse the repository at this point in the history
  • Loading branch information
digivava committed Oct 25, 2021
1 parent 090730c commit 23859a1
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 84 deletions.
35 changes: 19 additions & 16 deletions api/auth/approle/approle.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ import (
)

type AppRoleAuth struct {
mountPath string
roleID string
secretID string
secretIDSource string
unwrap bool
mountPath string
roleID string
secretID string
secretIDFile string
secretIDEnv string
unwrap bool
}

var _ api.AuthMethod = (*AppRoleAuth)(nil)

// SecretID is a struct that allows you to specify where your application is
// storing the secret ID required for login to the AppRole auth method.
type SecretID struct {
Expand Down Expand Up @@ -52,8 +55,6 @@ const (
//
// Supported options: WithMountPath, WithWrappingToken
func NewAppRoleAuth(roleID string, secretID *SecretID, opts ...LoginOption) (*AppRoleAuth, error) {
var _ api.AuthMethod = (*AppRoleAuth)(nil)

if roleID == "" {
return nil, fmt.Errorf("no role ID provided for login")
}
Expand All @@ -72,17 +73,13 @@ func NewAppRoleAuth(roleID string, secretID *SecretID, opts ...LoginOption) (*Ap
roleID: roleID,
}

// set secret ID source to a filepath for reading at login time if FromFile, otherwise just set secret ID to the specified value
// secret ID will be read in at login time if it comes from a file or environment variable, in case the underlying value changes
if secretID.FromFile != "" {
a.secretIDSource = secretID.FromFile
a.secretIDFile = secretID.FromFile
}

if secretID.FromEnv != "" {
fromEnv := os.Getenv(secretID.FromEnv)
if fromEnv == "" {
return nil, fmt.Errorf("secret ID was specified with an environment variable with an empty value")
}
a.secretID = fromEnv
a.secretIDEnv = secretID.FromEnv
}

if secretID.FromString != "" {
Expand All @@ -108,7 +105,7 @@ func (a *AppRoleAuth) Login(ctx context.Context, client *api.Client) (*api.Secre
"role_id": a.roleID,
}

if a.secretIDSource != "" {
if a.secretIDFile != "" {
secretIDValue, err := a.readSecretIDFromFile()
if err != nil {
return nil, fmt.Errorf("error reading secret ID: %w", err)
Expand All @@ -125,6 +122,12 @@ func (a *AppRoleAuth) Login(ctx context.Context, client *api.Client) (*api.Secre
} else {
loginData["secret_id"] = secretIDValue
}
} else if a.secretIDEnv != "" {
secretIDValue := os.Getenv(a.secretIDEnv)
if secretIDValue == "" {
return nil, fmt.Errorf("secret ID was specified with an environment variable with an empty value")
}
loginData["secret_id"] = secretIDValue
} else {
loginData["secret_id"] = a.secretID
}
Expand Down Expand Up @@ -153,7 +156,7 @@ func WithWrappingToken() LoginOption {
}

func (a *AppRoleAuth) readSecretIDFromFile() (string, error) {
secretIDFile, err := os.Open(a.secretIDSource)
secretIDFile, err := os.Open(a.secretIDFile)
if err != nil {
return "", fmt.Errorf("unable to open file containing secret ID: %w", err)
}
Expand Down
41 changes: 36 additions & 5 deletions api/auth/approle/approle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func init() {
os.Setenv("VAULT_TOKEN", "")
}
func TestLogin(t *testing.T) {
secretIDEnvVar := "APPROLE_SECRET_ID"
allowedRoleID := "my-role-id"
allowedSecretID := "my-secret-id"

Expand All @@ -44,6 +45,10 @@ func TestLogin(t *testing.T) {
t.Fatalf("error creating temp file: %v", err)
}
defer os.Remove(tmpfile.Name()) // clean up
err = os.Setenv(secretIDEnvVar, allowedSecretID)
if err != nil {
t.Fatalf("error writing secret ID to env var: %v", err)
}

if _, err := tmpfile.Write(content); err != nil {
t.Fatalf("error writing to temp file: %v", err)
Expand Down Expand Up @@ -84,16 +89,42 @@ func TestLogin(t *testing.T) {
t.Fatalf("error initializing Vault client: %v", err)
}

appRoleAuth, err := NewAppRoleAuth("my-role-id", &SecretID{FromFile: tmpfile.Name()})
authFromFile, err := NewAppRoleAuth(allowedRoleID, &SecretID{FromFile: tmpfile.Name()})
if err != nil {
t.Fatalf("error initializing AppRoleAuth: %v", err)
t.Fatalf("error initializing AppRoleAuth with secret ID file: %v", err)
}

authInfo, err := client.Auth().Login(context.TODO(), appRoleAuth)
loginRespFromFile, err := client.Auth().Login(context.TODO(), authFromFile)
if err != nil {
t.Fatalf("error logging in: %v", err)
t.Fatalf("error logging in with secret ID from file: %v", err)
}
if authInfo.Auth == nil || authInfo.Auth.ClientToken == "" {
if loginRespFromFile.Auth == nil || loginRespFromFile.Auth.ClientToken == "" {
t.Fatalf("no authentication info returned by login")
}

authFromEnv, err := NewAppRoleAuth(allowedRoleID, &SecretID{FromEnv: secretIDEnvVar})
if err != nil {
t.Fatalf("error initializing AppRoleAuth with secret ID env var: %v", err)
}

loginRespFromEnv, err := client.Auth().Login(context.TODO(), authFromEnv)
if err != nil {
t.Fatalf("error logging in with secret ID from env var: %v", err)
}
if loginRespFromEnv.Auth == nil || loginRespFromEnv.Auth.ClientToken == "" {
t.Fatalf("no authentication info returned by login with secret ID from env var")
}

authFromStr, err := NewAppRoleAuth(allowedRoleID, &SecretID{FromString: allowedSecretID})
if err != nil {
t.Fatalf("error initializing AppRoleAuth with secret ID string: %v", err)
}

loginRespFromStr, err := client.Auth().Login(context.TODO(), authFromStr)
if err != nil {
t.Fatalf("error logging in with string: %v", err)
}
if loginRespFromStr.Auth == nil || loginRespFromStr.Auth.ClientToken == "" {
t.Fatalf("no authentication info returned by login with secret ID from string")
}
}
8 changes: 4 additions & 4 deletions api/auth/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ type AWSAuth struct {
nonce string
}

var _ api.AuthMethod = (*AWSAuth)(nil)

type LoginOption func(a *AWSAuth) error

const (
Expand All @@ -41,9 +43,9 @@ const (
pkcs7Type = "pkcs7"
identityType = "identity"
defaultMountPath = "aws"
defaultAuthType = "iam"
defaultAuthType = iamType
defaultRegion = "us-east-1"
defaultSignatureType = "pkcs7"
defaultSignatureType = pkcs7Type
)

// NewAWSAuth initializes a new AWS auth method interface to be
Expand All @@ -52,8 +54,6 @@ const (
// Supported options: WithRole, WithMountPath, WithIAMAuth, WithEC2Auth,
// WithPKCS7Signature, WithIdentitySignature, WithIAMServerIDHeader, WithNonce, WithRegion
func NewAWSAuth(opts ...LoginOption) (*AWSAuth, error) {
var _ api.AuthMethod = (*AWSAuth)(nil)

a := &AWSAuth{
mountPath: defaultMountPath,
authType: defaultAuthType,
Expand Down
4 changes: 2 additions & 2 deletions api/auth/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type KubernetesAuth struct {
serviceAccountToken string
}

var _ api.AuthMethod = (*KubernetesAuth)(nil)

type LoginOption func(a *KubernetesAuth) error

const (
Expand All @@ -34,8 +36,6 @@ const (
//
// Supported options: WithMountPath, WithServiceAccountTokenPath, WithServiceAccountTokenEnv, WithServiceAccountToken
func NewKubernetesAuth(roleName string, opts ...LoginOption) (*KubernetesAuth, error) {
var _ api.AuthMethod = (*KubernetesAuth)(nil)

if roleName == "" {
return nil, fmt.Errorf("no role name was provided")
}
Expand Down
75 changes: 18 additions & 57 deletions api/auth/userpass/userpass.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import (
)

type UserpassAuth struct {
mountPath string
username string
password string
passwordSource string
mountPath string
username string
password string
passwordFile string
passwordEnv string
}

type Password struct {
Expand All @@ -28,6 +29,8 @@ type Password struct {
FromString string
}

var _ api.AuthMethod = (*UserpassAuth)(nil)

type LoginOption func(a *UserpassAuth) error

const (
Expand All @@ -39,8 +42,6 @@ const (
//
// Supported options: WithMountPath
func NewUserpassAuth(username string, password *Password, opts ...LoginOption) (*UserpassAuth, error) {
var _ api.AuthMethod = (*UserpassAuth)(nil)

if username == "" {
return nil, fmt.Errorf("no user name provided for login")
}
Expand All @@ -59,17 +60,13 @@ func NewUserpassAuth(username string, password *Password, opts ...LoginOption) (
username: username,
}

// set password source to a filepath for reading at login time if FromFile, otherwise just set password to the specified value
// password will be read in at login time if it comes from a file or environment variable, in case the underlying value changes
if password.FromFile != "" {
a.passwordSource = password.FromFile
a.passwordFile = password.FromFile
}

if password.FromEnv != "" {
fromEnv := os.Getenv(password.FromEnv)
if fromEnv == "" {
return nil, fmt.Errorf("password was specified with an environment variable with an empty value")
}
a.password = fromEnv
a.passwordEnv = password.FromEnv
}

if password.FromString != "" {
Expand All @@ -93,12 +90,18 @@ func NewUserpassAuth(username string, password *Password, opts ...LoginOption) (
func (a *UserpassAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) {
loginData := make(map[string]interface{})

if a.passwordSource != "" {
if a.passwordFile != "" {
passwordValue, err := a.readPasswordFromFile()
if err != nil {
return nil, fmt.Errorf("error reading password: %w", err)
}
loginData["password"] = passwordValue
} else if a.passwordEnv != "" {
passwordValue := os.Getenv(a.passwordEnv)
if passwordValue == "" {
return nil, fmt.Errorf("password was specified with an environment variable with an empty value")
}
loginData["password"] = passwordValue
} else {
loginData["password"] = a.password
}
Expand All @@ -120,7 +123,7 @@ func WithMountPath(mountPath string) LoginOption {
}

func (a *UserpassAuth) readPasswordFromFile() (string, error) {
passwordFile, err := os.Open(a.passwordSource)
passwordFile, err := os.Open(a.passwordFile)
if err != nil {
return "", fmt.Errorf("unable to open file containing password: %w", err)
}
Expand Down Expand Up @@ -161,45 +164,3 @@ func (password *Password) validate() error {
}
return nil
}

// func readPassword(password *Password) (string, error) {
// var parsedPassword string
// if password.FromFile != "" {
// if password.FromEnv != "" || password.FromString != "" {
// return "", fmt.Errorf("only one location for the password should be specified")
// }
// passwordFile, err := os.Open(password.FromFile)
// if err != nil {
// return "", fmt.Errorf("unable to open file containing password: %w", err)
// }
// defer passwordFile.Close()

// limitedReader := io.LimitReader(passwordFile, 1000)
// passwordBytes, err := io.ReadAll(limitedReader)
// if err != nil {
// return "", fmt.Errorf("unable to read password: %w", err)
// }
// parsedPassword = string(passwordBytes)
// }

// if password.FromEnv != "" {
// if password.FromFile != "" || password.FromString != "" {
// return "", fmt.Errorf("only one location for the password should be specified")
// }
// parsedPassword = os.Getenv(password.FromEnv)
// if parsedPassword == "" {
// return "", fmt.Errorf("password was specified with an environment variable with an empty value")
// }
// }

// if password.FromString != "" {
// if password.FromFile != "" || password.FromEnv != "" {
// return "", fmt.Errorf("only one location for the password should be specified")
// }
// parsedPassword = password.FromString
// }

// passwordValue := strings.TrimSuffix(parsedPassword, "\n")

// return passwordValue, nil
// }

0 comments on commit 23859a1

Please sign in to comment.