mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	 dac99be29d
			
		
	
	dac99be29d
	
	
	
		
			
			Add a new config option for Vault Agent's JWT auto auth `remove_jwt_after_reading`, which defaults to true. Can stop Agent from attempting to delete the file, which is useful in k8s where the service account JWT is mounted as a read-only file and so any attempt to delete it generates spammy error logs. When leaving the JWT file in place, the read period for new tokens is 1 minute instead of 500ms to reflect the assumption that there will always be a file there, so finding a file does not provide any signal that it needs to be re-read. Kubernetes has a minimum TTL of 10 minutes for tokens, so a period of 1 minute gives Agent plenty of time to detect new tokens, without leaving it too unresponsive. We may want to add a config option to override these default periods in the future. Co-authored-by: Tom Proctor <tomhjp@users.noreply.github.com>
		
			
				
	
	
		
			168 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package jwt
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"os"
 | |
| 	"path"
 | |
| 	"strings"
 | |
| 	"sync/atomic"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/hashicorp/go-hclog"
 | |
| 	"github.com/hashicorp/vault/command/agent/auth"
 | |
| )
 | |
| 
 | |
| func TestIngressToken(t *testing.T) {
 | |
| 	const (
 | |
| 		dir       = "dir"
 | |
| 		file      = "file"
 | |
| 		empty     = "empty"
 | |
| 		missing   = "missing"
 | |
| 		symlinked = "symlinked"
 | |
| 	)
 | |
| 
 | |
| 	rootDir, err := os.MkdirTemp("", "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 := os.MkdirTemp(rootDir, "")
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		err = os.WriteFile(path.Join(testDir, file), []byte("test"), 0o644)
 | |
| 		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), 0o755)
 | |
| 		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 unexpected error", logBuffer.String())
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDeleteAfterReading(t *testing.T) {
 | |
| 	for _, tc := range map[string]struct {
 | |
| 		configValue  string
 | |
| 		shouldDelete bool
 | |
| 	}{
 | |
| 		"default": {
 | |
| 			"",
 | |
| 			true,
 | |
| 		},
 | |
| 		"explicit true": {
 | |
| 			"true",
 | |
| 			true,
 | |
| 		},
 | |
| 		"false": {
 | |
| 			"false",
 | |
| 			false,
 | |
| 		},
 | |
| 	} {
 | |
| 		rootDir, err := os.MkdirTemp("", "vault-agent-jwt-auth-test")
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("failed to create temp dir: %s", err)
 | |
| 		}
 | |
| 		defer os.RemoveAll(rootDir)
 | |
| 		tokenPath := path.Join(rootDir, "token")
 | |
| 		err = os.WriteFile(tokenPath, []byte("test"), 0o644)
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		config := &auth.AuthConfig{
 | |
| 			Config: map[string]interface{}{
 | |
| 				"path": tokenPath,
 | |
| 				"role": "unusedrole",
 | |
| 			},
 | |
| 			Logger: hclog.Default(),
 | |
| 		}
 | |
| 		if tc.configValue != "" {
 | |
| 			config.Config["remove_jwt_after_reading"] = tc.configValue
 | |
| 		}
 | |
| 
 | |
| 		jwtAuth, err := NewJWTAuthMethod(config)
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		jwtAuth.(*jwtMethod).ingressToken()
 | |
| 
 | |
| 		if _, err := os.Lstat(tokenPath); tc.shouldDelete {
 | |
| 			if err == nil || !os.IsNotExist(err) {
 | |
| 				t.Fatal(err)
 | |
| 			}
 | |
| 		} else {
 | |
| 			if err != nil {
 | |
| 				t.Fatal(err)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 |