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 May 10, 2022
1 parent 8b53e1e commit 2ae0333
Show file tree
Hide file tree
Showing 20 changed files with 466 additions and 56 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 @@ -355,17 +355,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 deprecated and will be removed in future releases.It 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"
16 changes: 10 additions & 6 deletions internal/configs/ingress.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ type MergeableIngresses struct {
}

func generateNginxCfg(ingEx *IngressEx, apResources *AppProtectResources, dosResource *appProtectDosResource, isMinion bool,
baseCfgParams *ConfigParams, isPlus bool, isResolverConfigured bool, staticParams *StaticConfigParams, isWildcardEnabled bool) (version1.IngressNginxConfig, Warnings) {
baseCfgParams *ConfigParams, isPlus bool, isResolverConfigured bool, staticParams *StaticConfigParams, isWildcardEnabled bool,
) (version1.IngressNginxConfig, Warnings) {
hasAppProtect := staticParams.MainAppProtectLoadModule
hasAppProtectDos := staticParams.MainAppProtectDosLoadModule

Expand Down Expand Up @@ -290,7 +291,8 @@ func generateNginxCfg(ingEx *IngressEx, apResources *AppProtectResources, dosRes
}

func generateJWTConfig(owner runtime.Object, secretRefs map[string]*secrets.SecretReference, cfgParams *ConfigParams,
redirectLocationName string) (*version1.JWTAuth, *version1.JWTRedirectLocation, Warnings) {
redirectLocationName string,
) (*version1.JWTAuth, *version1.JWTRedirectLocation, Warnings) {
warnings := newWarnings()

secretRef := secretRefs[cfgParams.JWTKey]
Expand Down Expand Up @@ -326,7 +328,8 @@ func generateJWTConfig(owner runtime.Object, secretRefs map[string]*secrets.Secr
}

func addSSLConfig(server *version1.Server, owner runtime.Object, host string, ingressTLS []networking.IngressTLS,
secretRefs map[string]*secrets.SecretReference, isWildcardEnabled bool) Warnings {
secretRefs map[string]*secrets.SecretReference, isWildcardEnabled bool,
) Warnings {
warnings := newWarnings()

var tlsEnabled bool
Expand Down Expand Up @@ -427,7 +430,8 @@ func upstreamRequiresQueue(name string, ingEx *IngressEx, cfg *ConfigParams) (n
}

func createUpstream(ingEx *IngressEx, name string, backend *networking.IngressBackend, stickyCookie string, cfg *ConfigParams,
isPlus bool, isResolverConfigured bool, isLatencyMetricsEnabled bool) version1.Upstream {
isPlus bool, isResolverConfigured bool, isLatencyMetricsEnabled bool,
) version1.Upstream {
var ups version1.Upstream
labels := version1.UpstreamLabels{
Service: backend.Service.Name,
Expand Down Expand Up @@ -534,8 +538,8 @@ func upstreamMapToSlice(upstreams map[string]version1.Upstream) []version1.Upstr

func generateNginxCfgForMergeableIngresses(mergeableIngs *MergeableIngresses, apResources *AppProtectResources,
dosResource *appProtectDosResource, baseCfgParams *ConfigParams, isPlus bool, isResolverConfigured bool,
staticParams *StaticConfigParams, isWildcardEnabled bool) (version1.IngressNginxConfig, Warnings) {

staticParams *StaticConfigParams, isWildcardEnabled bool,
) (version1.IngressNginxConfig, Warnings) {
var masterServer version1.Server
var locations []version1.Location
var upstreams []version1.Upstream
Expand Down
1 change: 0 additions & 1 deletion internal/configs/version1/templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ var (
)

var ingCfg = IngressNginxConfig{

Servers: []Server{
{
Name: "test.example.com",
Expand Down
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
8 changes: 6 additions & 2 deletions internal/configs/version2/nginx-plus.virtualserver.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,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 Expand Up @@ -370,7 +372,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
38 changes: 31 additions & 7 deletions internal/configs/virtualserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,8 @@ func (p *policiesCfg) addWAFConfig(
}
}

if waf.SecurityLog != nil {
if waf.SecurityLog != nil && waf.SecurityLogs == nil {
glog.V(2).Info("the field securityLog is deprecated nad will be removed in future releases. Use field securityLogs instead")
p.WAF.ApSecurityLogEnable = true

logConfKey := waf.SecurityLog.ApLogConf
Expand All @@ -1052,13 +1053,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 Expand Up @@ -1575,7 +1594,8 @@ type errorPageDetails struct {
func generateLocation(path string, upstreamName string, upstream conf_v1.Upstream, action *conf_v1.Action,
cfgParams *ConfigParams, errorPages errorPageDetails, internal bool, proxySSLName string,
originalPath string, locSnippets string, enableSnippets bool, retLocIndex int, isVSR bool, vsrName string,
vsrNamespace string, vscWarnings Warnings) (version2.Location, *version2.ReturnLocation) {
vsrNamespace string, vscWarnings Warnings,
) (version2.Location, *version2.ReturnLocation) {
locationSnippets := generateSnippets(enableSnippets, locSnippets, cfgParams.LocationSnippets)

if action.Redirect != nil {
Expand Down Expand Up @@ -1674,7 +1694,8 @@ func generateProxyAddHeaders(proxy *conf_v1.ActionProxy) []version2.AddHeader {

func generateLocationForProxying(path string, upstreamName string, upstream conf_v1.Upstream,
cfgParams *ConfigParams, errorPages []conf_v1.ErrorPage, internal bool, errPageIndex int,
proxySSLName string, proxy *conf_v1.ActionProxy, originalPath string, locationSnippets []string, isVSR bool, vsrName string, vsrNamespace string) version2.Location {
proxySSLName string, proxy *conf_v1.ActionProxy, originalPath string, locationSnippets []string, isVSR bool, vsrName string, vsrNamespace string,
) version2.Location {
return version2.Location{
Path: generatePath(path),
Internal: internal,
Expand Down Expand Up @@ -1741,7 +1762,8 @@ func generateLocationForRedirect(
}

func generateLocationForReturn(path string, locationSnippets []string, actionReturn *conf_v1.ActionReturn,
retLocIndex int) (version2.Location, *version2.ReturnLocation) {
retLocIndex int,
) (version2.Location, *version2.ReturnLocation) {
defaultType := actionReturn.Type
if defaultType == "" {
defaultType = "text/plain"
Expand Down Expand Up @@ -1873,7 +1895,8 @@ func generateDefaultSplitsConfig(

func generateMatchesConfig(route conf_v1.Route, upstreamNamer *upstreamNamer, crUpstreams map[string]conf_v1.Upstream,
variableNamer *variableNamer, index int, scIndex int, cfgParams *ConfigParams, errorPages errorPageDetails,
locSnippets string, enableSnippets bool, retLocIndex int, isVSR bool, vsrName string, vsrNamespace string, vscWarnings Warnings) routingCfg {
locSnippets string, enableSnippets bool, retLocIndex int, isVSR bool, vsrName string, vsrNamespace string, vscWarnings Warnings,
) routingCfg {
// Generate maps
var maps []version2.Map

Expand Down Expand Up @@ -2101,7 +2124,8 @@ func getNameForSourceForMatchesRouteMapFromCondition(condition conf_v1.Condition
}

func (vsc *virtualServerConfigurator) generateSSLConfig(owner runtime.Object, tls *conf_v1.TLS, namespace string,
secretRefs map[string]*secrets.SecretReference, cfgParams *ConfigParams) *version2.SSL {
secretRefs map[string]*secrets.SecretReference, cfgParams *ConfigParams,
) *version2.SSL {
if tls == nil {
return nil
}
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 @@ -2938,7 +2938,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 @@ -6723,7 +6723,6 @@ func TestGenerateHealthCheck(t *testing.T) {
msg string
}{
{

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

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

wafInput: &conf_v1.WAF{
Enable: true,
},
Expand All @@ -8265,7 +8262,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 @@ -8288,13 +8284,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 @@ -8315,7 +8340,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 @@ -8326,7 +8351,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 @@ -8346,7 +8370,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 @@ -8357,7 +8381,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 @@ -8380,13 +8403,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
Loading

0 comments on commit 2ae0333

Please sign in to comment.