Skip to content

Commit

Permalink
vaultfs: auth improvements
Browse files Browse the repository at this point in the history
Signed-off-by: Dave Henderson <[email protected]>
  • Loading branch information
hairyhenderson committed Jan 14, 2024
1 parent 2fdf8ee commit 00d67d9
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 51 deletions.
2 changes: 1 addition & 1 deletion vaultfs/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type withAPIAuthMethoder interface {
// an optional interface that auth methods may implement to override the regular
// token revocation
type authLogouter interface {
Logout(client *api.Client)
Logout(ctx context.Context, client *api.Client)
}

// AuthMethod is an authentication method that vaultfs can use to acquire a
Expand Down
19 changes: 9 additions & 10 deletions vaultfs/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ func (f *vaultFile) Close() error {
// the token auth method manages its own logout, to avoid revoking the
// token, which shouldn't be managed here
if lauth, ok := f.auth.(authLogouter); ok {
lauth.Logout(f.client.Client)
lauth.Logout(f.ctx, f.client.Client)
} else {
revokeToken(f.ctx, f.client.Client)
}
Expand Down Expand Up @@ -387,17 +387,16 @@ func (f *vaultFile) Stat() (fs.FileInfo, error) {
resp, isV2, err := f.request(http.MethodGet)

rerr := &api.ResponseError{}
if errors.As(err, &rerr) {
// if it's a 404 it might be a directory - let's try to LIST it instead
if rerr.StatusCode != http.StatusNotFound {
return nil, &fs.PathError{
Op: "stat", Path: f.name,
Err: vaultFSError(err),
}
if errors.As(err, &rerr) && rerr.StatusCode != http.StatusNotFound {
return nil, &fs.PathError{
Op: "stat", Path: f.name,
Err: vaultFSError(err),
}
} else if err != nil {
_, err = f.list()
if err != nil {
// if it's a 404 it might be a directory - let's try to LIST it instead
_, lerr := f.list()
if lerr != nil {
// return the original error, not the LIST error
return nil, &fs.PathError{Op: "stat", Path: f.name, Err: err}
}

Expand Down
51 changes: 51 additions & 0 deletions vaultfs/vaultauth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,54 @@ func vaultFSError(err error) error {

return err
}

// CompositeAuthMethod returns an AuthMethod that will try each of the given
// methods in order, until one succeeds.
func CompositeAuthMethod(methods ...api.AuthMethod) api.AuthMethod {
return &compositeAuthMethod{methods: methods}
}

type compositeAuthMethod struct {
chosen api.AuthMethod
methods []api.AuthMethod
}

func (m *compositeAuthMethod) Login(ctx context.Context, client *api.Client) (secret *api.Secret, err error) {
if m.chosen == nil {
for _, auth := range m.methods {
if auth == nil {
continue
}

secret, err = auth.Login(ctx, client)
if err == nil {
m.chosen = auth

break
}
}
}

if m.chosen == nil {
return nil, fmt.Errorf("unable to authenticate with vault by any configured method. Last error was: %w", err)
}

return secret, nil
}

func (m *compositeAuthMethod) Logout(ctx context.Context, client *api.Client) {
if m.chosen == nil {
return
}

// some auth methods (like the token method) manage their own logout, to
// avoid revoking the token, which shouldn't be managed here
if lauth, ok := m.chosen.(interface {
Logout(ctx context.Context, client *api.Client)
}); ok {
lauth.Logout(ctx, client)
} else {
_, _ = client.Logical().WriteWithContext(ctx, "auth/token/revoke-self", nil)
client.ClearToken()
}
}
45 changes: 6 additions & 39 deletions vaultfs/vaultauth/env.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package vaultauth

import (
"context"
"fmt"
"os"

"github.com/hashicorp/vault/api"
Expand Down Expand Up @@ -41,43 +39,12 @@ import (
// to be heavily depended upon. It is recommended that you use the auth methods
// directly, and configure them with the appropriate options.
func EnvAuthMethod() api.AuthMethod {
return &envAuthMethod{
// sorted in order of precedence
methods: []api.AuthMethod{
envAppRoleAdapter(),
envGitHubAdapter(),
envUserPassAdapter(),
NewTokenAuth(""),
},
}
}

type envAuthMethod struct {
chosen api.AuthMethod
methods []api.AuthMethod
}

func (m *envAuthMethod) Login(ctx context.Context, client *api.Client) (secret *api.Secret, err error) {
if m.chosen == nil {
for _, auth := range m.methods {
if auth == nil {
continue
}

secret, err = auth.Login(ctx, client)
if err == nil {
m.chosen = auth

break
}
}
}

if m.chosen == nil {
return nil, fmt.Errorf("unable to authenticate with vault by any configured method. Last error was: %w", err)
}

return secret, nil
return CompositeAuthMethod(
envAppRoleAdapter(),
envGitHubAdapter(),
envUserPassAdapter(),
NewTokenAuth(""),
)
}

// envAppRoleAdapter builds an AppRoleAuth from environment variables, for use
Expand Down
2 changes: 1 addition & 1 deletion vaultfs/vaultauth/env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func TestEnvAuthLogin(t *testing.T) {
s, err := m.Login(ctx, v)
require.NoError(t, err)
assert.Equal(t, "foo", s.Auth.ClientToken)
assert.NotNil(t, m.(*envAuthMethod).chosen)
assert.NotNil(t, m.(*compositeAuthMethod).chosen)
}

func fakeVaultServer(t *testing.T) *api.Client {
Expand Down

0 comments on commit 00d67d9

Please sign in to comment.