Skip to content

Commit 2a84dfd

Browse files
committed
multiple syslog server support
1 parent 95d0916 commit 2a84dfd

File tree

9 files changed

+275
-72
lines changed

9 files changed

+275
-72
lines changed

internal/collector/otel_collector_plugin.go

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -543,17 +543,12 @@ func (oc *Collector) updateExistingNginxOSSReceiver(
543543

544544
func (oc *Collector) updateTcplogReceivers(nginxConfigContext *model.NginxConfigContext) bool {
545545
newTcplogReceiverAdded := false
546-
if nginxConfigContext.NAPSysLogServers != nil {
547-
napLoop:
548-
for _, napSysLogServer := range nginxConfigContext.NAPSysLogServers {
549-
if oc.doesTcplogReceiverAlreadyExist(napSysLogServer) {
550-
continue napLoop
551-
}
552-
546+
if nginxConfigContext.NAPSysLogServer != "" {
547+
if !oc.doesTcplogReceiverAlreadyExist(nginxConfigContext.NAPSysLogServer) {
553548
oc.config.Collector.Receivers.TcplogReceivers = append(
554549
oc.config.Collector.Receivers.TcplogReceivers,
555550
config.TcplogReceiver{
556-
ListenAddress: napSysLogServer,
551+
ListenAddress: nginxConfigContext.NAPSysLogServer,
557552
Operators: []config.Operator{
558553
{
559554
Type: "add",
@@ -621,12 +616,10 @@ func (oc *Collector) configDeletedNapReceivers(nginxConfigContext *model.NginxCo
621616
elements[tcplogReceiver.ListenAddress] = true
622617
}
623618

624-
if nginxConfigContext.NAPSysLogServers != nil {
619+
if nginxConfigContext.NAPSysLogServer != "" {
625620
addressesToDelete := make(map[string]bool)
626-
for _, napAddress := range nginxConfigContext.NAPSysLogServers {
627-
if !elements[napAddress] {
628-
addressesToDelete[napAddress] = true
629-
}
621+
if !elements[nginxConfigContext.NAPSysLogServer] {
622+
addressesToDelete[nginxConfigContext.NAPSysLogServer] = true
630623
}
631624

632625
return addressesToDelete

internal/collector/otel_collector_plugin_test.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -734,9 +734,7 @@ func TestCollector_updateTcplogReceivers(t *testing.T) {
734734
require.NoError(t, err)
735735

736736
nginxConfigContext := &model.NginxConfigContext{
737-
NAPSysLogServers: []string{
738-
"localhost:151",
739-
},
737+
NAPSysLogServer: "localhost:151",
740738
}
741739

742740
assert.Empty(t, conf.Collector.Receivers.TcplogReceivers)
@@ -767,9 +765,8 @@ func TestCollector_updateTcplogReceivers(t *testing.T) {
767765
})
768766

769767
t.Run("Test 4: New tcplogReceiver added and deleted another", func(tt *testing.T) {
770-
tcplogReceiverDeleted := collector.updateTcplogReceivers(&model.NginxConfigContext{NAPSysLogServers: []string{
771-
"localhost:152",
772-
}})
768+
tcplogReceiverDeleted := collector.
769+
updateTcplogReceivers(&model.NginxConfigContext{NAPSysLogServer: "localhost:152"})
773770
assert.True(t, tcplogReceiverDeleted)
774771
assert.Len(t, conf.Collector.Receivers.TcplogReceivers, 1)
775772
assert.Equal(t, "localhost:152", conf.Collector.Receivers.TcplogReceivers[0].ListenAddress)

internal/datasource/config/nginx_config_parser.go

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -173,18 +173,16 @@ func (ncp *NginxConfigParser) createNginxConfigContext(
173173
}
174174
case "app_protect_security_log":
175175
if len(directive.Args) > 1 {
176-
syslogArg := directive.Args[1]
177-
re := regexp.MustCompile(`syslog:server=([\S]+)`)
178-
matches := re.FindStringSubmatch(syslogArg)
179-
if len(matches) > 1 {
180-
syslogServer := matches[1]
181-
if !napSyslogServersFound[syslogServer] {
182-
nginxConfigContext.NAPSysLogServers = append(
183-
nginxConfigContext.NAPSysLogServers,
184-
syslogServer,
185-
)
186-
napSyslogServersFound[syslogServer] = true
187-
slog.DebugContext(ctx, "Found NAP syslog server", "address", syslogServer)
176+
slog.Info("args", "", directive.Args)
177+
sysLogServers := ncp.findValidSysLogServers(directive.Args)
178+
if len(sysLogServers) == 0 {
179+
slog.WarnContext(ctx, "Could not find valid Nap Syslog server")
180+
}
181+
for i := range sysLogServers {
182+
sysLogServer := sysLogServers[i]
183+
if !napSyslogServersFound[sysLogServer] {
184+
napSyslogServersFound[sysLogServer] = true
185+
slog.DebugContext(ctx, "Found NAP syslog server", "address", sysLogServer)
188186
}
189187
}
190188
}
@@ -207,6 +205,13 @@ func (ncp *NginxConfigParser) createNginxConfigContext(
207205
nginxConfigContext.PlusAPI = plusAPI
208206
}
209207

208+
if len(napSyslogServersFound) > 0 {
209+
syslogServer := ncp.parseSyslogDirective(ctx, napSyslogServersFound)
210+
if syslogServer != "" {
211+
nginxConfigContext.NAPSysLogServer = syslogServer
212+
}
213+
}
214+
210215
fileMeta, err := files.FileMeta(conf.File)
211216
if err != nil {
212217
slog.WarnContext(ctx, "Unable to get file metadata", "file_name", conf.File, "error", err)
@@ -218,6 +223,40 @@ func (ncp *NginxConfigParser) createNginxConfigContext(
218223
return nginxConfigContext, nil
219224
}
220225

226+
func (ncp *NginxConfigParser) parseSyslogDirective(ctx context.Context, napSyslogServers map[string]bool) string {
227+
for napSyslogServer := range napSyslogServers {
228+
ln, err := net.Listen("tcp", napSyslogServer)
229+
if err != nil {
230+
slog.WarnContext(ctx, "NAP syslog server is not reachable", "address", napSyslogServer,
231+
"error", err)
232+
233+
continue
234+
}
235+
ln.Close()
236+
slog.DebugContext(ctx, "Found valid NAP syslog server", "address", napSyslogServer)
237+
238+
return napSyslogServer
239+
}
240+
slog.WarnContext(ctx, "Could not find usable NAP syslog server")
241+
242+
return ""
243+
}
244+
245+
func (ncp *NginxConfigParser) findValidSysLogServers(sysLogServers []string) []string {
246+
re := regexp.MustCompile(`syslog:server=([\S]+)`)
247+
var servers []string
248+
for i := range sysLogServers {
249+
matches := re.FindStringSubmatch(sysLogServers[i])
250+
if len(matches) > 1 {
251+
if strings.HasPrefix(matches[1], "localhost") || strings.HasPrefix(matches[1], "127.0.0.1") {
252+
servers = append(servers, matches[1])
253+
}
254+
}
255+
}
256+
257+
return servers
258+
}
259+
221260
func (ncp *NginxConfigParser) parseIncludeDirective(directive *crossplane.Directive) string {
222261
var include string
223262
if filepath.IsAbs(directive.Args[0]) {

internal/datasource/config/nginx_config_parser_test.go

Lines changed: 120 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"bytes"
1010
"context"
1111
"fmt"
12+
"net"
1213
"net/http"
1314
"net/http/httptest"
1415
"os"
@@ -357,7 +358,7 @@ func TestNginxConfigParser_Parse(t *testing.T) {
357358
ltsvAccessLog.Name(),
358359
errorLog.Name(),
359360
protos.NginxOssInstance([]string{}).GetInstanceMeta().GetInstanceId(),
360-
[]string{"127.0.0.1:1515"},
361+
"127.0.0.1:1515",
361362
),
362363
expectedLog: "",
363364
allowedDirectories: []string{dir},
@@ -377,7 +378,7 @@ func TestNginxConfigParser_Parse(t *testing.T) {
377378
ltsvAccessLog.Name(),
378379
errorLog.Name(),
379380
protos.NginxPlusInstance([]string{}).GetInstanceMeta().GetInstanceId(),
380-
[]string{"127.0.0.1:1515"},
381+
"127.0.0.1:1515",
381382
),
382383
expectedLog: "",
383384
allowedDirectories: []string{dir},
@@ -392,7 +393,7 @@ func TestNginxConfigParser_Parse(t *testing.T) {
392393
errorLog.Name(),
393394
[]*mpi.File{&allowedFileWithMetas},
394395
protos.NginxPlusInstance([]string{}).GetInstanceMeta().GetInstanceId(),
395-
nil,
396+
"",
396397
),
397398
expectedLog: "",
398399
allowedDirectories: []string{dir},
@@ -402,13 +403,13 @@ func TestNginxConfigParser_Parse(t *testing.T) {
402403
instance: protos.NginxPlusInstance([]string{}),
403404
content: testconfig.NginxConfWithSSLCertsWithVariables(),
404405
expectedConfigContext: &model.NginxConfigContext{
405-
StubStatus: &model.APIDetails{},
406-
PlusAPI: &model.APIDetails{},
407-
InstanceID: protos.NginxPlusInstance([]string{}).GetInstanceMeta().GetInstanceId(),
408-
Files: []*mpi.File{},
409-
AccessLogs: []*model.AccessLog{},
410-
ErrorLogs: []*model.ErrorLog{},
411-
NAPSysLogServers: nil,
406+
StubStatus: &model.APIDetails{},
407+
PlusAPI: &model.APIDetails{},
408+
InstanceID: protos.NginxPlusInstance([]string{}).GetInstanceMeta().GetInstanceId(),
409+
Files: []*mpi.File{},
410+
AccessLogs: []*model.AccessLog{},
411+
ErrorLogs: []*model.ErrorLog{},
412+
NAPSysLogServer: "",
412413
},
413414
allowedDirectories: []string{dir},
414415
},
@@ -426,7 +427,7 @@ func TestNginxConfigParser_Parse(t *testing.T) {
426427
combinedAccessLog.Name(),
427428
ltsvAccessLog.Name(),
428429
protos.NginxPlusInstance([]string{}).GetInstanceMeta().GetInstanceId(),
429-
[]string{"127.0.0.1:1515"},
430+
"127.0.0.1:1515",
430431
),
431432
expectedLog: "Currently error log outputs to stderr. Log monitoring is disabled while applying a " +
432433
"config; log errors to file to enable error monitoring",
@@ -446,7 +447,7 @@ func TestNginxConfigParser_Parse(t *testing.T) {
446447
combinedAccessLog.Name(),
447448
ltsvAccessLog.Name(),
448449
protos.NginxPlusInstance([]string{}).GetInstanceMeta().GetInstanceId(),
449-
[]string{"127.0.0.1:1515"},
450+
"127.0.0.1:1515",
450451
),
451452
expectedLog: "Currently error log outputs to stdout. Log monitoring is disabled while applying a " +
452453
"config; log errors to file to enable error monitoring",
@@ -465,7 +466,7 @@ func TestNginxConfigParser_Parse(t *testing.T) {
465466
errorLog.Name(),
466467
[]*mpi.File{&certFileWithMetas},
467468
protos.NginxPlusInstance([]string{}).GetInstanceMeta().GetInstanceId(),
468-
nil,
469+
"",
469470
),
470471
allowedDirectories: []string{dir},
471472
},
@@ -483,7 +484,7 @@ func TestNginxConfigParser_Parse(t *testing.T) {
483484
errorLog.Name(),
484485
[]*mpi.File{&diffCertFileWithMetas, &certFileWithMetas},
485486
protos.NginxPlusInstance([]string{}).GetInstanceMeta().GetInstanceId(),
486-
nil,
487+
"",
487488
),
488489
allowedDirectories: []string{dir},
489490
},
@@ -501,7 +502,7 @@ func TestNginxConfigParser_Parse(t *testing.T) {
501502
errorLog.Name(),
502503
[]*mpi.File{&certFileWithMetas},
503504
protos.NginxPlusInstance([]string{}).GetInstanceMeta().GetInstanceId(),
504-
nil,
505+
"",
505506
),
506507
allowedDirectories: []string{dir},
507508
},
@@ -532,6 +533,7 @@ func TestNginxConfigParser_Parse(t *testing.T) {
532533
result, parseError := nginxConfig.Parse(ctx, test.instance)
533534
require.NoError(t, parseError)
534535

536+
t.Logf("Log: %s", logBuf.String())
535537
helpers.ValidateLog(t, test.expectedLog, logBuf)
536538
logBuf.Reset()
537539

@@ -548,7 +550,7 @@ func TestNginxConfigParser_Parse(t *testing.T) {
548550
assert.Truef(t,
549551
protoListEqual(test.expectedConfigContext.Files, result.Files),
550552
"Expect %s Got %s", test.expectedConfigContext.Files, result.Files)
551-
assert.Equal(t, test.expectedConfigContext.NAPSysLogServers, result.NAPSysLogServers)
553+
assert.Equal(t, test.expectedConfigContext.NAPSysLogServer, result.NAPSysLogServer)
552554
assert.Equal(t, test.expectedConfigContext.PlusAPI, result.PlusAPI)
553555
assert.ElementsMatch(t, test.expectedConfigContext.AccessLogs, result.AccessLogs)
554556
assert.ElementsMatch(t, test.expectedConfigContext.ErrorLogs, result.ErrorLogs)
@@ -582,6 +584,107 @@ func TestNginxConfigParser_sslCert(t *testing.T) {
582584
assert.Equal(t, certFile, sslCert.GetFileMeta().GetName())
583585
}
584586

587+
func TestNginxConfigParser_SyslogServerParse(t *testing.T) {
588+
ctx := context.Background()
589+
dir := t.TempDir()
590+
591+
file := helpers.CreateFileWithErrorCheck(t, dir, "nginx-parse-config.conf")
592+
defer helpers.RemoveFileWithErrorCheck(t, file.Name())
593+
594+
errorLog := helpers.CreateFileWithErrorCheck(t, dir, "error.log")
595+
defer helpers.RemoveFileWithErrorCheck(t, errorLog.Name())
596+
597+
accessLog := helpers.CreateFileWithErrorCheck(t, dir, "access.log")
598+
defer helpers.RemoveFileWithErrorCheck(t, accessLog.Name())
599+
600+
instance := protos.NginxOssInstance([]string{})
601+
instance.InstanceRuntime.ConfigPath = file.Name()
602+
603+
tests := []struct {
604+
name string
605+
content string
606+
expectedLog string
607+
expectedSyslogServers string
608+
portInUse bool
609+
}{
610+
{
611+
name: "Test 1: Valid port",
612+
expectedSyslogServers: "127.0.0.1:1515",
613+
content: testconfig.NginxConfigWithMultipleSysLogs(errorLog.Name(), accessLog.Name(),
614+
"192.168.12.34:1517", "my.domain.com:1517", "127.0.0.1:1515"),
615+
expectedLog: "Found valid NAP syslog server",
616+
portInUse: false,
617+
},
618+
{
619+
name: "Test 2: No valid server",
620+
expectedSyslogServers: "",
621+
content: testconfig.NginxConfigWithMultipleSysLogs(errorLog.Name(), accessLog.Name(),
622+
"random.domain:1515", "192.168.12.34:1517", "my.domain.com:1517"),
623+
expectedLog: "Could not find valid Nap Syslog server",
624+
portInUse: false,
625+
},
626+
{
627+
name: "Test 3: Port unavailable, use next valid sever",
628+
expectedSyslogServers: "localhost:1516",
629+
content: testconfig.NginxConfigWithMultipleSysLogs(errorLog.Name(), accessLog.Name(),
630+
"192.168.12.34:1517", "127.0.0.1:1515", "localhost:1516"),
631+
expectedLog: "NAP syslog server is not reachable",
632+
portInUse: true,
633+
},
634+
{
635+
name: "Test 4: Port unavailable, no server available",
636+
expectedSyslogServers: "",
637+
content: testconfig.NginxConfigWithMultipleSysLogs(errorLog.Name(), accessLog.Name(),
638+
"my.domain.com:1517", "127.0.0.1:1515", "my.domain.com:1517"),
639+
expectedLog: "Could not find usable NAP syslog server",
640+
portInUse: true,
641+
},
642+
}
643+
644+
for _, test := range tests {
645+
t.Run(test.name, func(t *testing.T) {
646+
logBuf := &bytes.Buffer{}
647+
stub.StubLoggerWith(logBuf)
648+
649+
agentConfig := types.AgentConfig()
650+
agentConfig.AllowedDirectories = []string{dir}
651+
nginxConfig := NewNginxConfigParser(agentConfig)
652+
653+
writeErr := os.WriteFile(file.Name(), []byte(test.content), 0o600)
654+
require.NoError(t, writeErr)
655+
656+
if test.portInUse {
657+
ln, err := net.Listen("tcp", "127.0.0.1:1515")
658+
require.NoError(t, err)
659+
defer ln.Close()
660+
}
661+
662+
result, parseError := nginxConfig.Parse(ctx, instance)
663+
require.NoError(t, parseError)
664+
665+
t.Logf("Log: %s", logBuf.String())
666+
helpers.ValidateLog(t, test.expectedLog, logBuf)
667+
logBuf.Reset()
668+
669+
assert.Equal(t, test.expectedSyslogServers, result.NAPSysLogServer)
670+
})
671+
}
672+
}
673+
674+
func TestNginxConfigParser_findValidSysLogServers(t *testing.T) {
675+
servers := []string{
676+
"/etc/app_protect/conf/log_default.json", "syslog:server=192.168.12.34:1517",
677+
"app_protect_security_log", "/etc/app_protect/conf/log_default1.json", "syslog:server=my.domain.com:1517",
678+
"app_protect_security_log", "/etc/app_protect/conf/log_default2.json", "syslog:server=127.0.0.1:1515",
679+
"app_protect_security_log", "/etc/app_protect/conf/log_default3.json", "syslog:server=localhost:1516",
680+
}
681+
expected := []string{"127.0.0.1:1515", "localhost:1516"}
682+
ncp := NewNginxConfigParser(types.AgentConfig())
683+
result := ncp.findValidSysLogServers(servers)
684+
685+
assert.Equal(t, expected, result)
686+
}
687+
585688
func TestNginxConfigParser_checkLog(t *testing.T) {
586689
logBuf := &bytes.Buffer{}
587690
stub.StubLoggerWith(logBuf)
@@ -1067,6 +1170,7 @@ func TestNginxConfigParser_pingAPIEndpoint_PlusAPI(t *testing.T) {
10671170
})
10681171

10691172
fakeServer := httptest.NewServer(handler)
1173+
t.Logf(fakeServer.URL)
10701174
defer fakeServer.Close()
10711175

10721176
nginxConfigParser := NewNginxConfigParser(types.AgentConfig())

0 commit comments

Comments
 (0)