Skip to content

Commit

Permalink
Move audit logger config (smartcontractkit#9468)
Browse files Browse the repository at this point in the history
  • Loading branch information
george-dorin authored Jun 6, 2023
1 parent f567d60 commit aee738f
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 207 deletions.
13 changes: 6 additions & 7 deletions core/chains/evm/config/mocks/chain_scoped_config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions core/config/app_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
"github.com/google/uuid"
"github.com/pkg/errors"
"go.uber.org/zap/zapcore"

"github.com/smartcontractkit/chainlink/v2/core/logger/audit"
)

// nolint
Expand Down Expand Up @@ -49,7 +47,7 @@ type AppConfig interface {
Web

Database() Database
AuditLogger() audit.Config
AuditLogger() AuditLogger
Keeper() Keeper
TelemetryIngress() TelemetryIngress
Sentry() Sentry
Expand Down
13 changes: 13 additions & 0 deletions core/config/audit_logger_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package config

import (
"github.com/smartcontractkit/chainlink/v2/core/store/models"
)

type AuditLogger interface {
Enabled() bool
ForwardToUrl() (models.URL, error)
Environment() string
JsonWrapperKey() string
Headers() (models.ServiceHeaders, error)
}
56 changes: 39 additions & 17 deletions core/config/v2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/build"
"github.com/smartcontractkit/chainlink/v2/core/config"
"github.com/smartcontractkit/chainlink/v2/core/config/parse"
"github.com/smartcontractkit/chainlink/v2/core/logger/audit"
"github.com/smartcontractkit/chainlink/v2/core/services/chainlink/cfgtest"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
Expand All @@ -39,22 +38,22 @@ type Core struct {
RootDir *string
ShutdownGracePeriod *models.Duration

Feature Feature `toml:",omitempty"`
Database Database `toml:",omitempty"`
TelemetryIngress TelemetryIngress `toml:",omitempty"`
AuditLogger audit.AuditLoggerConfig `toml:",omitempty"`
Log Log `toml:",omitempty"`
WebServer WebServer `toml:",omitempty"`
JobPipeline JobPipeline `toml:",omitempty"`
FluxMonitor FluxMonitor `toml:",omitempty"`
OCR2 OCR2 `toml:",omitempty"`
OCR OCR `toml:",omitempty"`
P2P P2P `toml:",omitempty"`
Keeper Keeper `toml:",omitempty"`
AutoPprof AutoPprof `toml:",omitempty"`
Pyroscope Pyroscope `toml:",omitempty"`
Sentry Sentry `toml:",omitempty"`
Insecure Insecure `toml:",omitempty"`
Feature Feature `toml:",omitempty"`
Database Database `toml:",omitempty"`
TelemetryIngress TelemetryIngress `toml:",omitempty"`
AuditLogger AuditLogger `toml:",omitempty"`
Log Log `toml:",omitempty"`
WebServer WebServer `toml:",omitempty"`
JobPipeline JobPipeline `toml:",omitempty"`
FluxMonitor FluxMonitor `toml:",omitempty"`
OCR2 OCR2 `toml:",omitempty"`
OCR OCR `toml:",omitempty"`
P2P P2P `toml:",omitempty"`
Keeper Keeper `toml:",omitempty"`
AutoPprof AutoPprof `toml:",omitempty"`
Pyroscope Pyroscope `toml:",omitempty"`
Sentry Sentry `toml:",omitempty"`
Insecure Insecure `toml:",omitempty"`
}

var (
Expand Down Expand Up @@ -387,6 +386,29 @@ func (t *TelemetryIngress) setFrom(f *TelemetryIngress) {
}
}

type AuditLogger struct {
Enabled *bool
ForwardToUrl *models.URL
JsonWrapperKey *string
Headers *[]models.ServiceHeader
}

func (p *AuditLogger) SetFrom(f *AuditLogger) {
if v := f.Enabled; v != nil {
p.Enabled = v
}
if v := f.ForwardToUrl; v != nil {
p.ForwardToUrl = v
}
if v := f.JsonWrapperKey; v != nil {
p.JsonWrapperKey = v
}
if v := f.Headers; v != nil {
p.Headers = v
}

}

// LogLevel replaces dpanic with crit/CRIT
type LogLevel zapcore.Level

Expand Down
137 changes: 3 additions & 134 deletions core/logger/audit/audit_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,14 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"net/url"
"os"
"regexp"
"strings"
"time"

"go.uber.org/multierr"

"github.com/smartcontractkit/chainlink/v2/core/config"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services"
"github.com/smartcontractkit/chainlink/v2/core/store/models"
Expand All @@ -35,133 +31,6 @@ type AuditLogger interface {
Audit(eventID EventID, data Data)
}

type AuditLoggerConfig struct {
Enabled *bool
ForwardToUrl *models.URL
JsonWrapperKey *string
Headers *[]ServiceHeader
}

func (p *AuditLoggerConfig) SetFrom(f *AuditLoggerConfig) {
if v := f.Enabled; v != nil {
p.Enabled = v
}
if v := f.ForwardToUrl; v != nil {
p.ForwardToUrl = v
}
if v := f.JsonWrapperKey; v != nil {
p.JsonWrapperKey = v
}
if v := f.Headers; v != nil {
p.Headers = v
}

}

// ServiceHeader is an HTTP header to include in POST to log service.
type ServiceHeader struct {
Header string
Value string
}

func (h *ServiceHeader) UnmarshalText(input []byte) error {
parts := strings.SplitN(string(input), ":", 2)
h.Header = parts[0]
if len(parts) > 1 {
h.Value = strings.TrimSpace(parts[1])
}
return h.validate()
}

func (h *ServiceHeader) MarshalText() ([]byte, error) {
var b bytes.Buffer
fmt.Fprintf(&b, "%s: %s", h.Header, h.Value)
return b.Bytes(), nil
}

// We act slightly more strictly than the HTTP specifications
// technically allow instead following the guidelines of
// cloudflare transforms.
// https://developers.cloudflare.com/rules/transform/request-header-modification/reference/header-format
var (
headerNameRegex = regexp.MustCompile(`^[A-Za-z\-]+$`)
headerValueRegex = regexp.MustCompile("^[A-Za-z_ :;.,\\/\"'?!(){}[\\]@<>=\\-+*#$&`|~^%]+$")
)

func (h ServiceHeader) validate() (err error) {
if !headerNameRegex.MatchString(h.Header) {
err = multierr.Append(err, errors.Errorf("invalid header name: %s", h.Header))
}

if !headerValueRegex.MatchString(h.Value) {
err = multierr.Append(err, errors.Errorf("invalid header value: %s", h.Value))
}
return
}

type ServiceHeaders []ServiceHeader

func (sh *ServiceHeaders) UnmarshalText(input []byte) error {
if sh == nil {
return errors.New("Cannot unmarshal to a nil receiver")
}

headers := string(input)

var parsedHeaders []ServiceHeader
if headers != "" {
headerLines := strings.Split(headers, "\\")
for _, header := range headerLines {
keyValue := strings.Split(header, "||")
if len(keyValue) != 2 {
return errors.Errorf("invalid headers provided for the audit logger. Value, single pair split on || required, got: %s", keyValue)
}
h := ServiceHeader{
Header: keyValue[0],
Value: keyValue[1],
}

if err := h.validate(); err != nil {
return err
}
parsedHeaders = append(parsedHeaders, h)
}
}

*sh = parsedHeaders
return nil
}

func (sh *ServiceHeaders) MarshalText() ([]byte, error) {
if sh == nil {
return nil, errors.New("Cannot marshal to a nil receiver")
}

sb := strings.Builder{}
for _, header := range *sh {
sb.WriteString(header.Header)
sb.WriteString("||")
sb.WriteString(header.Value)
sb.WriteString("\\")
}

serialized := sb.String()

if len(serialized) > 0 {
serialized = serialized[:len(serialized)-1]
}

return []byte(serialized), nil
}

type Config interface {
Enabled() bool
ForwardToUrl() (models.URL, error)
Environment() string
JsonWrapperKey() string
Headers() (ServiceHeaders, error)
}

type HTTPAuditLoggerInterface interface {
Do(req *http.Request) (*http.Response, error)
}
Expand All @@ -170,7 +39,7 @@ type AuditLoggerService struct {
logger logger.Logger // The standard logger configured in the node
enabled bool // Whether the audit logger is enabled or not
forwardToUrl models.URL // Location we are going to send logs to
headers []ServiceHeader // Headers to be sent along with logs for identification/authentication
headers []models.ServiceHeader // Headers to be sent along with logs for identification/authentication
jsonWrapperKey string // Wrap audit data as a map under this key if present
environmentName string // Decorate the environment this is coming from
hostname string // The self-reported hostname of the machine
Expand All @@ -194,7 +63,7 @@ var NoopLogger AuditLogger = &AuditLoggerService{}
// Parses and validates the AUDIT_LOGS_* environment values and returns an enabled
// AuditLogger instance. If the environment variables are not set, the logger
// is disabled and short circuits execution via enabled flag.
func NewAuditLogger(logger logger.Logger, config Config) (AuditLogger, error) {
func NewAuditLogger(logger logger.Logger, config config.AuditLogger) (AuditLogger, error) {
// If the unverified config is nil, then we assume this came from the
// configuration system and return a nil logger.
if config == nil || !config.Enabled() {
Expand Down
4 changes: 2 additions & 2 deletions core/logger/audit/audit_logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ func (c Config) ForwardToUrl() (models.URL, error) {
return *url, nil
}

func (c Config) Headers() (audit.ServiceHeaders, error) {
return make(audit.ServiceHeaders, 0), nil
func (c Config) Headers() (models.ServiceHeaders, error) {
return make(models.ServiceHeaders, 0), nil
}

func (c Config) JsonWrapperKey() string {
Expand Down
14 changes: 7 additions & 7 deletions core/services/chainlink/config_audit_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ package chainlink

import (
"github.com/smartcontractkit/chainlink/v2/core/build"
"github.com/smartcontractkit/chainlink/v2/core/logger/audit"
v2 "github.com/smartcontractkit/chainlink/v2/core/config/v2"
"github.com/smartcontractkit/chainlink/v2/core/store/models"
)

type auditLoggerConfig struct {
C audit.AuditLoggerConfig
c v2.AuditLogger
}

func (a auditLoggerConfig) Enabled() bool {
return *a.C.Enabled
return *a.c.Enabled
}

func (a auditLoggerConfig) ForwardToUrl() (models.URL, error) {
return *a.C.ForwardToUrl, nil
return *a.c.ForwardToUrl, nil
}

func (a auditLoggerConfig) Environment() string {
Expand All @@ -26,9 +26,9 @@ func (a auditLoggerConfig) Environment() string {
}

func (a auditLoggerConfig) JsonWrapperKey() string {
return *a.C.JsonWrapperKey
return *a.c.JsonWrapperKey
}

func (a auditLoggerConfig) Headers() (audit.ServiceHeaders, error) {
return *a.C.Headers, nil
func (a auditLoggerConfig) Headers() (models.ServiceHeaders, error) {
return *a.c.Headers, nil
}
33 changes: 33 additions & 0 deletions core/services/chainlink/config_audit_logger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package chainlink

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestAuditLoggerConfig(t *testing.T) {
opts := GeneralConfigOpts{
ConfigStrings: []string{fullTOML},
}
cfg, err := opts.New()
require.NoError(t, err)

auditConfig := cfg.AuditLogger()

require.Equal(t, true, auditConfig.Enabled())
require.Equal(t, "event", auditConfig.JsonWrapperKey())

fUrl, err := auditConfig.ForwardToUrl()
require.NoError(t, err)
require.Equal(t, "http", fUrl.Scheme)
require.Equal(t, "localhost:9898", fUrl.Host)

headers, err := auditConfig.Headers()
require.NoError(t, err)
require.Len(t, headers, 2)
require.Equal(t, "Authorization", headers[0].Header)
require.Equal(t, "token", headers[0].Value)
require.Equal(t, "X-SomeOther-Header", headers[1].Header)
require.Equal(t, "value with spaces | and a bar+*", headers[1].Value)
}
Loading

0 comments on commit aee738f

Please sign in to comment.