Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vaultfs: auth improvements #521

Merged
merged 1 commit into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 1 addition & 1 deletion vaultfs/vault_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ func (m *spyAuthMethod) Login(_ context.Context, client *api.Client) (*api.Secre
}

// make sure logout functionality works
func (m *spyAuthMethod) Logout(client *api.Client) {
func (m *spyAuthMethod) Logout(_ context.Context, client *api.Client) {
client.ClearToken()
}

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
2 changes: 1 addition & 1 deletion vaultfs/vaultauth/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (m *tokenAuthMethod) Login(_ context.Context, _ *api.Client) (*api.Secret,

// Logout implements the vaultfs.authLogouter interface because we need to keep
// the token unmanaged.
func (m *tokenAuthMethod) Logout(client *api.Client) {
func (m *tokenAuthMethod) Logout(_ context.Context, client *api.Client) {
// just clear the client's token, nothing else needs to be done here
client.ClearToken()
}
Loading