diff --git a/go.mod b/go.mod index 888504659..e71e8ac54 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,6 @@ require ( require ( github.com/go-resty/resty/v2 v2.7.0 github.com/nginx/agent/sdk/v2 v2.0.0-00010101000000-000000000000 - github.com/nginxinc/nginx-go-crossplane v0.4.1 github.com/prometheus/client_golang v1.13.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -56,6 +55,7 @@ require ( github.com/lufia/plan9stats v0.0.0-20220517141722-cf486979b281 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/nginxinc/nginx-go-crossplane v0.4.1 // indirect github.com/pascaldekloe/name v1.0.1 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.2 // indirect diff --git a/sdk/config_helpers.go b/sdk/config_helpers.go index f0109af1c..9155d6523 100644 --- a/sdk/config_helpers.go +++ b/sdk/config_helpers.go @@ -16,6 +16,7 @@ import ( "net" "net/http" "os" + "path" "path/filepath" "sort" "strconv" @@ -820,3 +821,61 @@ func convertToHexFormat(hexString string) string { } return formatted } + +func GetAppProtectPolicyAndSecurityLogFiles(cfg *proto.NginxConfig) ([]string, []string) { + policyMap := make(map[string]bool) + profileMap := make(map[string]bool) + + for _, directory := range cfg.GetDirectoryMap().GetDirectories() { + for _, file := range directory.GetFiles() { + confFile := path.Join(directory.GetName(), file.GetName()) + + payload, err := crossplane.Parse(confFile, + &crossplane.ParseOptions{ + SingleFile: false, + StopParsingOnError: true, + }, + ) + + if err != nil { + continue + } + + for _, conf := range payload.Config { + err = CrossplaneConfigTraverse(&conf, + func(parent *crossplane.Directive, directive *crossplane.Directive) (bool, error) { + switch directive.Directive { + case "app_protect_policy_file": + if len(directive.Args) == 1 { + _, policy := path.Split(directive.Args[0]) + policyMap[policy] = true + } + case "app_protect_security_log": + if len(directive.Args) == 2 { + _, profile := path.Split(directive.Args[0]) + profileMap[profile] = true + } + } + return true, nil + }) + if err != nil { + continue + } + } + if err != nil { + continue + } + } + } + policies := []string{} + for policy := range policyMap { + policies = append(policies, policy) + } + + profiles := []string{} + for profile := range profileMap { + profiles = append(profiles, profile) + } + + return policies, profiles +} diff --git a/sdk/config_helpers_test.go b/sdk/config_helpers_test.go index 52db34a6a..cedfb01ec 100644 --- a/sdk/config_helpers_test.go +++ b/sdk/config_helpers_test.go @@ -1360,3 +1360,154 @@ func TestAddAuxfileToNginxConfig(t *testing.T) { } } } + +func TestGetAppProtectPolicyAndSecurityLogFiles(t *testing.T) { + testCases := []struct { + testName string + file string + config string + expPolicies []string + expProfiles []string + }{ + { + testName: "NoNAPContent", + file: "/tmp/testdata/nginx/nginx.conf", + config: `daemon off; + worker_processes 2; + user www-data; + + events { + use epoll; + worker_connections 128; + } + + error_log /tmp/testdata/logs/error.log info; + + http { + log_format upstream_time '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent" ' + 'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"'; + + server_tokens off; + charset utf-8; + + access_log /tmp/testdata/logs/access1.log $upstream_time; + + server { + server_name localhost; + listen 127.0.0.1:80; + + error_page 500 502 503 504 /50x.html; + # ssl_certificate /usr/local/nginx/conf/cert.pem; + + location / { + root /tmp/testdata/root; + } + + location /privateapi { + limit_except GET { + auth_basic "NGINX Plus API"; + auth_basic_user_file /path/to/passwd/file; + } + api write=on; + allow 127.0.0.1; + deny all; + } + } + + access_log /tmp/testdata/logs/access2.log combined; + + }`, + expPolicies: []string{}, + expProfiles: []string{}, + }, + { + testName: "ConfigWithNAPContent", + file: "/tmp/testdata/nginx/nginx2.conf", + config: `daemon off; + worker_processes 2; + user www-data; + + events { + use epoll; + worker_connections 128; + } + + error_log /tmp/testdata/logs/error.log info; + + http { + app_protect_enable on; + app_protect_security_log_enable on; + + log_format upstream_time '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent" ' + 'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"'; + + server_tokens off; + charset utf-8; + + access_log /tmp/testdata/logs/access1.log $upstream_time; + app_protect_policy_file /tmp/testdata/root/my-nap-policy1.json; + app_protect_security_log "/tmp/testdata/root/log-all.json" /var/log/ssecurity.log; + + server { + server_name localhost; + listen 127.0.0.1:80; + app_protect_policy_file /tmp/testdata/root/my-nap-policy2.json; + app_protect_security_log "/tmp/testdata/root/log-blocked.json" /var/log/ssecurity.log; + + error_page 500 502 503 504 /50x.html; + # ssl_certificate /usr/local/nginx/conf/cert.pem; + + location / { + root /tmp/testdata/root; + app_protect_policy_file /tmp/testdata/root/my-nap-policy3.json; + app_protect_security_log "/tmp/testdata/root/log-default.json" /var/log/security.log; + } + + location /home { + app_protect_policy_file /tmp/testdata/root/my-nap-policy4.json; + app_protect_security_log "/tmp/testdata/root/log-illegal.json" /var/log/security.log; + } + + location /privateapi { + app_protect_policy_file /tmp/testdata/root/my-nap-policy4.json; + app_protect_security_log "/tmp/testdata/root/log-illegal.json" /var/log/security.log; + limit_except GET { + auth_basic "NGINX Plus API"; + auth_basic_user_file /path/to/passwd/file; + } + api write=on; + allow 127.0.0.1; + deny all; + } + } + + access_log /tmp/testdata/logs/access2.log combined; + + }`, + expPolicies: []string{"my-nap-policy2.json", "my-nap-policy1.json", "my-nap-policy3.json", "my-nap-policy4.json"}, + expProfiles: []string{"log-all.json", "log-blocked.json", "log-default.json", "log-illegal.json"}, + }, + } + + for _, tc := range testCases { + t.Run(tc.testName, func(t *testing.T) { + defer tearDownDirectories() + + err := setUpFile(tc.file, []byte(tc.config)) + assert.NoError(t, err) + + allowedDirs := map[string]struct{}{} + + cfg, err := GetNginxConfig(tc.file, nginxID, systemID, allowedDirs) + assert.NoError(t, err) + + policies, profiles := GetAppProtectPolicyAndSecurityLogFiles(cfg) + assert.ElementsMatch(t, tc.expPolicies, policies) + assert.ElementsMatch(t, tc.expProfiles, profiles) + }) + } +} diff --git a/src/extensions/nginx-app-protect/nap/nap_content.go b/src/extensions/nginx-app-protect/nap/nap_content.go deleted file mode 100644 index 5802179cc..000000000 --- a/src/extensions/nginx-app-protect/nap/nap_content.go +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) F5, Inc. - * - * This source code is licensed under the Apache License, Version 2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -package nap - -import ( - "path" - - "github.com/nginx/agent/sdk/v2" - "github.com/nginx/agent/sdk/v2/proto" - - "github.com/nginxinc/nginx-go-crossplane" -) - -// getContent parses the config for NAP policies and profiles -func getContent(cfg *proto.NginxConfig) ([]string, []string) { - policyMap := make(map[string]bool) - profileMap := make(map[string]bool) - - for _, directory := range cfg.GetDirectoryMap().GetDirectories() { - for _, file := range directory.GetFiles() { - confFile := path.Join(directory.GetName(), file.GetName()) - payload, err := crossplane.Parse(confFile, - &crossplane.ParseOptions{ - SingleFile: false, - StopParsingOnError: true, - }, - ) - if err != nil { - continue - } - for _, conf := range payload.Config { - err = sdk.CrossplaneConfigTraverse(&conf, - func(parent *crossplane.Directive, directive *crossplane.Directive) (bool, error) { - switch directive.Directive { - case "app_protect_policy_file": - if len(directive.Args) == 1 { - _, policy := path.Split(directive.Args[0]) - policyMap[policy] = true - } - case "app_protect_security_log": - if len(directive.Args) == 2 { - _, profile := path.Split(directive.Args[0]) - profileMap[profile] = true - } - } - return true, nil - }) - if err != nil { - continue - } - } - if err != nil { - continue - } - } - } - policies := []string{} - for policy, _ := range policyMap { - policies = append(policies, policy) - } - profiles := []string{} - for profile, _ := range profileMap { - profiles = append(profiles, profile) - } - - return policies, profiles -} diff --git a/src/extensions/nginx-app-protect/nap/nap_content_test.go b/src/extensions/nginx-app-protect/nap/nap_content_test.go deleted file mode 100644 index 705f5bfef..000000000 --- a/src/extensions/nginx-app-protect/nap/nap_content_test.go +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright (c) F5, Inc. - * - * This source code is licensed under the Apache License, Version 2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -package nap - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/nginx/agent/sdk/v2" - - "github.com/stretchr/testify/assert" -) - -const ( - nginxID = "1" - systemID = "2" -) - -var config0 = `daemon off; - worker_processes 2; - user www-data; - - events { - use epoll; - worker_connections 128; - } - - error_log /tmp/testdata/logs/error.log info; - - http { - log_format upstream_time '$remote_addr - $remote_user [$time_local] ' - '"$request" $status $body_bytes_sent ' - '"$http_referer" "$http_user_agent" ' - 'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"'; - - server_tokens off; - charset utf-8; - - access_log /tmp/testdata/logs/access1.log $upstream_time; - - server { - server_name localhost; - listen 127.0.0.1:80; - - error_page 500 502 503 504 /50x.html; - # ssl_certificate /usr/local/nginx/conf/cert.pem; - - location / { - root /tmp/testdata/root; - } - - location /privateapi { - limit_except GET { - auth_basic "NGINX Plus API"; - auth_basic_user_file /path/to/passwd/file; - } - api write=on; - allow 127.0.0.1; - deny all; - } - } - - access_log /tmp/testdata/logs/access2.log combined; - - }` - -var config1 = `daemon off; - worker_processes 2; - user www-data; - - events { - use epoll; - worker_connections 128; - } - - error_log /tmp/testdata/logs/error.log info; - - http { - app_protect_enable on; - app_protect_security_log_enable on; - - log_format upstream_time '$remote_addr - $remote_user [$time_local] ' - '"$request" $status $body_bytes_sent ' - '"$http_referer" "$http_user_agent" ' - 'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"'; - - server_tokens off; - charset utf-8; - - access_log /tmp/testdata/logs/access1.log $upstream_time; - app_protect_policy_file /tmp/testdata/root/my-nap-policy1.json; - app_protect_security_log "/tmp/testdata/root/log-all.json" /var/log/ssecurity.log; - - server { - server_name localhost; - listen 127.0.0.1:80; - app_protect_policy_file /tmp/testdata/root/my-nap-policy2.json; - app_protect_security_log "/tmp/testdata/root/log-blocked.json" /var/log/ssecurity.log; - - error_page 500 502 503 504 /50x.html; - # ssl_certificate /usr/local/nginx/conf/cert.pem; - - location / { - root /tmp/testdata/root; - app_protect_policy_file /tmp/testdata/root/my-nap-policy3.json; - app_protect_security_log "/tmp/testdata/root/log-default.json" /var/log/security.log; - } - - location /home { - app_protect_policy_file /tmp/testdata/root/my-nap-policy4.json; - app_protect_security_log "/tmp/testdata/root/log-illegal.json" /var/log/security.log; - } - - location /privateapi { - app_protect_policy_file /tmp/testdata/root/my-nap-policy4.json; - app_protect_security_log "/tmp/testdata/root/log-illegal.json" /var/log/security.log; - limit_except GET { - auth_basic "NGINX Plus API"; - auth_basic_user_file /path/to/passwd/file; - } - api write=on; - allow 127.0.0.1; - deny all; - } - } - - access_log /tmp/testdata/logs/access2.log combined; - - }` - -func TestNAPContent(t *testing.T) { - testCases := []struct { - testName string - file string - config string - expPolicies []string - expProfiles []string - }{ - { - testName: "NoNAPContent", - file: "/tmp/testdata/nginx/nginx.conf", - config: config0, - expPolicies: []string{}, - expProfiles: []string{}, - }, - { - testName: "ConfigWithNAPContent", - file: "/tmp/testdata/nginx/nginx2.conf", - config: config1, - expPolicies: []string{"my-nap-policy2.json", "my-nap-policy1.json", "my-nap-policy3.json", "my-nap-policy4.json"}, - expProfiles: []string{"log-all.json", "log-blocked.json", "log-default.json", "log-illegal.json"}, - }, - } - - for _, tc := range testCases { - t.Run(tc.testName, func(t *testing.T) { - defer tearDownDirectories() - - err := setUpFile(tc.file, []byte(tc.config)) - assert.NoError(t, err) - - allowedDirs := map[string]struct{}{} - - cfg, err := sdk.GetNginxConfig(tc.file, nginxID, systemID, allowedDirs) - assert.NoError(t, err) - - policies, profiles := getContent(cfg) - assert.ElementsMatch(t, tc.expPolicies, policies) - assert.ElementsMatch(t, tc.expProfiles, profiles) - }) - } -} - -func setUpFile(file string, content []byte) error { - err := os.MkdirAll(filepath.Dir(file), 0755) - if err != nil { - return err - } - err = ioutil.WriteFile(file, content, 0644) - if err != nil { - return err - } - - return nil -} - -func tearDownDirectories() { - os.RemoveAll("/tmp/testdata") -} diff --git a/src/extensions/nginx-app-protect/nap/nap_metadata.go b/src/extensions/nginx-app-protect/nap/nap_metadata.go index 8ef616585..16ee00e79 100644 --- a/src/extensions/nginx-app-protect/nap/nap_metadata.go +++ b/src/extensions/nginx-app-protect/nap/nap_metadata.go @@ -11,6 +11,7 @@ import ( "encoding/json" "os" + "github.com/nginx/agent/sdk/v2" "github.com/nginx/agent/sdk/v2/proto" log "github.com/sirupsen/logrus" @@ -45,7 +46,7 @@ func UpdateMetadata( return nil } - policies, profiles := getContent(cfg) + policies, profiles := sdk.GetAppProtectPolicyAndSecurityLogFiles(cfg) policyBundles := []*BundleMetadata{} profileBundles := []*BundleMetadata{} diff --git a/test/performance/vendor/github.com/nginx/agent/sdk/v2/config_helpers.go b/test/performance/vendor/github.com/nginx/agent/sdk/v2/config_helpers.go index f0109af1c..9155d6523 100644 --- a/test/performance/vendor/github.com/nginx/agent/sdk/v2/config_helpers.go +++ b/test/performance/vendor/github.com/nginx/agent/sdk/v2/config_helpers.go @@ -16,6 +16,7 @@ import ( "net" "net/http" "os" + "path" "path/filepath" "sort" "strconv" @@ -820,3 +821,61 @@ func convertToHexFormat(hexString string) string { } return formatted } + +func GetAppProtectPolicyAndSecurityLogFiles(cfg *proto.NginxConfig) ([]string, []string) { + policyMap := make(map[string]bool) + profileMap := make(map[string]bool) + + for _, directory := range cfg.GetDirectoryMap().GetDirectories() { + for _, file := range directory.GetFiles() { + confFile := path.Join(directory.GetName(), file.GetName()) + + payload, err := crossplane.Parse(confFile, + &crossplane.ParseOptions{ + SingleFile: false, + StopParsingOnError: true, + }, + ) + + if err != nil { + continue + } + + for _, conf := range payload.Config { + err = CrossplaneConfigTraverse(&conf, + func(parent *crossplane.Directive, directive *crossplane.Directive) (bool, error) { + switch directive.Directive { + case "app_protect_policy_file": + if len(directive.Args) == 1 { + _, policy := path.Split(directive.Args[0]) + policyMap[policy] = true + } + case "app_protect_security_log": + if len(directive.Args) == 2 { + _, profile := path.Split(directive.Args[0]) + profileMap[profile] = true + } + } + return true, nil + }) + if err != nil { + continue + } + } + if err != nil { + continue + } + } + } + policies := []string{} + for policy := range policyMap { + policies = append(policies, policy) + } + + profiles := []string{} + for profile := range profileMap { + profiles = append(profiles, profile) + } + + return policies, profiles +} diff --git a/test/performance/vendor/github.com/nginx/agent/v2/src/extensions/nginx-app-protect/nap/nap_content.go b/test/performance/vendor/github.com/nginx/agent/v2/src/extensions/nginx-app-protect/nap/nap_content.go deleted file mode 100644 index 5802179cc..000000000 --- a/test/performance/vendor/github.com/nginx/agent/v2/src/extensions/nginx-app-protect/nap/nap_content.go +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) F5, Inc. - * - * This source code is licensed under the Apache License, Version 2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -package nap - -import ( - "path" - - "github.com/nginx/agent/sdk/v2" - "github.com/nginx/agent/sdk/v2/proto" - - "github.com/nginxinc/nginx-go-crossplane" -) - -// getContent parses the config for NAP policies and profiles -func getContent(cfg *proto.NginxConfig) ([]string, []string) { - policyMap := make(map[string]bool) - profileMap := make(map[string]bool) - - for _, directory := range cfg.GetDirectoryMap().GetDirectories() { - for _, file := range directory.GetFiles() { - confFile := path.Join(directory.GetName(), file.GetName()) - payload, err := crossplane.Parse(confFile, - &crossplane.ParseOptions{ - SingleFile: false, - StopParsingOnError: true, - }, - ) - if err != nil { - continue - } - for _, conf := range payload.Config { - err = sdk.CrossplaneConfigTraverse(&conf, - func(parent *crossplane.Directive, directive *crossplane.Directive) (bool, error) { - switch directive.Directive { - case "app_protect_policy_file": - if len(directive.Args) == 1 { - _, policy := path.Split(directive.Args[0]) - policyMap[policy] = true - } - case "app_protect_security_log": - if len(directive.Args) == 2 { - _, profile := path.Split(directive.Args[0]) - profileMap[profile] = true - } - } - return true, nil - }) - if err != nil { - continue - } - } - if err != nil { - continue - } - } - } - policies := []string{} - for policy, _ := range policyMap { - policies = append(policies, policy) - } - profiles := []string{} - for profile, _ := range profileMap { - profiles = append(profiles, profile) - } - - return policies, profiles -} diff --git a/test/performance/vendor/github.com/nginx/agent/v2/src/extensions/nginx-app-protect/nap/nap_metadata.go b/test/performance/vendor/github.com/nginx/agent/v2/src/extensions/nginx-app-protect/nap/nap_metadata.go index 8ef616585..16ee00e79 100644 --- a/test/performance/vendor/github.com/nginx/agent/v2/src/extensions/nginx-app-protect/nap/nap_metadata.go +++ b/test/performance/vendor/github.com/nginx/agent/v2/src/extensions/nginx-app-protect/nap/nap_metadata.go @@ -11,6 +11,7 @@ import ( "encoding/json" "os" + "github.com/nginx/agent/sdk/v2" "github.com/nginx/agent/sdk/v2/proto" log "github.com/sirupsen/logrus" @@ -45,7 +46,7 @@ func UpdateMetadata( return nil } - policies, profiles := getContent(cfg) + policies, profiles := sdk.GetAppProtectPolicyAndSecurityLogFiles(cfg) policyBundles := []*BundleMetadata{} profileBundles := []*BundleMetadata{} diff --git a/vendor/github.com/nginx/agent/sdk/v2/config_helpers.go b/vendor/github.com/nginx/agent/sdk/v2/config_helpers.go index f0109af1c..9155d6523 100644 --- a/vendor/github.com/nginx/agent/sdk/v2/config_helpers.go +++ b/vendor/github.com/nginx/agent/sdk/v2/config_helpers.go @@ -16,6 +16,7 @@ import ( "net" "net/http" "os" + "path" "path/filepath" "sort" "strconv" @@ -820,3 +821,61 @@ func convertToHexFormat(hexString string) string { } return formatted } + +func GetAppProtectPolicyAndSecurityLogFiles(cfg *proto.NginxConfig) ([]string, []string) { + policyMap := make(map[string]bool) + profileMap := make(map[string]bool) + + for _, directory := range cfg.GetDirectoryMap().GetDirectories() { + for _, file := range directory.GetFiles() { + confFile := path.Join(directory.GetName(), file.GetName()) + + payload, err := crossplane.Parse(confFile, + &crossplane.ParseOptions{ + SingleFile: false, + StopParsingOnError: true, + }, + ) + + if err != nil { + continue + } + + for _, conf := range payload.Config { + err = CrossplaneConfigTraverse(&conf, + func(parent *crossplane.Directive, directive *crossplane.Directive) (bool, error) { + switch directive.Directive { + case "app_protect_policy_file": + if len(directive.Args) == 1 { + _, policy := path.Split(directive.Args[0]) + policyMap[policy] = true + } + case "app_protect_security_log": + if len(directive.Args) == 2 { + _, profile := path.Split(directive.Args[0]) + profileMap[profile] = true + } + } + return true, nil + }) + if err != nil { + continue + } + } + if err != nil { + continue + } + } + } + policies := []string{} + for policy := range policyMap { + policies = append(policies, policy) + } + + profiles := []string{} + for profile := range profileMap { + profiles = append(profiles, profile) + } + + return policies, profiles +}