diff --git a/internal/config/config.go b/internal/config/config.go index 4b2feb20d..8f27ddb74 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -917,12 +917,38 @@ func resolveExtensions() Extensions { } } + if headersSetter != nil { + headersSetter.Headers = updateHeaders(headersSetter.Headers) + } + return Extensions{ Health: health, HeadersSetter: headersSetter, } } +func updateHeaders(headers []Header) []Header { + var err error + newHeaders := []Header{} + + for _, header := range headers { + value := header.Value + if value == "" && header.FilePath != "" { + slog.Debug("Read value from file", "path", header.FilePath) + value, err = file.ReadFromFile(header.FilePath) + if err != nil { + slog.Error("Unable to read value from file path", + "error", err, "file_path", header.FilePath) + } + } + + header.Value = value + newHeaders = append(newHeaders, header) + } + + return newHeaders +} + func isHealthExtensionSet() bool { return viperInstance.IsSet(CollectorExtensionsHealthKey) || (viperInstance.IsSet(CollectorExtensionsHealthServerHostKey) && diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 811a1226d..339a667e1 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -5,6 +5,7 @@ package config import ( + _ "embed" "errors" "os" "path" @@ -12,6 +13,8 @@ import ( "testing" "time" + conf "github.com/nginx/agent/v3/test/config" + "github.com/nginx/agent/v3/pkg/config" "github.com/nginx/agent/v3/test/helpers" @@ -635,6 +638,128 @@ func TestValidateYamlFile(t *testing.T) { } } +func TestResolveExtensions(t *testing.T) { + tests := []struct { + name string + value string + value2 string + path string + path2 string + expected []string + }{ + { + name: "Test 1: User includes a single value header only", + value: "super-secret-token", + path: "", + expected: []string{"super-secret-token"}, + }, + { + name: "Test 2: User includes a single filepath header only", + value: "", + path: "testdata/nginx-token.crt", + expected: []string{"super-secret-token"}, + }, + { + name: "Test 3: User includes both a single token and a single filepath header", + value: "very-secret-token", + path: "testdata/nginx-token.crt", + expected: []string{"very-secret-token"}, + }, + { + name: "Test 4: User includes neither token nor filepath header", + value: "", + path: "", + expected: []string{""}, + }, + { + name: "Test 5: User includes multiple headers", + value: "super-secret-token", + value2: "very-secret-token", + path: "", + path2: "", + expected: []string{"super-secret-token", "very-secret-token"}, + }, + } + + viperInstance = viper.NewWithOptions(viper.KeyDelimiter(KeyDelimiter)) + tempDir := t.TempDir() + var confContent []byte + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tempFile := helpers.CreateFileWithErrorCheck(t, tempDir, "nginx-agent.conf") + defer helpers.RemoveFileWithErrorCheck(t, tempFile.Name()) + + if len(tt.expected) == 1 { + confContent = []byte(conf.GetAgentConfigWithToken(tt.value, tt.path)) + } else { + confContent = []byte(conf.AgentConfigWithMultipleHeaders(tt.value, tt.path, tt.value2, tt.path2)) + } + + _, writeErr := tempFile.Write(confContent) + require.NoError(t, writeErr) + + err := loadPropertiesFromFile(tempFile.Name()) + require.NoError(t, err) + + extension := resolveExtensions() + require.NotNil(t, extension) + + var result []string + for _, header := range extension.HeadersSetter.Headers { + result = append(result, header.Value) + } + + assert.Equal(t, tt.expected, result) + + err = tempFile.Close() + require.NoError(t, err) + }) + } +} + +func TestResolveExtensions_MultipleHeaders(t *testing.T) { + tests := []struct { + name string + token string + token2 string + path string + path2 string + expected string + }{ + { + name: "Test 1: User includes a single value header only", + token: "super-secret-token", + path: "", + expected: "super-secret-token", + }, + } + + viperInstance = viper.NewWithOptions(viper.KeyDelimiter(KeyDelimiter)) + tempDir := t.TempDir() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tempFile := helpers.CreateFileWithErrorCheck(t, tempDir, "nginx-agent.conf") + defer helpers.RemoveFileWithErrorCheck(t, tempFile.Name()) + + confContent := []byte(conf.GetAgentConfigWithToken(tt.token, tt.path)) + _, writeErr := tempFile.Write(confContent) + require.NoError(t, writeErr) + + err := loadPropertiesFromFile(tempFile.Name()) + require.NoError(t, err) + + extension := resolveExtensions() + require.NotNil(t, extension) + assert.Equal(t, tt.expected, extension.HeadersSetter.Headers[0].Value) + + err = tempFile.Close() + require.NoError(t, err) + }) + } +} + func getAgentConfig() *Config { return &Config{ UUID: "", diff --git a/internal/config/testdata/nginx-token.crt b/internal/config/testdata/nginx-token.crt new file mode 100644 index 000000000..4b3786748 --- /dev/null +++ b/internal/config/testdata/nginx-token.crt @@ -0,0 +1 @@ +super-secret-token diff --git a/internal/config/types.go b/internal/config/types.go index fc59eda74..ed4f77c31 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -139,6 +139,7 @@ type ( Value string `yaml:"value" mapstructure:"value"` DefaultValue string `yaml:"default_value" mapstructure:"default_value"` FromContext string `yaml:"from_context" mapstructure:"from_context"` + FilePath string `yaml:"file_path" mapstructure:"file_path"` } DebugExporter struct{} diff --git a/nginx-agent.conf b/nginx-agent.conf index d92004849..b67980261 100644 --- a/nginx-agent.conf +++ b/nginx-agent.conf @@ -27,4 +27,3 @@ allowed_directories: # token: "" # tls: # skip_verify: false - diff --git a/test/config/agent/nginx-agent-with-multiple-headers.conf b/test/config/agent/nginx-agent-with-multiple-headers.conf new file mode 100644 index 000000000..455406e8f --- /dev/null +++ b/test/config/agent/nginx-agent-with-multiple-headers.conf @@ -0,0 +1,17 @@ +log: + level: info + +collector: + extensions: + headers_setter: + headers: + - action: insert + key: "authorization" + value: %s + file_path: %s + + - action: insert + key: "authorization" + value: %s + file_path: %s + diff --git a/test/config/agent/nginx-agent-with-token.conf b/test/config/agent/nginx-agent-with-token.conf new file mode 100644 index 000000000..69edb7004 --- /dev/null +++ b/test/config/agent/nginx-agent-with-token.conf @@ -0,0 +1,11 @@ +log: + level: info + +collector: + extensions: + headers_setter: + headers: + - action: insert + key: "authorization" + value: %s + file_path: %s diff --git a/test/config/nginx_config.go b/test/config/nginx_config.go index e8f6f32ec..a4b19c0db 100644 --- a/test/config/nginx_config.go +++ b/test/config/nginx_config.go @@ -25,6 +25,12 @@ var embedNginxConfWithMultipleSSLCerts string //go:embed nginx/nginx-ssl-certs-with-variables.conf var embedNginxConfWithSSLCertsWithVariables string +//go:embed agent/nginx-agent-with-token.conf +var agentConfigWithToken string + +//go:embed agent/nginx-agent-with-multiple-headers.conf +var agentConfigWithMultipleHeaders string + func GetNginxConfigWithMultipleAccessLogs( errorLogName, accessLogName, @@ -55,3 +61,11 @@ func GetNginxConfigWithSSLCerts(errorLogFile, accessLogFile, certFile string) st func GetNginxConfigWithMultipleSSLCerts(errorLogFile, accessLogFile, certFile1, certFile2 string) string { return fmt.Sprintf(embedNginxConfWithMultipleSSLCerts, errorLogFile, accessLogFile, certFile1, certFile2) } + +func GetAgentConfigWithToken(value, path string) string { + return fmt.Sprintf(agentConfigWithToken, value, path) +} + +func AgentConfigWithMultipleHeaders(value, path, value2, path2 string) string { + return fmt.Sprintf(agentConfigWithMultipleHeaders, value, path, value2, path2) +}