Skip to content

Commit

Permalink
Add handling of mutiple log destinations
Browse files Browse the repository at this point in the history
  • Loading branch information
Rafal Wegrzycki committed Mar 11, 2022
1 parent 8791d41 commit e93d010
Show file tree
Hide file tree
Showing 16 changed files with 434 additions and 39 deletions.
12 changes: 12 additions & 0 deletions deployments/common/crds/k8s.nginx.org_policies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,18 @@ spec:
type: boolean
logDest:
type: string
securityLogs:
type: array
items:
description: SecurityLog defines the security log of a WAF policy.
type: object
properties:
apLogConf:
type: string
enable:
type: boolean
logDest:
type: string
status:
description: PolicyStatus is the status of the policy resource
type: object
Expand Down
12 changes: 12 additions & 0 deletions deployments/helm-chart/crds/k8s.nginx.org_policies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,18 @@ spec:
type: boolean
logDest:
type: string
securityLogs:
type: array
items:
description: SecurityLog defines the security log of a WAF policy.
type: object
properties:
apLogConf:
type: string
enable:
type: boolean
logDest:
type: string
status:
description: PolicyStatus is the status of the policy resource
type: object
Expand Down
11 changes: 7 additions & 4 deletions docs/content/configuration/policy-resource.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,17 +362,20 @@ For `kubectl get` and similar commands, you can also use the short name `pol` in

The WAF policy configures NGINX Plus to secure client requests using App Protect policies.

For example, the following policy will enable the referenced APPolicy and APLogConf with the configured log destination:
For example, the following policy will enable the referenced APPolicy. You can configure multiple APLogConfs with log destinations:
```yaml
waf:
enable: true
apPolicy: "default/dataguard-alarm"
securityLog:
enable: true
securityLogs:
- enable: true
apLogConf: "default/logconf"
logDest: "syslog:server=syslog-svc.default:514"
- enable: true
apLogConf: "default/logconf"
logDest: "syslog:server=syslog-svc-secondary.default:514"
```

