Skip to content

Commit

Permalink
Support TLS for syslog (#487)
Browse files Browse the repository at this point in the history
Add TLS support for Log Insights integration using syslog.
Users can now specify the new `db_log_syslog_server_cert_file` and
`db_log_syslog_server_key_file` config settings, or
`LOG_SYSLOG_SERVER_CERT_FILE` and `LOG_SYSLOG_SERVER_KEY_FILE` environment
variables to configure a TLS certificate for the collector syslog server.
You can optionally specify not the file path but the content using
`db_log_syslog_server_cert_contents` and `db_log_syslog_server_key_contents`.
The Certificate Authority both on the server side and client side also can be
specified via config settings.
  • Loading branch information
keiko713 authored Dec 15, 2023
1 parent 34a0847 commit 036baf8
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 6 deletions.
11 changes: 10 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,17 @@ type ServerConfig struct {
LogDockerTail string `ini:"db_log_docker_tail"`

// Configures the collector to start a built-in syslog server that listens
// on the specifed "hostname:port" for Postgres log messages
// on the specified "hostname:port" for Postgres log messages
LogSyslogServer string `ini:"db_log_syslog_server"`
// For TLS support for syslog server
LogSyslogServerCAFile string `ini:"db_log_syslog_server_ca_file"`
LogSyslogServerCertFile string `ini:"db_log_syslog_server_cert_file"`
LogSyslogServerKeyFile string `ini:"db_log_syslog_server_key_file"`
LogSyslogServerClientCAFile string `ini:"db_log_syslog_server_client_ca_file"`
LogSyslogServerCAContents string `ini:"db_log_syslog_server_ca_contents"`
LogSyslogServerCertContents string `ini:"db_log_syslog_server_cert_contents"`
LogSyslogServerKeyContents string `ini:"db_log_syslog_server_key_contents"`
LogSyslogServerClientCAContents string `ini:"db_log_syslog_server_client_ca_contents"`

// Configures the collector to use the "pg_read_file" (superuser) or
// "pganalyze.read_log_file" (helper) function to retrieve log data
Expand Down
52 changes: 52 additions & 0 deletions config/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,30 @@ func getDefaultConfig() *ServerConfig {
if logSyslogServer := os.Getenv("LOG_SYSLOG_SERVER"); logSyslogServer != "" {
config.LogSyslogServer = logSyslogServer
}
if logSyslogServerCAFile := os.Getenv("LOG_SYSLOG_SERVER_CA_FILE"); logSyslogServerCAFile != "" {
config.LogSyslogServerCAFile = logSyslogServerCAFile
}
if logSyslogServerCertFile := os.Getenv("LOG_SYSLOG_SERVER_CERT_FILE"); logSyslogServerCertFile != "" {
config.LogSyslogServerCertFile = logSyslogServerCertFile
}
if logSyslogServerKeyFile := os.Getenv("LOG_SYSLOG_SERVER_KEY_FILE"); logSyslogServerKeyFile != "" {
config.LogSyslogServerKeyFile = logSyslogServerKeyFile
}
if logSyslogServerClientCAFile := os.Getenv("LOG_SYSLOG_SERVER_CLIENT_CA_FILE"); logSyslogServerClientCAFile != "" {
config.LogSyslogServerClientCAFile = logSyslogServerClientCAFile
}
if logSyslogServerCAContents := os.Getenv("LOG_SYSLOG_SERVER_CA_CONTENTS"); logSyslogServerCAContents != "" {
config.LogSyslogServerCAContents = logSyslogServerCAContents
}
if logSyslogServerCertContents := os.Getenv("LOG_SYSLOG_SERVER_CERT_CONTENTS"); logSyslogServerCertContents != "" {
config.LogSyslogServerCertContents = logSyslogServerCertContents
}
if logSyslogServerKeyContents := os.Getenv("LOG_SYSLOG_SERVER_KEY_CONTENTS"); logSyslogServerKeyContents != "" {
config.LogSyslogServerKeyContents = logSyslogServerKeyContents
}
if logSyslogServerClientCAContents := os.Getenv("LOG_SYSLOG_SERVER_CLIENT_CA_CONTENTS"); logSyslogServerClientCAContents != "" {
config.LogSyslogServerClientCAContents = logSyslogServerClientCAContents
}
if alwaysCollectSystemData := os.Getenv("PGA_ALWAYS_COLLECT_SYSTEM_DATA"); alwaysCollectSystemData != "" {
config.AlwaysCollectSystemData = parseConfigBool(alwaysCollectSystemData)
}
Expand Down Expand Up @@ -582,6 +606,34 @@ func preprocessConfig(config *ServerConfig) (*ServerConfig, error) {
config.LogPgReadFile = true
}

if config.LogSyslogServerCAContents != "" {
config.LogSyslogServerCAFile, err = writeValueToTempfile(config.LogSyslogServerCAContents)
if err != nil {
return config, err
}
}

if config.LogSyslogServerCertContents != "" {
config.LogSyslogServerCertFile, err = writeValueToTempfile(config.LogSyslogServerCertContents)
if err != nil {
return config, err
}
}

if config.LogSyslogServerKeyContents != "" {
config.LogSyslogServerKeyFile, err = writeValueToTempfile(config.LogSyslogServerKeyContents)
if err != nil {
return config, err
}
}

if config.LogSyslogServerClientCAContents != "" {
config.LogSyslogServerClientCAFile, err = writeValueToTempfile(config.LogSyslogServerClientCAContents)
if err != nil {
return config, err
}
}

return config, nil
}

Expand Down
2 changes: 1 addition & 1 deletion input/system/selfhosted/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func SetupLogTails(ctx context.Context, wg *sync.WaitGroup, globalCollectionOpts
}
} else if server.Config.LogSyslogServer != "" {
logStream := setupLogTransformer(ctx, wg, server, globalCollectionOpts, prefixedLogger, parsedLogStream)
err := setupSyslogHandler(ctx, server.Config.LogSyslogServer, logStream, prefixedLogger)
err := setupSyslogHandler(ctx, server.Config, logStream, prefixedLogger)
if err != nil {
prefixedLogger.PrintError("ERROR - %s", err)
}
Expand Down
66 changes: 62 additions & 4 deletions input/system/selfhosted/syslog_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,87 @@ package selfhosted

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"regexp"
"strconv"
"time"

"gopkg.in/mcuadros/go-syslog.v2"

"github.com/pganalyze/collector/config"
"github.com/pganalyze/collector/util"
)

var logLinePartsRegexp = regexp.MustCompile(`^\[(\d+)-(\d+)\] (.*)`)
var logLineNumberPartsRegexp = regexp.MustCompile(`^\[(\d+)-(\d+)\]$`)

func setupSyslogHandler(ctx context.Context, logSyslogServer string, out chan<- SelfHostedLogStreamItem, prefixedLogger *util.Logger) error {
func setupSyslogHandler(ctx context.Context, config config.ServerConfig, out chan<- SelfHostedLogStreamItem, prefixedLogger *util.Logger) error {
logSyslogServer := config.LogSyslogServer
channel := make(syslog.LogPartsChannel)
handler := syslog.NewChannelHandler(channel)

server := syslog.NewServer()
server.SetFormat(syslog.RFC5424)
server.SetHandler(handler)
err := server.ListenTCP(logSyslogServer)
if err != nil {
return err
// Peer name verification is already handled by crypto/tls and is not required at the go-syslog level
// The defaultTlsPeerName in go-syslog can lead to false verification failures, so set nil to bypass
server.SetTlsPeerNameFunc(nil)

if config.LogSyslogServerCertFile != "" {
serverCaPool := x509.NewCertPool()
clientCaPool := x509.NewCertPool()
if config.LogSyslogServerCAFile != "" {
ca, err := os.ReadFile(config.LogSyslogServerCAFile)
if err != nil {
return fmt.Errorf("failed to read a Certificate Authority: %s", err)
}
if ok := serverCaPool.AppendCertsFromPEM(ca); !ok {
return fmt.Errorf("failed to append a Certificate Authority")
}
}
if config.LogSyslogServerClientCAFile != "" {
ca, err := os.ReadFile(config.LogSyslogServerClientCAFile)
if err != nil {
return fmt.Errorf("failed to read a client Certificate Authority: %s", err)
}
if ok := clientCaPool.AppendCertsFromPEM(ca); !ok {
return fmt.Errorf("failed to append a client Certificate Authority")
}
}

cert, err := os.ReadFile(config.LogSyslogServerCertFile)
if err != nil {
return fmt.Errorf("failed to read a certificate: %s", err)
}
key, err := os.ReadFile(config.LogSyslogServerKeyFile)
if err != nil {
return fmt.Errorf("failed to read a key: %s", err)
}
tlsCert, err := tls.X509KeyPair(cert, key)
if err != nil {
return err
}

tlsConfig := tls.Config{
ClientAuth: tls.VerifyClientCertIfGiven,
Certificates: []tls.Certificate{tlsCert},
RootCAs: serverCaPool,
ClientCAs: clientCaPool,
}
err = server.ListenTCPTLS(logSyslogServer, &tlsConfig)
if err != nil {
return err
}
} else {
err := server.ListenTCP(logSyslogServer)
if err != nil {
return err
}
}

server.Boot()

go func(ctx context.Context, server *syslog.Server, channel syslog.LogPartsChannel) {
Expand Down

0 comments on commit 036baf8

Please sign in to comment.