From 1af41e76ed5c195812321c7d0482735f3b7987ed Mon Sep 17 00:00:00 2001 From: Tom Proctor Date: Fri, 30 Apr 2021 14:37:38 +0100 Subject: [PATCH 1/4] Allow Agent auto auth to read symlinked JWT files --- command/agent/auth/jwt/jwt.go | 12 +++- command/agent/auth/jwt/jwt_test.go | 108 +++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 command/agent/auth/jwt/jwt_test.go diff --git a/command/agent/auth/jwt/jwt.go b/command/agent/auth/jwt/jwt.go index 403a57fae1dcd..bf7b1bbb3aa69 100644 --- a/command/agent/auth/jwt/jwt.go +++ b/command/agent/auth/jwt/jwt.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "io/fs" "io/ioutil" "net/http" "os" @@ -31,6 +32,8 @@ type jwtMethod struct { latestToken *atomic.Value } +// NewJWTAuthMethod returns an implementation of Agent's auth.AuthMethod +// interface for JWT auth. func NewJWTAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) { if conf == nil { return nil, errors.New("empty config") @@ -86,7 +89,7 @@ func NewJWTAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) { return j, nil } -func (j *jwtMethod) Authenticate(_ context.Context, client *api.Client) (string, http.Header, map[string]interface{}, error) { +func (j *jwtMethod) Authenticate(_ context.Context, _ *api.Client) (string, http.Header, map[string]interface{}, error) { j.logger.Trace("beginning authentication") j.ingressToken() @@ -160,8 +163,11 @@ func (j *jwtMethod) ingressToken() { j.logger.Debug("new jwt file found") - if !fi.Mode().IsRegular() { - j.logger.Error("jwt file is not a regular file") + // Check that the path refers to a file. + // If it's a sym link, it could still be a sym link to a directory, + // but ioutil.ReadFile below will return a descriptive error. + if !fi.Mode().IsRegular() && (fi.Mode()&fs.ModeSymlink) == 0 { + j.logger.Error("jwt file is not a regular file or symlink") return } diff --git a/command/agent/auth/jwt/jwt_test.go b/command/agent/auth/jwt/jwt_test.go new file mode 100644 index 0000000000000..d758216301e24 --- /dev/null +++ b/command/agent/auth/jwt/jwt_test.go @@ -0,0 +1,108 @@ +package jwt + +import ( + "bytes" + "io/ioutil" + "os" + "path" + "strings" + "sync/atomic" + "testing" + + "github.com/hashicorp/go-hclog" +) + +func TestIngressToken(t *testing.T) { + const ( + dir = "dir" + file = "file" + empty = "empty" + missing = "missing" + symlinked = "symlinked" + ) + + rootDir, err := ioutil.TempDir("", "vault-agent-jwt-auth-test") + if err != nil { + t.Fatalf("failed to create temp dir: %s", err) + } + defer os.RemoveAll(rootDir) + + setupTestDir := func() string { + testDir, err := ioutil.TempDir(rootDir, "") + if err != nil { + t.Fatal(err) + } + err = ioutil.WriteFile(path.Join(testDir, file), []byte("test"), 0644) + if err != nil { + t.Fatal(err) + } + _, err = os.Create(path.Join(testDir, empty)) + if err != nil { + t.Fatal(err) + } + err = os.Mkdir(path.Join(testDir, dir), 0755) + if err != nil { + t.Fatal(err) + } + err = os.Symlink(path.Join(testDir, file), path.Join(testDir, symlinked)) + if err != nil { + t.Fatal(err) + } + + return testDir + } + + for _, tc := range []struct { + name string + path string + errString string + }{ + { + "happy path", + file, + "", + }, + { + "path is directory", + dir, + "[ERROR] jwt file is not a regular file or symlink", + }, + { + "path is symlink", + symlinked, + "", + }, + { + "path is missing (implies nothing for ingressToken to do)", + missing, + "", + }, + { + "path is empty file", + empty, + "[WARN] empty jwt file read", + }, + } { + testDir := setupTestDir() + logBuffer := bytes.Buffer{} + jwtAuth := &jwtMethod{ + logger: hclog.New(&hclog.LoggerOptions{ + Output: &logBuffer, + }), + latestToken: new(atomic.Value), + path: path.Join(testDir, tc.path), + } + + jwtAuth.ingressToken() + + if tc.errString != "" { + if !strings.Contains(logBuffer.String(), tc.errString) { + t.Fatal("logs did no contain expected error", tc.errString, logBuffer.String()) + } + } else { + if strings.Contains(logBuffer.String(), "[ERROR]") || strings.Contains(logBuffer.String(), "[WARN]") { + t.Fatal("logs contained unexepected error", logBuffer.String()) + } + } + } +} From 3bb7d2e67a836538baa04fb35c36ae7b814dbac3 Mon Sep 17 00:00:00 2001 From: Tom Proctor Date: Fri, 30 Apr 2021 14:53:03 +0100 Subject: [PATCH 2/4] Update jwt.go --- command/agent/auth/jwt/jwt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/agent/auth/jwt/jwt.go b/command/agent/auth/jwt/jwt.go index bf7b1bbb3aa69..edd130e4deede 100644 --- a/command/agent/auth/jwt/jwt.go +++ b/command/agent/auth/jwt/jwt.go @@ -164,7 +164,7 @@ func (j *jwtMethod) ingressToken() { j.logger.Debug("new jwt file found") // Check that the path refers to a file. - // If it's a sym link, it could still be a sym link to a directory, + // If it's a sym link, it could still be a symlink to a directory, // but ioutil.ReadFile below will return a descriptive error. if !fi.Mode().IsRegular() && (fi.Mode()&fs.ModeSymlink) == 0 { j.logger.Error("jwt file is not a regular file or symlink") From dea2d9d30c87c57ac83521de22485c348622e556 Mon Sep 17 00:00:00 2001 From: Tom Proctor Date: Tue, 4 May 2021 11:53:34 +0100 Subject: [PATCH 3/4] typo Co-authored-by: Theron Voran --- command/agent/auth/jwt/jwt_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/agent/auth/jwt/jwt_test.go b/command/agent/auth/jwt/jwt_test.go index d758216301e24..ef33bfb7e720b 100644 --- a/command/agent/auth/jwt/jwt_test.go +++ b/command/agent/auth/jwt/jwt_test.go @@ -101,7 +101,7 @@ func TestIngressToken(t *testing.T) { } } else { if strings.Contains(logBuffer.String(), "[ERROR]") || strings.Contains(logBuffer.String(), "[WARN]") { - t.Fatal("logs contained unexepected error", logBuffer.String()) + t.Fatal("logs contained unexpected error", logBuffer.String()) } } } From 98e8d817484f4a2f7650746683264e546a14c8f3 Mon Sep 17 00:00:00 2001 From: Tom Proctor Date: Tue, 4 May 2021 12:02:43 +0100 Subject: [PATCH 4/4] Use switch case for ease of reading --- command/agent/auth/jwt/jwt.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/command/agent/auth/jwt/jwt.go b/command/agent/auth/jwt/jwt.go index edd130e4deede..0c97bee905ec3 100644 --- a/command/agent/auth/jwt/jwt.go +++ b/command/agent/auth/jwt/jwt.go @@ -164,9 +164,14 @@ func (j *jwtMethod) ingressToken() { j.logger.Debug("new jwt file found") // Check that the path refers to a file. - // If it's a sym link, it could still be a symlink to a directory, + // If it's a symlink, it could still be a symlink to a directory, // but ioutil.ReadFile below will return a descriptive error. - if !fi.Mode().IsRegular() && (fi.Mode()&fs.ModeSymlink) == 0 { + switch mode := fi.Mode(); { + case mode.IsRegular(): + // regular file + case mode&fs.ModeSymlink != 0: + // symlink + default: j.logger.Error("jwt file is not a regular file or symlink") return }