> Note: The field `waf.securityLog` is supported but will be ignored if `waf.securityLogs` is populated.
> Note: The feature is implemented using the NGINX Plus [NGINX App Protect Module](https://docs.nginx.com/nginx-app-protect/configuration/).

{{% table %}}
Expand Down
4 changes: 2 additions & 2 deletions examples/custom-resources/waf/waf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ spec:
waf:
enable: true
apPolicy: "default/dataguard-alarm"
securityLog:
enable: true
securityLogs:
- enable: true
apLogConf: "default/logconf"
logDest: "syslog:server=syslog-svc.default:514"
2 changes: 1 addition & 1 deletion internal/configs/version2/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ type WAF struct {
Enable string
ApPolicy string
ApSecurityLogEnable bool
ApLogConf string
ApLogConf []string
}

// Dos defines Dos configuration.
Expand Down
4 changes: 3 additions & 1 deletion internal/configs/version2/nginx-plus.virtualserver.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,9 @@ server {

{{ if .ApSecurityLogEnable }}
app_protect_security_log_enable on;
app_protect_security_log {{ .ApLogConf }};
{{ range $logconf := .ApLogConf }}
app_protect_security_log {{ $logconf }};
{{ end }}
{{ end }}
{{ end }}

Expand Down
2 changes: 1 addition & 1 deletion internal/configs/version2/templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ var virtualServerCfg = VirtualServerConfig{
WAF: &WAF{
ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
ApSecurityLogEnable: true,
ApLogConf: "/etc/nginx/waf/nac-logconfs/default-logconf",
ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"},
},
Snippets: []string{"# server snippet"},
InternalRedirectLocations: []InternalRedirectLocation{
Expand Down
22 changes: 20 additions & 2 deletions internal/configs/virtualserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,7 @@ func (p *policiesCfg) addWAFConfig(
}
}

if waf.SecurityLog != nil {
if waf.SecurityLog != nil && waf.SecurityLogs == nil {
p.WAF.ApSecurityLogEnable = true

logConfKey := waf.SecurityLog.ApLogConf
Expand All @@ -1052,13 +1052,31 @@ func (p *policiesCfg) addWAFConfig(

if logConfPath, ok := apResources.LogConfs[logConfKey]; ok {
logDest := generateString(waf.SecurityLog.LogDest, "syslog:server=localhost:514")
p.WAF.ApLogConf = fmt.Sprintf("%s %s", logConfPath, logDest)
p.WAF.ApLogConf = []string{fmt.Sprintf("%s %s", logConfPath, logDest)}
} else {
res.addWarningf("WAF policy %s references an invalid or non-existing log config %s", polKey, logConfKey)
res.isError = true
}
}

if waf.SecurityLogs != nil {
p.WAF.ApSecurityLogEnable = true
p.WAF.ApLogConf = []string{}
for _, loco := range waf.SecurityLogs {
logConfKey := loco.ApLogConf
hasNamepace := strings.Contains(logConfKey, "/")
if !hasNamepace {
logConfKey = fmt.Sprintf("%v/%v", polNamespace, logConfKey)
}
if logConfPath, ok := apResources.LogConfs[logConfKey]; ok {
logDest := generateString(loco.LogDest, "syslog:server=localhost:514")
p.WAF.ApLogConf = append(p.WAF.ApLogConf, fmt.Sprintf("%s %s", logConfPath, logDest))
} else {
res.addWarningf("WAF policy %s references an invalid or non-existing log config %s", polKey, logConfKey)
res.isError = true
}
}
}
return res
}

Expand Down
48 changes: 35 additions & 13 deletions internal/configs/virtualserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2924,7 +2924,7 @@ func TestGeneratePolicies(t *testing.T) {
Enable: "on",
ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
ApSecurityLogEnable: true,
ApLogConf: "/etc/nginx/waf/nac-logconfs/default-logconf syslog:server=127.0.0.1:514",
ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf syslog:server=127.0.0.1:514"},
},
},
msg: "WAF reference",
Expand Down Expand Up @@ -6673,7 +6673,6 @@ func TestGenerateHealthCheck(t *testing.T) {
msg string
}{
{

upstream: conf_v1.Upstream{
HealthCheck: &conf_v1.HealthCheck{
Enable: true,
Expand Down Expand Up @@ -6854,7 +6853,6 @@ func TestGenerateGrpcHealthCheck(t *testing.T) {
msg string
}{
{

upstream: conf_v1.Upstream{
HealthCheck: &conf_v1.HealthCheck{
Enable: true,
Expand Down Expand Up @@ -8174,7 +8172,6 @@ func TestAddWafConfig(t *testing.T) {
msg string
}{
{

wafInput: &conf_v1.WAF{
Enable: true,
},
Expand All @@ -8191,7 +8188,6 @@ func TestAddWafConfig(t *testing.T) {
msg: "valid waf config, default App Protect config",
},
{

wafInput: &conf_v1.WAF{
Enable: true,
ApPolicy: "dataguard-alarm",
Expand All @@ -8214,13 +8210,42 @@ func TestAddWafConfig(t *testing.T) {
wafConfig: &version2.WAF{
ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
ApSecurityLogEnable: true,
ApLogConf: "/etc/nginx/waf/nac-logconfs/default-logconf",
ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"},
},
expected: &validationResults{isError: false},
msg: "valid waf config",
},
{
wafInput: &conf_v1.WAF{
Enable: true,
ApPolicy: "dataguard-alarm",
SecurityLogs: []*conf_v1.SecurityLog{
{
Enable: true,
ApLogConf: "logconf",
LogDest: "syslog:server=127.0.0.1:514",
},
},
},
polKey: "default/waf-policy",
polNamespace: "default",
apResources: &appProtectResourcesForVS{
Policies: map[string]string{
"default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
},
LogConfs: map[string]string{
"default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf",
},
},
wafConfig: &version2.WAF{
ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
ApSecurityLogEnable: true,
ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"},
},
expected: &validationResults{isError: false},
msg: "valid waf config",
},
{

wafInput: &conf_v1.WAF{
Enable: true,
ApPolicy: "default/dataguard-alarm",
Expand All @@ -8241,7 +8266,7 @@ func TestAddWafConfig(t *testing.T) {
wafConfig: &version2.WAF{
ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
ApSecurityLogEnable: true,
ApLogConf: "/etc/nginx/waf/nac-logconfs/default-logconf",
ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"},
},
expected: &validationResults{
isError: true,
Expand All @@ -8252,7 +8277,6 @@ func TestAddWafConfig(t *testing.T) {
msg: "invalid waf config, apLogConf references non-existing log conf",
},
{

wafInput: &conf_v1.WAF{
Enable: true,
ApPolicy: "default/dataguard-alarm",
Expand All @@ -8272,7 +8296,7 @@ func TestAddWafConfig(t *testing.T) {
wafConfig: &version2.WAF{
ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
ApSecurityLogEnable: true,
ApLogConf: "/etc/nginx/waf/nac-logconfs/default-logconf",
ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"},
},
expected: &validationResults{
isError: true,
Expand All @@ -8283,7 +8307,6 @@ func TestAddWafConfig(t *testing.T) {
msg: "invalid waf config, apLogConf references non-existing ap conf",
},
{

wafInput: &conf_v1.WAF{
Enable: true,
ApPolicy: "ns1/dataguard-alarm",
Expand All @@ -8306,13 +8329,12 @@ func TestAddWafConfig(t *testing.T) {
wafConfig: &version2.WAF{
ApPolicy: "/etc/nginx/waf/nac-policies/ns1-dataguard-alarm",
ApSecurityLogEnable: true,
ApLogConf: "/etc/nginx/waf/nac-logconfs/ns2-logconf",
ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/ns2-logconf"},
},
expected: &validationResults{},
msg: "valid waf config, cross ns reference",
},
{

wafInput: &conf_v1.WAF{
Enable: false,
ApPolicy: "dataguard-alarm",
Expand Down
43 changes: 34 additions & 9 deletions internal/k8s/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -2786,19 +2786,37 @@ func (lbc *LoadBalancerController) addWAFPolicyRefs(
apPolRef[apPolKey] = apPolicy
}

if pol.Spec.WAF.SecurityLog != nil && pol.Spec.WAF.SecurityLog.ApLogConf != "" {
logConfKey := pol.Spec.WAF.SecurityLog.ApLogConf
if !strings.Contains(pol.Spec.WAF.SecurityLog.ApLogConf, "/") {
logConfKey = fmt.Sprintf("%v/%v", pol.Namespace, logConfKey)
}
if pol.Spec.WAF.SecurityLog != nil && pol.Spec.WAF.SecurityLogs == nil {
if pol.Spec.WAF.SecurityLog.ApLogConf != "" {
logConfKey := pol.Spec.WAF.SecurityLog.ApLogConf
if !strings.Contains(pol.Spec.WAF.SecurityLog.ApLogConf, "/") {
logConfKey = fmt.Sprintf("%v/%v", pol.Namespace, logConfKey)
}

logConf, err := lbc.appProtectConfiguration.GetAppResource(appprotect.LogConfGVK.Kind, logConfKey)
if err != nil {
return fmt.Errorf("WAF policy %q is invalid: %w", logConfKey, err)
logConf, err := lbc.appProtectConfiguration.GetAppResource(appprotect.LogConfGVK.Kind, logConfKey)
if err != nil {
return fmt.Errorf("WAF policy %q is invalid: %w", logConfKey, err)
}
logConfRef[logConfKey] = logConf
}
logConfRef[logConfKey] = logConf
}

if pol.Spec.WAF.SecurityLogs != nil {
for _, SecLog := range pol.Spec.WAF.SecurityLogs {
if SecLog.ApLogConf != "" {
logConfKey := SecLog.ApLogConf
if !strings.Contains(SecLog.ApLogConf, "/") {
logConfKey = fmt.Sprintf("%v/%v", pol.Namespace, logConfKey)
}

logConf, err := lbc.appProtectConfiguration.GetAppResource(appprotect.LogConfGVK.Kind, logConfKey)
if err != nil {
return fmt.Errorf("WAF policy %q is invalid: %w", logConfKey, err)
}
logConfRef[logConfKey] = logConf
}
}
}
}
return nil
}
Expand Down Expand Up @@ -2846,6 +2864,13 @@ func getWAFPoliciesForAppProtectLogConf(pols []*conf_v1.Policy, key string) []*c
if pol.Spec.WAF != nil && pol.Spec.WAF.SecurityLog != nil && isMatchingResourceRef(pol.Namespace, pol.Spec.WAF.SecurityLog.ApLogConf, key) {
policies = append(policies, pol)
}
if pol.Spec.WAF != nil && pol.Spec.WAF.SecurityLogs != nil {
for _, logConf := range pol.Spec.WAF.SecurityLogs {
if isMatchingResourceRef(pol.Namespace, logConf.ApLogConf, key) {
policies = append(policies, pol)
}
}
}
}

return policies
Expand Down
Loading

0 comments on commit e93d010

Please sign in to comment.