Skip to content

Commit d202006

Browse files
authored
NGINX App Protect OTel syslog processor (#1262)
1 parent 0bc90df commit d202006

File tree

15 files changed

+897
-10
lines changed

15 files changed

+897
-10
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ require (
1616
github.com/google/uuid v1.6.0
1717
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
1818
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2
19+
github.com/leodido/go-syslog/v4 v4.2.0
1920
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c
2021
github.com/nginx/nginx-plus-go-client/v3 v3.0.1
2122
github.com/nginxinc/nginx-prometheus-exporter v1.3.0
@@ -180,7 +181,6 @@ require (
180181
github.com/knadh/koanf/providers/confmap v1.0.0 // indirect
181182
github.com/knadh/koanf/v2 v2.3.0 // indirect
182183
github.com/kylelemons/godebug v1.1.0 // indirect
183-
github.com/leodido/go-syslog/v4 v4.2.0 // indirect
184184
github.com/leodido/go-urn v1.4.0 // indirect
185185
github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b // indirect
186186
github.com/lightstep/go-expohisto v1.0.0 // indirect

internal/collector/factories.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/nginx/agent/v3/internal/collector/logsgzipprocessor"
1111
nginxreceiver "github.com/nginx/agent/v3/internal/collector/nginxossreceiver"
1212
"github.com/nginx/agent/v3/internal/collector/nginxplusreceiver"
13+
"github.com/nginx/agent/v3/internal/collector/securityviolationsprocessor"
1314

1415
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/prometheusexporter"
1516
"github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension"
@@ -104,6 +105,7 @@ func createProcessorFactories() map[component.Type]processor.Factory {
104105
memorylimiterprocessor.NewFactory(),
105106
redactionprocessor.NewFactory(),
106107
resourceprocessor.NewFactory(),
108+
securityviolationsprocessor.NewFactory(),
107109
transformprocessor.NewFactory(),
108110
logsgzipprocessor.NewFactory(),
109111
}

internal/collector/factories_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func TestOTelComponentFactoriesDefault(t *testing.T) {
1919
assert.NotNil(t, factories, "factories should not be nil")
2020

2121
assert.Len(t, factories.Receivers, 6)
22-
assert.Len(t, factories.Processors, 9)
22+
assert.Len(t, factories.Processors, 10)
2323
assert.Len(t, factories.Exporters, 4)
2424
assert.Len(t, factories.Extensions, 3)
2525
assert.Empty(t, factories.Connectors)

internal/collector/otel_collector_plugin_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ func TestCollector_ProcessResourceUpdateTopicFails(t *testing.T) {
423423
conf.Collector.Processors.Attribute = nil
424424
conf.Collector.Processors.Resource = nil
425425
conf.Collector.Processors.LogsGzip = nil
426+
conf.Collector.Processors.SecurityViolations = nil
426427
conf.Collector.Exporters.OtlpExporters = nil
427428
conf.Collector.Exporters.PrometheusExporter = &config.PrometheusExporter{
428429
Server: &config.ServerConfig{
@@ -747,6 +748,7 @@ func TestCollector_updateNginxAppProtectTcplogReceivers(t *testing.T) {
747748
conf.Collector.Processors.Attribute = nil
748749
conf.Collector.Processors.Resource = nil
749750
conf.Collector.Processors.LogsGzip = nil
751+
conf.Collector.Processors.SecurityViolations = nil
750752
collector, err := NewCollector(conf)
751753
require.NoError(t, err)
752754

@@ -929,6 +931,9 @@ func TestCollector_findAvailableSyslogServers(t *testing.T) {
929931
conf.Collector.Processors.Attribute = nil
930932
conf.Collector.Processors.Resource = nil
931933
conf.Collector.Processors.LogsGzip = nil
934+
conf.Collector.Processors.SecurityViolations = nil
935+
collector, err := NewCollector(conf)
936+
require.NoError(t, err)
932937

933938
tests := []struct {
934939
name string
@@ -983,7 +988,6 @@ func TestCollector_findAvailableSyslogServers(t *testing.T) {
983988

984989
for _, test := range tests {
985990
t.Run(test.name, func(tt *testing.T) {
986-
collector, err := NewCollector(conf)
987991
require.NoError(t, err)
988992

989993
collector.previousNAPSysLogServer = test.previousNAPSysLogServer

internal/collector/otelcol.tmpl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ processors:
166166
send_batch_max_size: {{ $batch.SendBatchMaxSize }}
167167
{{- end }}
168168
{{- end }}
169+
{{- if ne .Processors.SecurityViolations nil }}
170+
{{- range $key, $value := .Processors.SecurityViolations }}
171+
securityviolations/{{$key}}: {}
172+
{{- end }}
173+
{{- end }}
169174
{{- if ne .Processors.LogsGzip nil }}
170175
{{ range $key, $value := .Processors.LogsGzip }}
171176
logsgzip/{{$key}}: {}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SecurityViolations Processor
2+
3+
Internal component of the NGINX Agent that processes security violation syslog messages. Parses RFC3164 formatted syslog entries from log records and extracts structured attributes. Successfully parsed messages have their body replaced with the clean message content.
4+
5+
Part of the NGINX Agent's log collection pipeline.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) F5, Inc.
2+
//
3+
// This source code is licensed under the Apache License, Version 2.0 license found in the
4+
// LICENSE file in the root directory of this source tree.
5+
6+
package securityviolationsprocessor
7+
8+
import (
9+
"context"
10+
11+
"go.opentelemetry.io/collector/component"
12+
"go.opentelemetry.io/collector/consumer"
13+
"go.opentelemetry.io/collector/processor"
14+
)
15+
16+
const typeStr = "securityviolations"
17+
18+
// NewFactory creates a factory for the securityviolations processor.
19+
//
20+
//nolint:ireturn // factory methods return interfaces by design
21+
func NewFactory() processor.Factory {
22+
return processor.NewFactory(
23+
component.MustNewType(typeStr),
24+
func() component.Config { return &struct{}{} },
25+
processor.WithLogs(createSecurityViolationsProcessor, component.StabilityLevelAlpha),
26+
)
27+
}
28+
29+
// createSecurityViolationsProcessor instantiates the logs processor.
30+
//
31+
//nolint:ireturn // required to comply with component factory interface
32+
func createSecurityViolationsProcessor(
33+
_ context.Context,
34+
settings processor.Settings,
35+
_ component.Config,
36+
next consumer.Logs,
37+
) (processor.Logs, error) {
38+
settings.Logger.Info("Creating security violations processor")
39+
40+
return newSecurityViolationsProcessor(next, settings), nil
41+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (c) F5, Inc.
2+
//
3+
// This source code is licensed under the Apache License, Version 2.0 license found in the
4+
// LICENSE file in the root directory of this source tree.
5+
6+
package securityviolationsprocessor
7+
8+
// SecurityViolationEvent represents the structured NGINX App Protect security violation data
9+
type SecurityViolationEvent struct {
10+
PolicyName string `json:"policy_name"`
11+
SupportID string `json:"support_id"`
12+
Outcome string `json:"outcome"`
13+
OutcomeReason string `json:"outcome_reason"`
14+
BlockingExceptionReason string `json:"blocking_exception_reason"`
15+
Method string `json:"method"`
16+
Protocol string `json:"protocol"`
17+
XForwardedForHeaderValue string `json:"xff_header_value"`
18+
URI string `json:"uri"`
19+
Request string `json:"request"`
20+
IsTruncated string `json:"is_truncated"`
21+
RequestStatus string `json:"request_status"`
22+
ResponseCode string `json:"response_code"`
23+
ServerAddr string `json:"server_addr"`
24+
VSName string `json:"vs_name"`
25+
RemoteAddr string `json:"remote_addr"`
26+
RemotePort string `json:"destination_port"`
27+
ServerPort string `json:"server_port"`
28+
Violations string `json:"violations"`
29+
SubViolations string `json:"sub_violations"`
30+
ViolationRating string `json:"violation_rating"`
31+
SigSetNames string `json:"sig_set_names"`
32+
SigCVEs string `json:"sig_cves"`
33+
ClientClass string `json:"client_class"`
34+
ClientApplication string `json:"client_application"`
35+
ClientApplicationVersion string `json:"client_application_version"`
36+
Severity string `json:"severity"`
37+
ThreatCampaignNames string `json:"threat_campaign_names"`
38+
BotAnomalies string `json:"bot_anomalies"`
39+
BotCategory string `json:"bot_category"`
40+
EnforcedBotAnomalies string `json:"enforced_bot_anomalies"`
41+
BotSignatureName string `json:"bot_signature_name"`
42+
SystemID string `json:"system_id"`
43+
InstanceTags string `json:"instance_tags"`
44+
InstanceGroup string `json:"instance_group"`
45+
ParentHostname string `json:"parent_hostname"`
46+
DisplayName string `json:"display_name"`
47+
ViolationsData []ViolationData `json:"violations_data"`
48+
}
49+
50+
type ViolationData struct {
51+
Name string `json:"violation_data_name"`
52+
Context string `json:"violation_data_context"`
53+
ContextData ContextData `json:"violation_data_context_data"`
54+
Signatures []SignatureData `json:"violation_data_signatures"`
55+
}
56+
57+
// SignatureData represents signature data contained within each violation
58+
type SignatureData struct {
59+
ID string `json:"sig_data_id"`
60+
BlockingMask string `json:"sig_data_blocking_mask"`
61+
Buffer string `json:"sig_data_buffer"`
62+
Offset string `json:"sig_data_offset"`
63+
Length string `json:"sig_data_length"`
64+
}
65+
66+
// ContextData represents the context data of the violation
67+
type ContextData struct {
68+
Name string `json:"context_data_name"`
69+
Value string `json:"context_data_value"`
70+
}

0 commit comments

Comments
 (0)