| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| package agent |
| |
| import ( |
| "context" |
| "os" |
| "path/filepath" |
| "testing" |
| "time" |
| |
| log "github.com/hashicorp/go-hclog" |
| "github.com/hashicorp/vault/command/agentproxyshared/auth" |
| token_file "github.com/hashicorp/vault/command/agentproxyshared/auth/token-file" |
| "github.com/hashicorp/vault/command/agentproxyshared/sink" |
| "github.com/hashicorp/vault/command/agentproxyshared/sink/file" |
| vaulthttp "github.com/hashicorp/vault/http" |
| "github.com/hashicorp/vault/sdk/helper/logging" |
| "github.com/hashicorp/vault/vault" |
| ) |
| |
| func TestTokenFileEndToEnd(t *testing.T) { |
| var err error |
| logger := logging.NewVaultLogger(log.Trace) |
| coreConfig := &vault.CoreConfig{ |
| DisableMlock: true, |
| DisableCache: true, |
| Logger: log.NewNullLogger(), |
| } |
| |
| cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{ |
| HandlerFunc: vaulthttp.Handler, |
| }) |
| |
| cluster.Start() |
| defer cluster.Cleanup() |
| |
| cores := cluster.Cores |
| |
| vault.TestWaitActive(t, cores[0].Core) |
| |
| client := cores[0].Client |
| |
| secret, err := client.Auth().Token().Create(nil) |
| if err != nil || secret == nil { |
| t.Fatal(err) |
| } |
| |
| tokenFile, err := os.Create(filepath.Join(t.TempDir(), "token_file")) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tokenFileName := tokenFile.Name() |
| tokenFile.Close() // WriteFile doesn't need it open |
| os.WriteFile(tokenFileName, []byte(secret.Auth.ClientToken), 0o666) |
| defer os.Remove(tokenFileName) |
| |
| ahConfig := &auth.AuthHandlerConfig{ |
| Logger: logger.Named("auth.handler"), |
| Client: client, |
| } |
| |
| ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) |
| |
| am, err := token_file.NewTokenFileAuthMethod(&auth.AuthConfig{ |
| Logger: logger.Named("auth.method"), |
| Config: map[string]interface{}{ |
| "token_file_path": tokenFileName, |
| }, |
| }) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| ah := auth.NewAuthHandler(ahConfig) |
| errCh := make(chan error) |
| go func() { |
| errCh <- ah.Run(ctx, am) |
| }() |
| defer func() { |
| select { |
| case <-ctx.Done(): |
| case err := <-errCh: |
| if err != nil { |
| t.Fatal(err) |
| } |
| } |
| }() |
| |
| // We close these right away because we're just basically testing |
| // permissions and finding a usable file name |
| sinkFile, err := os.Create(filepath.Join(t.TempDir(), "auth.tokensink.test.")) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tokenSinkFileName := sinkFile.Name() |
| sinkFile.Close() |
| os.Remove(tokenSinkFileName) |
| t.Logf("output: %s", tokenSinkFileName) |
| |
| config := &sink.SinkConfig{ |
| Logger: logger.Named("sink.file"), |
| Config: map[string]interface{}{ |
| "path": tokenSinkFileName, |
| }, |
| WrapTTL: 10 * time.Second, |
| } |
| |
| fs, err := file.NewFileSink(config) |
| if err != nil { |
| t.Fatal(err) |
| } |
| config.Sink = fs |
| |
| ss := sink.NewSinkServer(&sink.SinkServerConfig{ |
| Logger: logger.Named("sink.server"), |
| Client: client, |
| }) |
| go func() { |
| errCh <- ss.Run(ctx, ah.OutputCh, []*sink.SinkConfig{config}) |
| }() |
| defer func() { |
| select { |
| case <-ctx.Done(): |
| case err := <-errCh: |
| if err != nil { |
| t.Fatal(err) |
| } |
| } |
| }() |
| |
| // This has to be after the other defers, so it happens first. It allows |
| // successful test runs to immediately cancel all of the runner goroutines |
| // and unblock any of the blocking defer calls by the runner's DoneCh that |
| // comes before this and avoid successful tests from taking the entire |
| // timeout duration. |
| defer cancel() |
| |
| if stat, err := os.Lstat(tokenSinkFileName); err == nil { |
| t.Fatalf("expected err but got %s", stat) |
| } else if !os.IsNotExist(err) { |
| t.Fatal("expected notexist err") |
| } |
| |
| // Wait 2 seconds for the env variables to be detected and an auth to be generated. |
| time.Sleep(time.Second * 2) |
| |
| token, err := readToken(tokenSinkFileName) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if token.Token == "" { |
| t.Fatal("expected token but didn't receive it") |
| } |
| |
| _, err = os.Stat(tokenFileName) |
| if err != nil { |
| t.Fatal("Token file removed") |
| } |
| } |