From 6ff5012c4232e14d4faf1e6fc3d20b1ea7e46948 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Thu, 9 Jun 2022 23:59:24 -0700 Subject: [PATCH 01/92] Create new Splunk Logger (forward event data with HEC in goroutine) Add calls to emit log event for each event/action performable through the UI or CLI, in addition to calls only accessible through GraphQL. Initialize fields needed for the Splunk logger implementation in NewLogger(), adding fields to the config directly from os.Getenv - where the Sentry logger implementation pulls these environment variables directly in it's own module Update some new_*_controller functions to pass logger into struct Audit log calls are implemented in either 'mutations.go' or '*_controller.go', and sometimes both. This is because there are multiple potential ways to call a given functionality. CLI and JSON HTTP go through the standard router, hitting the '*_controller.go' handlers, but the GraphQL API allows mutation, which directly interfaces to the Provider struct bound function calls through reflection. These Provider functions are all in 'mutation.go', so audit log calls were added to each. The controller functions sometimes delegate that calls to the Provider, but not always. In these cases, the audit log emit events are at the controller level. --- core/logger/logger.go | 56 +++- core/logger/null_logger.go | 17 +- core/logger/prometheus.go | 4 + core/logger/sentry.go | 4 + core/logger/splunk.go | 241 ++++++++++++++++++ core/logger/zap.go | 2 + core/sessions/orm.go | 18 +- core/web/bridge_types_controller.go | 17 ++ core/web/chains_controller.go | 14 +- core/web/config_controller.go | 4 + core/web/csa_keys_controller.go | 14 + core/web/eth_keys_controller.go | 19 ++ core/web/evm_chains_controller.go | 2 +- core/web/evm_forwarders_controller.go | 7 + core/web/evm_nodes_controller.go | 4 +- core/web/evm_transfer_controller.go | 5 + core/web/external_initiators_controller.go | 8 + core/web/jobs_controller.go | 8 +- core/web/keys_controller.go | 47 ++++ core/web/log_controller.go | 11 + core/web/nodes_controller.go | 8 + core/web/ocr2_keys_controller.go | 22 ++ core/web/ocr_keys_controller.go | 15 ++ core/web/p2p_keys_controller.go | 17 ++ .../pipeline_job_spec_errors_controller.go | 2 + core/web/pipeline_runs_controller.go | 2 + core/web/resolver/mutation.go | 124 +++++++++ core/web/sessions_controller.go | 2 + core/web/solana_chains_controller.go | 2 +- core/web/solana_nodes_controller.go | 4 +- core/web/solana_transfer_controller.go | 4 + core/web/terra_chains_controller.go | 2 +- core/web/terra_nodes_controller.go | 4 +- core/web/terra_transfer_controller.go | 5 + core/web/user_controller.go | 7 + core/web/vrf_keys_controller.go | 14 + core/web/webauthn_controller.go | 11 + 37 files changed, 718 insertions(+), 29 deletions(-) create mode 100644 core/logger/splunk.go diff --git a/core/logger/logger.go b/core/logger/logger.go index ee3a093b3b4..91e958397b4 100644 --- a/core/logger/logger.go +++ b/core/logger/logger.go @@ -78,6 +78,11 @@ type Logger interface { // exit uncleanly Fatal(args ...interface{}) + // The .Audit function here is specific to the SplunkLogger implementation + // It is added to the interface to ensure that audit logs are sent regardless of log level. + // All other Logger implementations should continue the pattern of propogating wrapped logger calls + Auditf(eventID string, data map[string]interface{}) + Tracef(format string, values ...interface{}) Debugf(format string, values ...interface{}) Infof(format string, values ...interface{}) @@ -115,6 +120,20 @@ type Logger interface { Recover(panicErr interface{}) } +type Config struct { + LogLevel zapcore.Level + Dir string + JsonConsole bool + UnixTS bool + FileMaxSizeMB int + FileMaxAgeDays int + FileMaxBackups int // files + Hostname string + ChainlinkDev bool + SplunkToken string // enables splunk logging if not empty + SplunkURL string +} + // Constants for service names for package specific logging configuration const ( HeadTracker = "HeadTracker" @@ -213,6 +232,25 @@ func NewLogger() (Logger, func() error) { } } + // Set Splunk values in Config if any defined in env + c.SplunkToken = os.Getenv("SPLUNK_TOKEN") + if c.SplunkToken != "" { + c.SplunkURL = os.Getenv("SPLUNK_URL") + if c.SplunkURL == "" { + errString := "Misconfigured Splunk environment variables set. SPLUNK_URL is required if SPLUNK_TOKEN is not empty, unset the SPLUNK_URL environment variable or set SPLUNK_TOKEN to enable Splunk audit logging." + parseErrs = append(parseErrs, errString) + c.SplunkToken = "" + } + } + + c.ChainlinkDev = os.Getenv("CHAINLINK_DEV") == "true" + hostname, err := os.Hostname() + if err != nil { + errString := fmt.Sprintf("Error get hostname for Logger config: %s", err) + parseErrs = append(parseErrs, errString) + } + c.Hostname = hostname + c.UnixTS, invalid = envvar.LogUnixTS.Parse() if invalid != "" { parseErrs = append(parseErrs, invalid) @@ -228,16 +266,6 @@ func NewLogger() (Logger, func() error) { return l.Named(verShaNameStatic()), close } -type Config struct { - LogLevel zapcore.Level - Dir string - JsonConsole bool - UnixTS bool - FileMaxSizeMB int - FileMaxAgeDays int - FileMaxBackups int // files -} - // New returns a new Logger with pretty printing to stdout, prometheus counters, and sentry forwarding. // Tests should use TestLogger. func (c *Config) New() (Logger, func() error) { @@ -252,8 +280,14 @@ func (c *Config) New() (Logger, func() error) { if err != nil { log.Fatal(err) } + + // If Splunk logging is enabled (token set), extend/wrap the logger with a splunkLogger instance + if c.SplunkToken != "" { + l = newSplunkLogger(l, c.SplunkToken, c.SplunkURL, c.Hostname, c.ChainlinkDev) + } l = newSentryLogger(l) - return newPrometheusLogger(l), close + l = newPrometheusLogger(l) + return l, close } // DebugLogsToDisk returns whether debug logs should be stored in disk diff --git a/core/logger/null_logger.go b/core/logger/null_logger.go index 54ce0df54fd..81b32211248 100644 --- a/core/logger/null_logger.go +++ b/core/logger/null_logger.go @@ -15,14 +15,15 @@ func (l *nullLogger) With(args ...interface{}) Logger { return l } func (l *nullLogger) Named(name string) Logger { return l } func (l *nullLogger) SetLogLevel(_ zapcore.Level) {} -func (l *nullLogger) Trace(args ...interface{}) {} -func (l *nullLogger) Debug(args ...interface{}) {} -func (l *nullLogger) Info(args ...interface{}) {} -func (l *nullLogger) Warn(args ...interface{}) {} -func (l *nullLogger) Error(args ...interface{}) {} -func (l *nullLogger) Critical(args ...interface{}) {} -func (l *nullLogger) Panic(args ...interface{}) {} -func (l *nullLogger) Fatal(args ...interface{}) {} +func (l *nullLogger) Auditf(eventID string, data map[string]interface{}) {} +func (l *nullLogger) Trace(args ...interface{}) {} +func (l *nullLogger) Debug(args ...interface{}) {} +func (l *nullLogger) Info(args ...interface{}) {} +func (l *nullLogger) Warn(args ...interface{}) {} +func (l *nullLogger) Error(args ...interface{}) {} +func (l *nullLogger) Critical(args ...interface{}) {} +func (l *nullLogger) Panic(args ...interface{}) {} +func (l *nullLogger) Fatal(args ...interface{}) {} func (l *nullLogger) Tracef(format string, values ...interface{}) {} func (l *nullLogger) Debugf(format string, values ...interface{}) {} diff --git a/core/logger/prometheus.go b/core/logger/prometheus.go index 8ddf6776c6a..9cefffa6fef 100644 --- a/core/logger/prometheus.go +++ b/core/logger/prometheus.go @@ -86,6 +86,10 @@ func (s *prometheusLogger) SetLogLevel(level zapcore.Level) { s.h.SetLogLevel(level) } +func (s *prometheusLogger) Auditf(eventID string, data map[string]interface{}) { + s.h.Auditf(eventID, data) +} + func (s *prometheusLogger) Trace(args ...interface{}) { s.h.Trace(args...) } diff --git a/core/logger/sentry.go b/core/logger/sentry.go index d968bd88764..1d7f1ab6f90 100644 --- a/core/logger/sentry.go +++ b/core/logger/sentry.go @@ -87,6 +87,10 @@ func (s *sentryLogger) SetLogLevel(level zapcore.Level) { s.h.SetLogLevel(level) } +func (s *sentryLogger) Auditf(eventID string, data map[string]interface{}) { + s.h.Auditf(eventID, data) +} + func (s *sentryLogger) Trace(args ...interface{}) { s.h.Trace(args...) } diff --git a/core/logger/splunk.go b/core/logger/splunk.go new file mode 100644 index 00000000000..576999daf14 --- /dev/null +++ b/core/logger/splunk.go @@ -0,0 +1,241 @@ +package logger + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "time" + + "go.uber.org/zap/zapcore" +) + +// Static audit log event type constants +const ( + AUTH_LOGIN_FAILED_EMAIL = "AUTH_LOGIN_FAILED_EMAIL" + AUTH_LOGIN_FAILED_PASSWORD = "AUTH_LOGIN_FAILED_PASSWORD" + AUTH_LOGIN_FAILED_2FA = "AUTH_LOGIN_FAILED_2FA" + AUTH_LOGIN_SUCCESS_WITH_2FA = "AUTH_LOGIN_SUCCESS_WITH_2FA" + AUTH_LOGIN_SUCCESS_NO_2FA = "AUTH_LOGIN_SUCCESS_NO_2FA" + AUTH_2FA_ENROLLED = "AUTH_2FA_ENROLLED" + AUTH_SESSION_DELETED = "SESSION_DELETED" + + PASSWORD_RESET_ATTEMPT_FAILED_MISMATCH = "PASSWORD_RESET_ATTEMPT_FAILED_MISMATCH" + PASSWORD_RESET_SUCCESS = "PASSWORD_RESET_SUCCESS" + + API_TOKEN_CREATE_ATTEMPT_PASSWORD_MISMATCH = "API_TOKEN_CREATE_ATTEMPT_PASSWORD_MISMATCH" + API_TOKEN_CREATED = "API_TOKEN_CREATED" + API_TOKEN_DELETE_ATTEMPT_PASSWORD_MISMATCH = "API_TOKEN_DELETE_ATTEMPT_PASSWORD_MISMATCH" + API_TOKEN_DELETED = "API_TOKEN_DELETED" + + CSA_KEY_CREATED = "CSA_KEY_CREATED" + CSA_KEY_IMPORTED = "CSA_KEY_IMPORTED" + CSA_KEY_EXPORTED = "CSA_KEY_EXPORTED" + CSA_KEY_DELETED = "CSA_KEY_DELETED" + + FEEDS_MAN_CREATED = "FEEDS_MAN_CREATED" + FEEDS_MAN_UPDATED = "FEEDS_MAN_UPDATED" + + FEEDS_MAN_CHAIN_CONFIG_CREATED = "FEEDS_MAN_CHAIN_CONFIG_CREATED" + FEEDS_MAN_CHAIN_CONFIG_UPDATED = "FEEDS_MAN_CHAIN_CONFIG_UPDATED" + FEEDS_MAN_CHAIN_CONFIG_DELETED = "FEEDS_MAN_CHAIN_CONFIG_DELETED" + + OCR_KEY_BUNDLE_CREATED = "OCR_KEY_BUNDLE_CREATED" + OCR_KEY_BUNDLE_IMPORTED = "OCR_KEY_BUNDLE_IMPORTED" + OCR_KEY_BUNDLE_EXPORTED = "OCR_KEY_BUNDLE_EXPORTED" + OCR_KEY_BUNDLE_DELETED = "OCR_KEY_BUNDLE_DELETED" + + OCR2_KEY_BUNDLE_CREATED = "OCR2_KEY_BUNDLE_CREATED" + OCR2_KEY_BUNDLE_IMPORTED = "OCR2_KEY_BUNDLE_IMPORTED" + OCR2_KEY_BUNDLE_EXPORTED = "OCR2_KEY_BUNDLE_EXPORTED" + OCR2_KEY_BUNDLE_DELETED = "OCR2_KEY_BUNDLE_DELETED" + + ETH_KEY_CREATED = "ETH_KEY_CREATED" + ETH_KEY_UPDATED = "ETH_KEY_UPDATED" + ETH_KEY_IMPORTED = "ETH_KEY_IMPORTED" + ETH_KEY_EXPORTED = "ETH_KEY_EXPORTED" + ETH_KEY_DELETED = "ETH_KEY_DELETED" + + P2P_KEY_CREATED = "P2P_KEY_CREATED" + P2P_KEY_IMPORTED = "P2P_KEY_IMPORTED" + P2P_KEY_EXPORTED = "P2P_KEY_EXPORTED" + P2P_KEY_DELETED = "P2P_KEY_DELETED" + + VRF_KEY_CREATED = "VRF_KEY_CREATED" + VRF_KEY_IMPORTED = "VRF_KEY_IMPORTED" + VRF_KEY_EXPORTED = "VRF_KEY_EXPORTED" + VRF_KEY_DELETED = "VRF_KEY_DELETED" + + TERRA_KEY_CREATED = "TERRA_KEY_CREATED" + TERRA_KEY_IMPORTED = "TERRA_KEY_IMPORTED" + TERRA_KEY_EXPORTED = "TERRA_KEY_EXPORTED" + TERRA_KEY_DELETED = "TERRA_KEY_DELETED" + + SOLANA_KEY_CREATED = "SOLANA_KEY_CREATED" + SOLANA_KEY_IMPORTED = "SOLANA_KEY_IMPORTED" + SOLANA_KEY_EXPORTED = "SOLANA_KEY_EXPORTED" + SOLANA_KEY_DELETED = "SOLANA_KEY_DELETED" + + ETH_TRANSACTION_CREATED = "ETH_TRANSACTION_CREATED" + TERRA_TRANSACTION_CREATED = "TERRA_TRANSACTION_CREATED" + SOLANA_TRANSACTION_CREATED = "SOLANA_TRANSACTION_CREATED" + + JOB_CREATED = "JOB_CREATED" + JOB_DELETED = "JOB_DELETED" + + CHAIN_ADDED = "CHAIN_ADDED" + CHAIN_SPEC_UPDATED = "CHAIN_SPEC_UPDATED" + CHAIN_DELETED = "CHAIN_DELETED" + + CHAIN_RPC_NODE_ADDED = "CHAIN_RPC_NODE_ADDED" + CHAIN_RPC_NODE_DELETED = "CHAIN_RPC_NODE_DELETED" + + BRIDGE_CREATED = "BRIDGE_CREATED" + BRIDGE_UPDATED = "BRIDGE_UPDATED" + BRIDGE_DELETED = "BRIDGE_DELETED" + + FORWARDER_CREATED = "FORWARDER_CREATED" + FORWARDER_DELETED = "FORWARDER_DELETED" + + EXTERNAL_INITIATOR_CREATED = "EXTERNAL_INITIATOR_CREATED" + EXTERNAL_INITIATOR_DELETED = "EXTERNAL_INITIATOR_DELETED" + + JOB_PROPOSAL_SPEC_APPROVED = "JOB_PROPOSAL_SPEC_APPROVED" + JOB_PROPOSAL_SPEC_UPDATED = "JOB_PROPOSAL_SPEC_UPDATED" + JOB_PROPOSAL_SPEC_CANCELED = "JOB_PROPOSAL_SPEC_CANCELED" + JOB_PROPOSAL_SPEC_REJECTED = "JOB_PROPOSAL_SPEC_REJECTED" + + CONFIG_UPDATED = "CONFIG_UPDATED" + CONFIG_SQL_LOGGING_ENABLED = "CONFIG_SQL_LOGGING_ENABLED" + CONFIG_SQL_LOGGING_DISABLED = "CONFIG_SQL_LOGGING_DISABLED" + GLOBAL_LOG_LEVEL_SET = "GLOBAL_LOG_LEVEL_SET" + + JOB_ERROR_DISMISSED = "JOB_ERROR_DISMISSED" + JOB_RUN_SET = "JOB_RUN_SET" + + ENV_NONCRITICAL_ENV_DUMPED = "ENV_NONCRITICAL_ENV_DUMPED" + + UNAUTHED_RUN_RESUMED = "UNAUTHED_RUN_RESUMED" +) + +type splunkLogger struct { + logger Logger + splunkToken string + splunkURL string + developFlag bool + hostname string + localIP string +} + +func newSplunkLogger(logger Logger, splunkToken string, splunkURL string, hostname string, chainlinkDev bool) Logger { + // Initialize and return Splunk logger struct with required state for HEC calls + return &splunkLogger{ + logger: logger.Named("Splunk"), + splunkToken: splunkToken, + splunkURL: splunkURL, + developFlag: chainlinkDev, + hostname: hostname, + localIP: getLocalIP(), + } +} + +func (l *splunkLogger) Auditf(eventID string, data map[string]interface{}) { + // goroutine to async POST to splunk HTTP Event Collector (HEC) + go l.postLogToSplunk(eventID, data) +} + +// getLocalIP returns the first non- loopback local IP of the host +func getLocalIP() string { + addrs, err := net.InterfaceAddrs() + if err != nil { + return "" + } + for _, address := range addrs { + // check the address type and if it is not a loopback the display it + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + return ipnet.IP.String() + } + } + } + return "" +} + +func (l *splunkLogger) postLogToSplunk(eventID string, data map[string]interface{}) { + env := "production" + if l.developFlag { + env = "develop" + } + + // Splunk JSON data + splunkLog := map[string]interface{}{ + "eventID": eventID, + "hostname": l.hostname, + "localIP": l.localIP, + "env": env, + } + if len(data) != 0 { + splunkLog["data"] = data + } + + // Wrap serialized audit log map into JSON object `event` for API call + serializedArgs, _ := json.Marshal(splunkLog) + splunkLog = map[string]interface{}{"event": string(serializedArgs)} + serializedSplunkLog, _ := json.Marshal(splunkLog) + + // Send up to HEC log collector + httpClient := &http.Client{Timeout: time.Second * 60} + req, _ := http.NewRequest("POST", l.splunkURL, bytes.NewReader(serializedSplunkLog)) + req.Header.Add("Authorization", "Splunk "+l.splunkToken) + resp, err := httpClient.Do(req) + if err != nil { + fmt.Printf("Error sending log to Splunk: %v\n", err) + } + if resp.StatusCode != 200 { + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Printf("Error reading errored Splunk webhook response body: %v\n", err) + } + fmt.Printf("Error sending log to Splunk\nstatus code: %d\nbody: %s", resp.StatusCode, string(bodyBytes)) + } +} + +// The Splunk logger should be the bottom of the nested logs, stub all other calls +func (l *splunkLogger) With(args ...interface{}) Logger { return l } +func (l *splunkLogger) Named(name string) Logger { return l } +func (l *splunkLogger) NewRootLogger(lvl zapcore.Level) (Logger, error) { return l, nil } +func (l *splunkLogger) SetLogLevel(_ zapcore.Level) {} +func (l *splunkLogger) Trace(args ...interface{}) {} +func (l *splunkLogger) Info(args ...interface{}) {} +func (l *splunkLogger) Debug(args ...interface{}) {} +func (l *splunkLogger) Warn(args ...interface{}) {} +func (l *splunkLogger) Error(args ...interface{}) {} +func (l *splunkLogger) Critical(args ...interface{}) {} +func (l *splunkLogger) Panic(args ...interface{}) {} +func (l *splunkLogger) Fatal(args ...interface{}) {} +func (l *splunkLogger) Tracef(format string, values ...interface{}) {} +func (l *splunkLogger) Debugf(format string, values ...interface{}) {} +func (l *splunkLogger) Infof(format string, values ...interface{}) {} +func (l *splunkLogger) Warnf(format string, values ...interface{}) {} +func (l *splunkLogger) Errorf(format string, values ...interface{}) {} +func (l *splunkLogger) Criticalf(format string, values ...interface{}) {} +func (l *splunkLogger) Panicf(format string, values ...interface{}) {} +func (l *splunkLogger) Fatalf(format string, values ...interface{}) {} +func (l *splunkLogger) Tracew(msg string, keysAndValues ...interface{}) {} +func (l *splunkLogger) Debugw(msg string, keysAndValues ...interface{}) {} +func (l *splunkLogger) Infow(msg string, keysAndValues ...interface{}) {} +func (l *splunkLogger) Warnw(msg string, keysAndValues ...interface{}) {} +func (l *splunkLogger) Errorw(msg string, keysAndValues ...interface{}) {} +func (l *splunkLogger) Criticalw(msg string, keysAndValues ...interface{}) {} +func (l *splunkLogger) Panicw(msg string, keysAndValues ...interface{}) {} +func (l *splunkLogger) Fatalw(msg string, keysAndValues ...interface{}) {} +func (l *splunkLogger) WarnIf(err error, msg string) {} +func (l *splunkLogger) ErrorIf(err error, msg string) {} +func (l *splunkLogger) PanicIf(err error, msg string) {} +func (l *splunkLogger) ErrorIfClosing(io.Closer, string) {} +func (l *splunkLogger) Sync() error { return nil } +func (l *splunkLogger) Helper(skip int) Logger { return l } +func (l *splunkLogger) Recover(panicErr interface{}) {} diff --git a/core/logger/zap.go b/core/logger/zap.go index f26e8674d8d..f445c468fda 100644 --- a/core/logger/zap.go +++ b/core/logger/zap.go @@ -126,6 +126,8 @@ func (l *zapLogger) With(args ...interface{}) Logger { return &newLogger } +func (l *zapLogger) Auditf(eventID string, data map[string]interface{}) { /* STUB */ } + // copyFields returns a copy of fields with add appended. func copyFields(fields []interface{}, add ...interface{}) []interface{} { f := make([]interface{}, 0, len(fields)+len(add)) diff --git a/core/sessions/orm.go b/core/sessions/orm.go index e66d23c3852..f4a9de97187 100644 --- a/core/sessions/orm.go +++ b/core/sessions/orm.go @@ -132,10 +132,12 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { // Do email and password check first to prevent extra database look up // for MFA tokens leaking if an account has MFA tokens or not. if !constantTimeEmailCompare(sr.Email, user.Email) { + o.lggr.Auditf(logger.AUTH_LOGIN_FAILED_EMAIL, map[string]interface{}{"email": sr.Email}) return "", errors.New("Invalid email") } if !utils.CheckPasswordHash(sr.Password, user.HashedPassword) { + o.lggr.Auditf(logger.AUTH_LOGIN_FAILED_PASSWORD, map[string]interface{}{"email": sr.Email}) return "", errors.New("Invalid password") } @@ -152,6 +154,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { lggr.Infof("No MFA for user. Creating Session") session := NewSession() _, err = o.db.Exec("INSERT INTO sessions (id, last_used, created_at) VALUES ($1, now(), now())", session.ID) + o.lggr.Auditf(logger.AUTH_LOGIN_SUCCESS_NO_2FA, map[string]interface{}{"email": sr.Email}) return session.ID, err } @@ -182,6 +185,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { if err != nil { // The user does have WebAuthn enabled but failed the check + o.lggr.Auditf(logger.AUTH_LOGIN_FAILED_2FA, map[string]interface{}{"email": sr.Email, "error": err}) lggr.Errorf("User sent an invalid attestation: %v", err) return "", errors.New("MFA Error") } @@ -190,7 +194,19 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { // This is a success so we can create the sessions session := NewSession() _, err = o.db.Exec("INSERT INTO sessions (id, last_used, created_at) VALUES ($1, now(), now())", session.ID) - return session.ID, err + if err != nil { + return "", err + } + + // Forward registered credentials for audit logs + uwasj, err := json.Marshal(uwas) + if err != nil { + lggr.Errorf("error in Marshal credentials: %s", err) + return "", err + } + o.lggr.Auditf(logger.AUTH_LOGIN_SUCCESS_WITH_2FA, map[string]interface{}{"email": sr.Email, "credential": string(uwasj)}) + + return session.ID, nil } const constantTimeEmailLength = 256 diff --git a/core/web/bridge_types_controller.go b/core/web/bridge_types_controller.go index af41963844a..401f8a8b150 100644 --- a/core/web/bridge_types_controller.go +++ b/core/web/bridge_types_controller.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/core/assets" "github.com/smartcontractkit/chainlink/core/bridges" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/store/models" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -96,6 +97,13 @@ func (btc *BridgeTypesController) Create(c *gin.Context) { resource := presenters.NewBridgeResource(*bt) resource.IncomingToken = bta.IncomingToken + btc.App.GetLogger().Auditf(logger.BRIDGE_CREATED, map[string]interface{}{ + "bridgeName": bta.Name, + "bridgeConfirmations": bta.Confirmations, + "bridgeMinimumContractPayment": bta.MinimumContractPayment, + "bridgeURL": bta.URL, + }) + jsonAPIResponse(c, resource, "bridge") } } @@ -170,6 +178,13 @@ func (btc *BridgeTypesController) Update(c *gin.Context) { return } + btc.App.GetLogger().Auditf(logger.BRIDGE_UPDATED, map[string]interface{}{ + "bridgeName": bt.Name, + "bridgeConfirmations": bt.Confirmations, + "bridgeMinimumContractPayment": bt.MinimumContractPayment, + "bridgeURL": bt.URL, + }) + jsonAPIResponse(c, presenters.NewBridgeResource(bt), "bridge") } @@ -207,5 +222,7 @@ func (btc *BridgeTypesController) Destroy(c *gin.Context) { return } + btc.App.GetLogger().Auditf(logger.BRIDGE_DELETED, map[string]interface{}{"name": name}) + jsonAPIResponse(c, presenters.NewBridgeResource(bt), "bridge") } diff --git a/core/web/chains_controller.go b/core/web/chains_controller.go index d65a0e72e08..ec226a9350b 100644 --- a/core/web/chains_controller.go +++ b/core/web/chains_controller.go @@ -2,6 +2,7 @@ package web import ( "database/sql" + "encoding/json" "net/http" "github.com/gin-gonic/gin" @@ -9,6 +10,7 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink/core/chains" + "github.com/smartcontractkit/chainlink/core/logger" ) type ChainsController interface { @@ -30,16 +32,18 @@ type chainsController[I chains.ID, C chains.Config, R jsonapi.EntityNamer] struc errNotEnabled error parseChainID func(string) (I, error) newResource func(chains.DBChain[I, C]) R + lggr logger.Logger } func newChainsController[I chains.ID, C chains.Config, R jsonapi.EntityNamer](prefix string, chainSet chains.DBChainSet[I, C], errNotEnabled error, - parseChainID func(string) (I, error), newResource func(chains.DBChain[I, C]) R) *chainsController[I, C, R] { + parseChainID func(string) (I, error), newResource func(chains.DBChain[I, C]) R, lggr logger.Logger) *chainsController[I, C, R] { return &chainsController[I, C, R]{ resourceName: prefix + "_chain", chainSet: chainSet, errNotEnabled: errNotEnabled, parseChainID: parseChainID, newResource: newResource, + lggr: lggr, } } @@ -91,6 +95,9 @@ func (cc *chainsController[I, C, R]) Create(c *gin.Context) { return } + chainj, _ := json.Marshal(chain) + cc.lggr.Auditf(logger.CHAIN_ADDED, map[string]interface{}{"chain": chainj}) + jsonAPIResponseWithStatus(c, cc.newResource(chain), cc.resourceName, http.StatusCreated) } @@ -145,6 +152,9 @@ func (cc *chainsController[I, C, R]) Update(c *gin.Context) { return } + chainj, _ := json.Marshal(chain) + cc.lggr.Auditf(logger.CHAIN_SPEC_UPDATED, map[string]interface{}{"chain": chainj}) + jsonAPIResponse(c, cc.newResource(chain), cc.resourceName) } @@ -167,5 +177,7 @@ func (cc *chainsController[I, C, R]) Delete(c *gin.Context) { return } + cc.lggr.Auditf(logger.CHAIN_DELETED, map[string]interface{}{"id": id}) + jsonAPIResponseWithStatus(c, nil, cc.resourceName, http.StatusNoContent) } diff --git a/core/web/config_controller.go b/core/web/config_controller.go index ee32a54da4e..45bbdd05c26 100644 --- a/core/web/config_controller.go +++ b/core/web/config_controller.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/smartcontractkit/chainlink/core/config" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/utils" @@ -22,6 +23,7 @@ type ConfigController struct { func (cc *ConfigController) Show(c *gin.Context) { cw := config.NewConfigPrinter(cc.App.GetConfig()) + cc.App.GetLogger().Auditf(logger.ENV_NONCRITICAL_ENV_DUMPED, map[string]interface{}{}) jsonAPIResponse(c, cw, "config") } @@ -85,5 +87,7 @@ func (cc *ConfigController) Patch(c *gin.Context) { To: request.EvmGasPriceDefault.String(), }, EVMChainID: utils.NewBig(chain.ID()), } + + cc.App.GetLogger().Auditf(logger.CONFIG_UPDATED, map[string]interface{}{"configResponse": response}) jsonAPIResponse(c, response, "config") } diff --git a/core/web/csa_keys_controller.go b/core/web/csa_keys_controller.go index 31aeaafc3a4..e62a186edc3 100644 --- a/core/web/csa_keys_controller.go +++ b/core/web/csa_keys_controller.go @@ -7,6 +7,7 @@ import ( "github.com/gin-gonic/gin" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/keystore" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -43,6 +44,12 @@ func (ctrl *CSAKeysController) Create(c *gin.Context) { jsonAPIError(c, http.StatusInternalServerError, err) return } + + ctrl.App.GetLogger().Auditf(logger.CSA_KEY_CREATED, map[string]interface{}{ + "CSAPublicKey": key.PublicKey, + "CSVersion": key.Version, + }) + jsonAPIResponse(c, presenters.NewCSAKeyResource(key), "csaKeys") } @@ -62,6 +69,11 @@ func (ctrl *CSAKeysController) Import(c *gin.Context) { return } + ctrl.App.GetLogger().Auditf(logger.CSA_KEY_IMPORTED, map[string]interface{}{ + "CSAPublicKey": key.PublicKey, + "CSVersion": key.Version, + }) + jsonAPIResponse(c, presenters.NewCSAKeyResource(key), "csaKey") } @@ -77,5 +89,7 @@ func (ctrl *CSAKeysController) Export(c *gin.Context) { jsonAPIError(c, http.StatusInternalServerError, err) return } + + ctrl.App.GetLogger().Auditf(logger.CSA_KEY_EXPORTED, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/eth_keys_controller.go b/core/web/eth_keys_controller.go index b7e12d62eee..a611a7168c6 100644 --- a/core/web/eth_keys_controller.go +++ b/core/web/eth_keys_controller.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/core/assets" "github.com/smartcontractkit/chainlink/core/chains/evm" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/core/utils" @@ -131,6 +132,11 @@ func (ekc *ETHKeysController) Create(c *gin.Context) { return } + ekc.App.GetLogger().Auditf(logger.ETH_KEY_CREATED, map[string]interface{}{ + "ethPublicKey": key.Address, + "ethID": key.ID(), + }) + jsonAPIResponseWithStatus(c, r, "account", http.StatusCreated) } @@ -181,6 +187,11 @@ func (ekc *ETHKeysController) Update(c *gin.Context) { return } + ekc.App.GetLogger().Auditf(logger.ETH_KEY_UPDATED, map[string]interface{}{ + "ethPublicKey": key.Address, + "ethID": key.ID(), + }) + jsonAPIResponseWithStatus(c, r, "account", http.StatusOK) } @@ -232,6 +243,7 @@ func (ekc *ETHKeysController) Delete(c *gin.Context) { return } + ekc.App.GetLogger().Auditf(logger.ETH_KEY_DELETED, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, r, "account") } @@ -279,6 +291,11 @@ func (ekc *ETHKeysController) Import(c *gin.Context) { return } + ekc.App.GetLogger().Auditf(logger.ETH_KEY_IMPORTED, map[string]interface{}{ + "ethPublicKey": key.Address, + "ethID": key.ID(), + }) + jsonAPIResponse(c, r, "account") } @@ -293,6 +310,8 @@ func (ekc *ETHKeysController) Export(c *gin.Context) { jsonAPIError(c, http.StatusInternalServerError, err) return } + + ekc.App.GetLogger().Auditf(logger.ETH_KEY_EXPORTED, map[string]interface{}{"address": address}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/evm_chains_controller.go b/core/web/evm_chains_controller.go index 2487e17909a..300efc05dea 100644 --- a/core/web/evm_chains_controller.go +++ b/core/web/evm_chains_controller.go @@ -17,5 +17,5 @@ func NewEVMChainsController(app chainlink.Application) ChainsController { return } return newChainsController[utils.Big, *types.ChainCfg, presenters.EVMChainResource]( - "evm", app.GetChains().EVM, ErrEVMNotEnabled, parse, presenters.NewEVMChainResource) + "evm", app.GetChains().EVM, ErrEVMNotEnabled, parse, presenters.NewEVMChainResource, app.GetLogger()) } diff --git a/core/web/evm_forwarders_controller.go b/core/web/evm_forwarders_controller.go index 6157358784e..a2d18c8df75 100644 --- a/core/web/evm_forwarders_controller.go +++ b/core/web/evm_forwarders_controller.go @@ -5,6 +5,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink/core/chains/evm/forwarders" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/utils" "github.com/smartcontractkit/chainlink/core/utils/stringutils" @@ -58,6 +59,11 @@ func (cc *EVMForwardersController) Create(c *gin.Context) { return } + cc.App.GetLogger().Auditf(logger.FORWARDER_CREATED, map[string]interface{}{ + "forwarderID": fwd.ID, + "forwarderAddress": fwd.Address, + "forwarderEVMChainID": fwd.EVMChainID, + }) jsonAPIResponseWithStatus(c, presenters.NewEVMForwarderResource(fwd), "forwarder", http.StatusCreated) } @@ -77,5 +83,6 @@ func (cc *EVMForwardersController) Delete(c *gin.Context) { return } + cc.App.GetLogger().Auditf(logger.FORWARDER_DELETED, map[string]interface{}{"id": id}) jsonAPIResponseWithStatus(c, nil, "forwarder", http.StatusNoContent) } diff --git a/core/web/evm_nodes_controller.go b/core/web/evm_nodes_controller.go index a7cb964c498..761baae3432 100644 --- a/core/web/evm_nodes_controller.go +++ b/core/web/evm_nodes_controller.go @@ -28,5 +28,7 @@ func NewEVMNodesController(app chainlink.Application) NodesController { HTTPURL: request.HTTPURL, SendOnly: request.SendOnly, }, nil - }) + }, + app.GetLogger(), + ) } diff --git a/core/web/evm_transfer_controller.go b/core/web/evm_transfer_controller.go index d9d893e2466..175fa5eaf06 100644 --- a/core/web/evm_transfer_controller.go +++ b/core/web/evm_transfer_controller.go @@ -9,6 +9,7 @@ import ( "github.com/smartcontractkit/chainlink/core/assets" "github.com/smartcontractkit/chainlink/core/chains/evm" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/store/models" "github.com/smartcontractkit/chainlink/core/utils" @@ -63,6 +64,10 @@ func (tc *EVMTransfersController) Create(c *gin.Context) { return } + tc.App.GetLogger().Auditf(logger.ETH_TRANSACTION_CREATED, map[string]interface{}{ + "ethTX": etx, + }) + jsonAPIResponse(c, presenters.NewEthTxResource(etx), "eth_tx") } diff --git a/core/web/external_initiators_controller.go b/core/web/external_initiators_controller.go index 7ce67175b90..c153bc858ec 100644 --- a/core/web/external_initiators_controller.go +++ b/core/web/external_initiators_controller.go @@ -8,6 +8,7 @@ import ( "github.com/smartcontractkit/chainlink/core/auth" "github.com/smartcontractkit/chainlink/core/bridges" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/store/models" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -84,6 +85,12 @@ func (eic *ExternalInitiatorsController) Create(c *gin.Context) { return } + eic.App.GetLogger().Auditf(logger.EXTERNAL_INITIATOR_CREATED, map[string]interface{}{ + "externalInitiatorID": ei.ID, + "externalInitiatorName": ei.Name, + "externalInitiatorURL": ei.URL, + }) + resp := presenters.NewExternalInitiatorAuthentication(*ei, *eia) jsonAPIResponseWithStatus(c, resp, "external initiator authentication", http.StatusCreated) } @@ -101,5 +108,6 @@ func (eic *ExternalInitiatorsController) Destroy(c *gin.Context) { return } + eic.App.GetLogger().Auditf(logger.EXTERNAL_INITIATOR_DELETED, map[string]interface{}{"name": name}) jsonAPIResponseWithStatus(c, nil, "external initiator", http.StatusNoContent) } diff --git a/core/web/jobs_controller.go b/core/web/jobs_controller.go index a1fa1f14f53..e78aab49619 100644 --- a/core/web/jobs_controller.go +++ b/core/web/jobs_controller.go @@ -3,6 +3,7 @@ package web import ( "context" "database/sql" + "encoding/json" "net/http" "time" @@ -10,6 +11,7 @@ import ( "github.com/pkg/errors" uuid "github.com/satori/go.uuid" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/blockhashstore" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/cron" @@ -156,6 +158,9 @@ func (jc *JobsController) Create(c *gin.Context) { return } + jbj, _ := json.Marshal(jb) + jc.App.GetLogger().Auditf(logger.JOB_CREATED, map[string]interface{}{"job": string(jbj)}) + jsonAPIResponse(c, presenters.NewJobResource(jb), jb.Type.String()) } @@ -174,14 +179,13 @@ func (jc *JobsController) Delete(c *gin.Context) { err = jc.App.DeleteJob(c.Request.Context(), j.ID) if errors.Is(err, sql.ErrNoRows) { jsonAPIError(c, http.StatusNotFound, errors.New("JobSpec not found")) - return } if err != nil { jsonAPIError(c, http.StatusInternalServerError, err) - return } + jc.App.GetLogger().Auditf(logger.JOB_DELETED, map[string]interface{}{"id": j.ID}) jsonAPIResponseWithStatus(c, nil, "job", http.StatusNoContent) } diff --git a/core/web/keys_controller.go b/core/web/keys_controller.go index f8483e1451f..e9f74212f14 100644 --- a/core/web/keys_controller.go +++ b/core/web/keys_controller.go @@ -3,12 +3,15 @@ package web import ( "io/ioutil" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/manyminds/api2go/jsonapi" "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/keystore" + "github.com/smartcontractkit/chainlink/core/services/keystore/keys/solkey" + "github.com/smartcontractkit/chainlink/core/services/keystore/keys/terrakey" ) type Keystore[K keystore.Key] interface { @@ -67,6 +70,21 @@ func (kc *keysController[K, R]) Create(c *gin.Context) { jsonAPIError(c, http.StatusInternalServerError, err) return } + + // Emit audit log, determine if Terra or Solana key + switch unwrappedKey := any(key).(type) { + case terrakey.Key: + kc.lggr.Auditf(logger.TERRA_KEY_CREATED, map[string]interface{}{ + "terraPublicKey": unwrappedKey.PublicKey(), + "terraID": unwrappedKey.ID(), + }) + case solkey.Key: + kc.lggr.Auditf(logger.SOLANA_KEY_CREATED, map[string]interface{}{ + "solanaPublicKey": unwrappedKey.PublicKey(), + "solanaID": unwrappedKey.ID(), + }) + } + jsonAPIResponse(c, kc.newResource(key), kc.resourceName) } @@ -82,6 +100,15 @@ func (kc *keysController[K, R]) Delete(c *gin.Context) { jsonAPIError(c, http.StatusInternalServerError, err) return } + + // Emit audit log, determine if Terra or Solana key + switch any(key).(type) { + case terrakey.Key: + kc.lggr.Auditf(logger.TERRA_KEY_DELETED, map[string]interface{}{"terraID": keyID}) + case solkey.Key: + kc.lggr.Auditf(logger.SOLANA_KEY_DELETED, map[string]interface{}{"solanaID": keyID}) + } + jsonAPIResponse(c, kc.newResource(key), kc.resourceName) } @@ -100,6 +127,20 @@ func (kc *keysController[K, R]) Import(c *gin.Context) { return } + // Emit audit log, determine if Terra or Solana key + switch unwrappedKey := any(key).(type) { + case terrakey.Key: + kc.lggr.Auditf(logger.TERRA_KEY_IMPORTED, map[string]interface{}{ + "terraPublicKey": unwrappedKey.PublicKey(), + "terraID": unwrappedKey.ID(), + }) + case solkey.Key: + kc.lggr.Auditf(logger.SOLANA_KEY_IMPORTED, map[string]interface{}{ + "solanaPublicKey": unwrappedKey.PublicKey(), + "solanaID": unwrappedKey.ID(), + }) + } + jsonAPIResponse(c, kc.newResource(key), kc.resourceName) } @@ -114,5 +155,11 @@ func (kc *keysController[K, R]) Export(c *gin.Context) { return } + if strings.HasPrefix(c.Request.URL.Path, "/v2/keys/terra") { + kc.lggr.Auditf(logger.TERRA_KEY_EXPORTED, map[string]interface{}{"terraID": keyID}) + } else if strings.HasPrefix(c.Request.URL.Path, "/v2/keys/solana") { + kc.lggr.Auditf(logger.SOLANA_KEY_EXPORTED, map[string]interface{}{"solanaID": keyID}) + } + c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/log_controller.go b/core/web/log_controller.go index f29b8104d72..12a2c98d213 100644 --- a/core/web/log_controller.go +++ b/core/web/log_controller.go @@ -8,6 +8,7 @@ import ( "github.com/gin-gonic/gin" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/web/presenters" ) @@ -90,5 +91,15 @@ func (cc *LogController) Patch(c *gin.Context) { LogLevel: lvls, } + cc.App.GetLogger().Auditf(logger.GLOBAL_LOG_LEVEL_SET, map[string]interface{}{"logLevel": request.Level}) + + if request.Level == "debug" { + if request.SqlEnabled != nil && *request.SqlEnabled { + cc.App.GetLogger().Auditf(logger.CONFIG_SQL_LOGGING_ENABLED, map[string]interface{}{}) + } else { + cc.App.GetLogger().Auditf(logger.CONFIG_SQL_LOGGING_DISABLED, map[string]interface{}{}) + } + } + jsonAPIResponse(c, response, "log") } diff --git a/core/web/nodes_controller.go b/core/web/nodes_controller.go index 53e05197bc1..f888bbb148b 100644 --- a/core/web/nodes_controller.go +++ b/core/web/nodes_controller.go @@ -8,6 +8,7 @@ import ( "github.com/manyminds/api2go/jsonapi" "github.com/smartcontractkit/chainlink/core/chains" + "github.com/smartcontractkit/chainlink/core/logger" ) type NodesController interface { @@ -25,6 +26,7 @@ type nodesController[I chains.ID, N chains.Node, R jsonapi.EntityNamer] struct { errNotEnabled error newResource func(N) R createNode func(*gin.Context) (N, error) + lggr logger.Logger } func newNodesController[I chains.ID, N chains.Node, R jsonapi.EntityNamer]( @@ -33,6 +35,7 @@ func newNodesController[I chains.ID, N chains.Node, R jsonapi.EntityNamer]( parseChainID func(string) (I, error), newResource func(N) R, createNode func(*gin.Context) (N, error), + lggr logger.Logger, ) NodesController { return &nodesController[I, N, R]{ nodeSet: nodeSet, @@ -40,6 +43,7 @@ func newNodesController[I chains.ID, N chains.Node, R jsonapi.EntityNamer]( parseChainID: parseChainID, newResource: newResource, createNode: createNode, + lggr: lggr, } } @@ -95,6 +99,8 @@ func (n *nodesController[I, N, R]) Create(c *gin.Context) { return } + n.lggr.Auditf(logger.CHAIN_RPC_NODE_ADDED, map[string]interface{}{}) + jsonAPIResponse(c, n.newResource(node), "node") } @@ -117,5 +123,7 @@ func (n *nodesController[I, N, R]) Delete(c *gin.Context) { return } + n.lggr.Auditf(logger.CHAIN_DELETED, map[string]interface{}{"id": id}) + jsonAPIResponseWithStatus(c, nil, "node", http.StatusNoContent) } diff --git a/core/web/ocr2_keys_controller.go b/core/web/ocr2_keys_controller.go index e95e8347f9d..21a336ca771 100644 --- a/core/web/ocr2_keys_controller.go +++ b/core/web/ocr2_keys_controller.go @@ -6,6 +6,7 @@ import ( "github.com/gin-gonic/gin" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -42,6 +43,15 @@ func (ocr2kc *OCR2KeysController) Create(c *gin.Context) { jsonAPIError(c, http.StatusInternalServerError, err) return } + + ocr2kc.App.GetLogger().Auditf(logger.OCR2_KEY_BUNDLE_CREATED, map[string]interface{}{ + "ocr2KeyID": key.ID(), + "ocr2KeyChainType": key.ChainType(), + "ocr2KeyConfigEncryptionPublicKey": key.ConfigEncryptionPublicKey(), + "ocr2KeyOffchainPublicKey": key.OffchainPublicKey(), + "ocr2KeyMaxSignatureLength": key.MaxSignatureLength(), + "ocr2KeyPublicKey": key.PublicKey(), + }) jsonAPIResponse(c, presenters.NewOCR2KeysBundleResource(key), "offChainReporting2KeyBundle") } @@ -60,6 +70,8 @@ func (ocr2kc *OCR2KeysController) Delete(c *gin.Context) { jsonAPIError(c, http.StatusInternalServerError, err) return } + + ocr2kc.App.GetLogger().Auditf(logger.OCR2_KEY_BUNDLE_DELETED, map[string]interface{}{"id": id}) jsonAPIResponse(c, presenters.NewOCR2KeysBundleResource(key), "offChainReporting2KeyBundle") } @@ -81,6 +93,15 @@ func (ocr2kc *OCR2KeysController) Import(c *gin.Context) { return } + ocr2kc.App.GetLogger().Auditf(logger.OCR2_KEY_BUNDLE_IMPORTED, map[string]interface{}{ + "ocr2KeyID": keyBundle.ID(), + "ocr2KeyChainType": keyBundle.ChainType(), + "ocr2KeyConfigEncryptionPublicKey": keyBundle.ConfigEncryptionPublicKey(), + "ocr2KeyOffchainPublicKey": keyBundle.OffchainPublicKey(), + "ocr2KeyMaxSignatureLength": keyBundle.MaxSignatureLength(), + "ocr2KeyPublicKey": keyBundle.PublicKey(), + }) + jsonAPIResponse(c, presenters.NewOCR2KeysBundleResource(keyBundle), "offChainReporting2KeyBundle") } @@ -98,5 +119,6 @@ func (ocr2kc *OCR2KeysController) Export(c *gin.Context) { return } + ocr2kc.App.GetLogger().Auditf(logger.OCR2_KEY_BUNDLE_EXPORTED, map[string]interface{}{"keyID": stringID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/ocr_keys_controller.go b/core/web/ocr_keys_controller.go index fe76560eb2c..ea19c229085 100644 --- a/core/web/ocr_keys_controller.go +++ b/core/web/ocr_keys_controller.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/web/presenters" ) @@ -35,6 +36,11 @@ func (ocrkc *OCRKeysController) Create(c *gin.Context) { jsonAPIError(c, http.StatusInternalServerError, err) return } + + ocrkc.App.GetLogger().Auditf(logger.OCR_KEY_BUNDLE_CREATED, map[string]interface{}{ + "ocrKeyBundleID": key.ID(), + "ocrKeyBundlePublicKeyAddressOnChain": key.PublicKeyAddressOnChain(), + }) jsonAPIResponse(c, presenters.NewOCRKeysBundleResource(key), "offChainReportingKeyBundle") } @@ -54,6 +60,8 @@ func (ocrkc *OCRKeysController) Delete(c *gin.Context) { jsonAPIError(c, http.StatusInternalServerError, err) return } + + ocrkc.App.GetLogger().Auditf(logger.OCR_KEY_BUNDLE_DELETED, map[string]interface{}{"id": id}) jsonAPIResponse(c, presenters.NewOCRKeysBundleResource(key), "offChainReportingKeyBundle") } @@ -75,6 +83,12 @@ func (ocrkc *OCRKeysController) Import(c *gin.Context) { return } + ocrkc.App.GetLogger().Auditf(logger.OCR_KEY_BUNDLE_IMPORTED, map[string]interface{}{ + "OCRID": encryptedOCRKeyBundle.GetID(), + "OCRPublicKeyAddressOnChain": encryptedOCRKeyBundle.PublicKeyAddressOnChain(), + "OCRPublicKeyOffChain": encryptedOCRKeyBundle.PublicKeyOffChain(), + }) + jsonAPIResponse(c, encryptedOCRKeyBundle, "offChainReportingKeyBundle") } @@ -92,5 +106,6 @@ func (ocrkc *OCRKeysController) Export(c *gin.Context) { return } + ocrkc.App.GetLogger().Auditf(logger.OCR_KEY_BUNDLE_EXPORTED, map[string]interface{}{"keyID": stringID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/p2p_keys_controller.go b/core/web/p2p_keys_controller.go index 81cec668690..5c3b3304035 100644 --- a/core/web/p2p_keys_controller.go +++ b/core/web/p2p_keys_controller.go @@ -6,6 +6,7 @@ import ( "github.com/gin-gonic/gin" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -37,6 +38,13 @@ func (p2pkc *P2PKeysController) Create(c *gin.Context) { jsonAPIError(c, http.StatusInternalServerError, err) return } + + p2pkc.App.GetLogger().Auditf(logger.P2P_KEY_CREATED, map[string]interface{}{ + "p2pPublicKey": key.PublicKeyHex(), + "p2pID": key.ID(), + "p2pPeerID": key.PeerID(), + "p2pType": key.Type(), + }) jsonAPIResponse(c, presenters.NewP2PKeyResource(key), "p2pKey") } @@ -60,6 +68,8 @@ func (p2pkc *P2PKeysController) Delete(c *gin.Context) { jsonAPIError(c, http.StatusInternalServerError, err) return } + + p2pkc.App.GetLogger().Auditf(logger.P2P_KEY_DELETED, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, presenters.NewP2PKeyResource(key), "p2pKey") } @@ -81,6 +91,12 @@ func (p2pkc *P2PKeysController) Import(c *gin.Context) { return } + p2pkc.App.GetLogger().Auditf(logger.P2P_KEY_IMPORTED, map[string]interface{}{ + "p2pPublicKey": key.PublicKeyHex(), + "p2pID": key.ID(), + "p2pPeerID": key.PeerID(), + "p2pType": key.Type(), + }) jsonAPIResponse(c, presenters.NewP2PKeyResource(key), "p2pKey") } @@ -103,5 +119,6 @@ func (p2pkc *P2PKeysController) Export(c *gin.Context) { return } + p2pkc.App.GetLogger().Auditf(logger.P2P_KEY_EXPORTED, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/pipeline_job_spec_errors_controller.go b/core/web/pipeline_job_spec_errors_controller.go index c20e7339268..e2b1be5d7f4 100644 --- a/core/web/pipeline_job_spec_errors_controller.go +++ b/core/web/pipeline_job_spec_errors_controller.go @@ -8,6 +8,7 @@ import ( "github.com/gin-gonic/gin" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/job" ) @@ -37,5 +38,6 @@ func (psec *PipelineJobSpecErrorsController) Destroy(c *gin.Context) { return } + psec.App.GetLogger().Auditf(logger.JOB_ERROR_DISMISSED, map[string]interface{}{"id": jobSpec.ID}) jsonAPIResponseWithStatus(c, nil, "job", http.StatusNoContent) } diff --git a/core/web/pipeline_runs_controller.go b/core/web/pipeline_runs_controller.go index 01794a13850..724041bf826 100644 --- a/core/web/pipeline_runs_controller.go +++ b/core/web/pipeline_runs_controller.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" uuid "github.com/satori/go.uuid" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/job" "github.com/smartcontractkit/chainlink/core/services/pipeline" @@ -179,5 +180,6 @@ func (prc *PipelineRunsController) Resume(c *gin.Context) { return } + prc.App.GetLogger().Auditf(logger.UNAUTHED_RUN_RESUMED, map[string]interface{}{"runID": c.Param("runID")}) c.Status(http.StatusOK) } diff --git a/core/web/resolver/mutation.go b/core/web/resolver/mutation.go index 691ca17b1ce..c34031aebb0 100644 --- a/core/web/resolver/mutation.go +++ b/core/web/resolver/mutation.go @@ -1,8 +1,11 @@ package resolver +// TODO: Andrew backup and revert this whole file + import ( "context" "database/sql" + "encoding/json" "fmt" "net/url" "time" @@ -16,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/core/auth" "github.com/smartcontractkit/chainlink/core/bridges" "github.com/smartcontractkit/chainlink/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/blockhashstore" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/cron" @@ -94,6 +98,13 @@ func (r *Resolver) CreateBridge(ctx context.Context, args struct{ Input createBr return nil, err } + r.App.GetLogger().Auditf(logger.BRIDGE_CREATED, map[string]interface{}{ + "bridgeName": bta.Name, + "bridgeConfirmations": bta.Confirmations, + "bridgeMinimumContractPayment": bta.MinimumContractPayment, + "bridgeURL": bta.URL, + }) + return NewCreateBridgePayload(*bt, bta.IncomingToken), nil } @@ -111,6 +122,11 @@ func (r *Resolver) CreateCSAKey(ctx context.Context) (*CreateCSAKeyPayloadResolv return nil, err } + r.App.GetLogger().Auditf(logger.CSA_KEY_CREATED, map[string]interface{}{ + "CSAPublicKey": key.PublicKey, + "CSVersion": key.Version, + }) + return NewCreateCSAKeyPayload(&key, nil), nil } @@ -130,6 +146,8 @@ func (r *Resolver) DeleteCSAKey(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.CSA_KEY_DELETED, map[string]interface{}{"id": args.ID}) + return NewDeleteCSAKeyPayload(key, nil), nil } @@ -220,6 +238,9 @@ func (r *Resolver) CreateFeedsManagerChainConfig(ctx context.Context, args struc return nil, err } + fmj, _ := json.Marshal(ccfg) + r.App.GetLogger().Auditf(logger.FEEDS_MAN_CHAIN_CONFIG_CREATED, map[string]interface{}{"feedsManager": fmj}) + return NewCreateFeedsManagerChainConfigPayload(ccfg, nil, nil), nil } @@ -254,6 +275,8 @@ func (r *Resolver) DeleteFeedsManagerChainConfig(ctx context.Context, args struc return nil, err } + r.App.GetLogger().Auditf(logger.FEEDS_MAN_CHAIN_CONFIG_DELETED, map[string]interface{}{"id": args.ID}) + return NewDeleteFeedsManagerChainConfigPayload(ccfg, nil), nil } @@ -335,6 +358,9 @@ func (r *Resolver) UpdateFeedsManagerChainConfig(ctx context.Context, args struc return nil, err } + fmj, _ := json.Marshal(ccfg) + r.App.GetLogger().Auditf(logger.FEEDS_MAN_CHAIN_CONFIG_UPDATED, map[string]interface{}{"feedsManager": fmj}) + return NewUpdateFeedsManagerChainConfigPayload(ccfg, nil, nil), nil } @@ -383,6 +409,9 @@ func (r *Resolver) CreateFeedsManager(ctx context.Context, args struct { return nil, err } + mgrj, _ := json.Marshal(mgr) + r.App.GetLogger().Auditf(logger.FEEDS_MAN_CREATED, map[string]interface{}{"mgrj": mgrj}) + return NewCreateFeedsManagerPayload(mgr, nil, nil), nil } @@ -445,6 +474,13 @@ func (r *Resolver) UpdateBridge(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.BRIDGE_UPDATED, map[string]interface{}{ + "bridgeName": bridge.Name, + "bridgeConfirmations": bridge.Confirmations, + "bridgeMinimumContractPayment": bridge.MinimumContractPayment, + "bridgeURL": bridge.URL, + }) + return NewUpdateBridgePayload(&bridge, nil), nil } @@ -496,6 +532,9 @@ func (r *Resolver) UpdateFeedsManager(ctx context.Context, args struct { return nil, err } + mgrj, _ := json.Marshal(mgr) + r.App.GetLogger().Auditf(logger.FEEDS_MAN_UPDATED, map[string]interface{}{"mgrj": mgrj}) + return NewUpdateFeedsManagerPayload(mgr, nil, nil), nil } @@ -509,6 +548,11 @@ func (r *Resolver) CreateOCRKeyBundle(ctx context.Context) (*CreateOCRKeyBundleP return nil, err } + r.App.GetLogger().Auditf(logger.OCR_KEY_BUNDLE_CREATED, map[string]interface{}{ + "ocrKeyBundleID": key.ID(), + "ocrKeyBundlePublicKeyAddressOnChain": key.PublicKeyAddressOnChain(), + }) + return NewCreateOCRKeyBundlePayload(&key), nil } @@ -527,6 +571,7 @@ func (r *Resolver) DeleteOCRKeyBundle(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.OCR_KEY_BUNDLE_DELETED, map[string]interface{}{"id": args.ID}) return NewDeleteOCRKeyBundlePayloadResolver(deletedKey, nil), nil } @@ -548,6 +593,16 @@ func (r *Resolver) CreateNode(ctx context.Context, args struct { return nil, err } + wsURL, _ := url.Parse(args.Input.WSURL.String) // Forward only RPC host to logs + httpURL, _ := url.Parse(args.Input.HTTPURL.String) + r.App.GetLogger().Auditf(logger.CHAIN_RPC_NODE_ADDED, map[string]interface{}{ + "chainNodeName": args.Input.Name, + "chainNodeEvmChainID": args.Input.EVMChainID, + "chainNodeRPCWebSocketHost": wsURL.Host, + "chainNodeRPCHTTPHost": httpURL.Host, + "chainNodeSendOnly": args.Input.SendOnly, + }) + return NewCreateNodePayloadResolver(&node), nil } @@ -584,6 +639,7 @@ func (r *Resolver) DeleteNode(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.CHAIN_RPC_NODE_DELETED, map[string]interface{}{"id": id}) return NewDeleteNodePayloadResolver(&node, nil), nil } @@ -621,6 +677,7 @@ func (r *Resolver) DeleteBridge(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.BRIDGE_DELETED, map[string]interface{}{"name": bt.Name}) return NewDeleteBridgePayload(&bt, nil), nil } @@ -634,6 +691,13 @@ func (r *Resolver) CreateP2PKey(ctx context.Context) (*CreateP2PKeyPayloadResolv return nil, err } + r.App.GetLogger().Auditf(logger.P2P_KEY_CREATED, map[string]interface{}{ + "p2pPublicKey": key.PublicKeyHex(), + "p2pID": key.ID(), + "p2pPeerID": key.PeerID(), + "p2pType": key.Type(), + }) + return NewCreateP2PKeyPayload(key), nil } @@ -657,6 +721,7 @@ func (r *Resolver) DeleteP2PKey(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.P2P_KEY_DELETED, map[string]interface{}{"id": args.ID}) return NewDeleteP2PKeyPayload(key, nil), nil } @@ -670,6 +735,12 @@ func (r *Resolver) CreateVRFKey(ctx context.Context) (*CreateVRFKeyPayloadResolv return nil, err } + r.App.GetLogger().Auditf(logger.VRF_KEY_CREATED, map[string]interface{}{ + "vrfPublicKey": key.PublicKey, + "vrfID": key.ID(), + "vrfPublicKeyAddress": key.PublicKey.Address(), + }) + return NewCreateVRFKeyPayloadResolver(key), nil } @@ -688,6 +759,7 @@ func (r *Resolver) DeleteVRFKey(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.VRF_KEY_DELETED, map[string]interface{}{"id": args.ID}) return NewDeleteVRFKeyPayloadResolver(key, nil), nil } @@ -725,6 +797,9 @@ func (r *Resolver) ApproveJobProposalSpec(ctx context.Context, args struct { } } + specj, _ := json.Marshal(spec) + r.App.GetLogger().Auditf(logger.JOB_PROPOSAL_SPEC_APPROVED, map[string]interface{}{"spec": specj}) + return NewApproveJobProposalSpecPayload(spec, err), nil } @@ -757,6 +832,9 @@ func (r *Resolver) CancelJobProposalSpec(ctx context.Context, args struct { } } + specj, _ := json.Marshal(spec) + r.App.GetLogger().Auditf(logger.JOB_PROPOSAL_SPEC_CANCELED, map[string]interface{}{"spec": specj}) + return NewCancelJobProposalSpecPayload(spec, err), nil } @@ -789,6 +867,9 @@ func (r *Resolver) RejectJobProposalSpec(ctx context.Context, args struct { } } + specj, _ := json.Marshal(spec) + r.App.GetLogger().Auditf(logger.JOB_PROPOSAL_SPEC_REJECTED, map[string]interface{}{"spec": specj}) + return NewRejectJobProposalSpecPayload(spec, err), nil } @@ -824,6 +905,9 @@ func (r *Resolver) UpdateJobProposalSpecDefinition(ctx context.Context, args str } } + specj, _ := json.Marshal(spec) + r.App.GetLogger().Auditf(logger.JOB_PROPOSAL_SPEC_UPDATED, map[string]interface{}{"spec": specj}) + return NewUpdateJobProposalSpecDefinitionPayload(spec, err), nil } @@ -845,6 +929,8 @@ func (r *Resolver) UpdateUserPassword(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.OldPassword, dbUser.HashedPassword) { + r.App.GetLogger().Auditf(logger.PASSWORD_RESET_ATTEMPT_FAILED_MISMATCH, map[string]interface{}{"user": dbUser.Email}) + return NewUpdatePasswordPayload(nil, map[string]string{ "oldPassword": "old password does not match", }), nil @@ -859,6 +945,7 @@ func (r *Resolver) UpdateUserPassword(ctx context.Context, args struct { return nil, failedPasswordUpdateError{} } + r.App.GetLogger().Auditf(logger.PASSWORD_RESET_SUCCESS, map[string]interface{}{"user": dbUser.Email}) return NewUpdatePasswordPayload(session.User, nil), nil } @@ -871,6 +958,12 @@ func (r *Resolver) SetSQLLogging(ctx context.Context, args struct { r.App.GetConfig().SetLogSQL(args.Input.Enabled) + if args.Input.Enabled { + r.App.GetLogger().Auditf(logger.CONFIG_SQL_LOGGING_ENABLED, map[string]interface{}{}) + } else { + r.App.GetLogger().Auditf(logger.CONFIG_SQL_LOGGING_DISABLED, map[string]interface{}{}) + } + return NewSetSQLLoggingPayload(args.Input.Enabled), nil } @@ -887,6 +980,8 @@ func (r *Resolver) CreateAPIToken(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.Password, dbUser.HashedPassword) { + r.App.GetLogger().Auditf(logger.API_TOKEN_CREATE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": dbUser.Email}) + return NewCreateAPITokenPayload(nil, map[string]string{ "password": "incorrect password", }), nil @@ -897,6 +992,7 @@ func (r *Resolver) CreateAPIToken(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.API_TOKEN_CREATED, map[string]interface{}{"user": dbUser.Email}) return NewCreateAPITokenPayload(newToken, nil), nil } @@ -913,6 +1009,8 @@ func (r *Resolver) DeleteAPIToken(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.Password, dbUser.HashedPassword) { + r.App.GetLogger().Auditf(logger.API_TOKEN_DELETE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": dbUser.Email}) + return NewDeleteAPITokenPayload(nil, map[string]string{ "password": "incorrect password", }), nil @@ -923,6 +1021,8 @@ func (r *Resolver) DeleteAPIToken(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.API_TOKEN_DELETED, map[string]interface{}{"user": dbUser.Email}) + return NewDeleteAPITokenPayload(&auth.Token{ AccessKey: dbUser.TokenKey.String, }, nil), nil @@ -972,6 +1072,9 @@ func (r *Resolver) CreateChain(ctx context.Context, args struct { return nil, err } + chainj, _ := json.Marshal(chain) + r.App.GetLogger().Auditf(logger.CHAIN_ADDED, map[string]interface{}{"chain": chainj}) + return NewCreateChainPayload(&chain, nil), nil } @@ -1024,6 +1127,9 @@ func (r *Resolver) UpdateChain(ctx context.Context, args struct { return nil, err } + chainj, _ := json.Marshal(chain) + r.App.GetLogger().Auditf(logger.CHAIN_SPEC_UPDATED, map[string]interface{}{"chainj": chainj}) + return NewUpdateChainPayload(&chain, nil, nil), nil } @@ -1054,6 +1160,7 @@ func (r *Resolver) DeleteChain(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.CHAIN_DELETED, map[string]interface{}{"id": id}) return NewDeleteChainPayload(&chain, nil), nil } @@ -1119,6 +1226,9 @@ func (r *Resolver) CreateJob(ctx context.Context, args struct { return nil, err } + jbj, _ := json.Marshal(jb) + r.App.GetLogger().Auditf(logger.JOB_CREATED, map[string]interface{}{"job": string(jbj)}) + return NewCreateJobPayload(r.App, &jb, nil), nil } @@ -1152,6 +1262,7 @@ func (r *Resolver) DeleteJob(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.JOB_DELETED, map[string]interface{}{"id": args.ID}) return NewDeleteJobPayload(r.App, &j, nil), nil } @@ -1185,6 +1296,7 @@ func (r *Resolver) DismissJobError(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.JOB_ERROR_DISMISSED, map[string]interface{}{"id": args.ID}) return NewDismissJobErrorPayload(&specErr, nil), nil } @@ -1214,6 +1326,7 @@ func (r *Resolver) RunJob(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.JOB_RUN_SET, map[string]interface{}{"jobID": args.ID, "jobRunID": jobRunID, "planRunID": plnRun}) return NewRunJobPayload(&plnRun, r.App, nil), nil } @@ -1238,6 +1351,7 @@ func (r *Resolver) SetGlobalLogLevel(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.GLOBAL_LOG_LEVEL_SET, map[string]interface{}{"logLevel": args.Level}) return NewSetGlobalLogLevelPayload(args.Level, nil), nil } @@ -1256,6 +1370,15 @@ func (r *Resolver) CreateOCR2KeyBundle(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.OCR2_KEY_BUNDLE_CREATED, map[string]interface{}{ + "ocrKeyID": key.ID(), + "ocrKeyChainType": key.ChainType(), + "ocrKeyConfigEncryptionPublicKey": key.ConfigEncryptionPublicKey(), + "ocrKeyOffchainPublicKey": key.OffchainPublicKey(), + "ocrKeyMaxSignatureLength": key.MaxSignatureLength(), + "ocrKeyPublicKey": key.PublicKey(), + }) + return NewCreateOCR2KeyBundlePayload(&key), nil } @@ -1278,5 +1401,6 @@ func (r *Resolver) DeleteOCR2KeyBundle(ctx context.Context, args struct { return nil, err } + r.App.GetLogger().Auditf(logger.OCR2_KEY_BUNDLE_DELETED, map[string]interface{}{"id": id}) return NewDeleteOCR2KeyBundlePayloadResolver(&key, nil), nil } diff --git a/core/web/sessions_controller.go b/core/web/sessions_controller.go index 2c45ef14e7f..3b8915fffd6 100644 --- a/core/web/sessions_controller.go +++ b/core/web/sessions_controller.go @@ -9,6 +9,7 @@ import ( "github.com/gin-gonic/gin" "go.uber.org/multierr" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" clsessions "github.com/smartcontractkit/chainlink/core/sessions" "github.com/smartcontractkit/chainlink/core/web/auth" @@ -83,6 +84,7 @@ func (sc *SessionsController) Destroy(c *gin.Context) { return } + sc.App.GetLogger().Auditf(logger.AUTH_SESSION_DELETED, map[string]interface{}{"removedSessionID": sessionID}) jsonAPIResponse(c, Session{Authenticated: false}, "session") } diff --git a/core/web/solana_chains_controller.go b/core/web/solana_chains_controller.go index 047b6c66685..5392102234b 100644 --- a/core/web/solana_chains_controller.go +++ b/core/web/solana_chains_controller.go @@ -9,5 +9,5 @@ import ( func NewSolanaChainsController(app chainlink.Application) ChainsController { return newChainsController[string, *db.ChainCfg]("solana", app.GetChains().Solana, ErrSolanaNotEnabled, - func(s string) (string, error) { return s, nil }, presenters.NewSolanaChainResource) + func(s string) (string, error) { return s, nil }, presenters.NewSolanaChainResource, app.GetLogger()) } diff --git a/core/web/solana_nodes_controller.go b/core/web/solana_nodes_controller.go index f7421f2e3b4..691cc309175 100644 --- a/core/web/solana_nodes_controller.go +++ b/core/web/solana_nodes_controller.go @@ -35,5 +35,7 @@ func NewSolanaNodesController(app chainlink.Application) NodesController { SolanaChainID: request.SolanaChainID, SolanaURL: request.SolanaURL, }, nil - }) + }, + app.GetLogger(), + ) } diff --git a/core/web/solana_transfer_controller.go b/core/web/solana_transfer_controller.go index d0529fafe45..c8c03213d2a 100644 --- a/core/web/solana_transfer_controller.go +++ b/core/web/solana_transfer_controller.go @@ -13,6 +13,7 @@ import ( solanaGo "github.com/gagliardetto/solana-go" "github.com/smartcontractkit/chainlink/core/chains" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" solanamodels "github.com/smartcontractkit/chainlink/core/store/models/solana" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -110,6 +111,9 @@ func (tc *SolanaTransfersController) Create(c *gin.Context) { resource.From = tr.From.String() resource.To = tr.To.String() + tc.App.GetLogger().Auditf(logger.SOLANA_TRANSACTION_CREATED, map[string]interface{}{ + "solanaTransactionResource": resource, + }) jsonAPIResponse(c, resource, "solana_tx") } diff --git a/core/web/terra_chains_controller.go b/core/web/terra_chains_controller.go index 07b919a9296..ce5147895e6 100644 --- a/core/web/terra_chains_controller.go +++ b/core/web/terra_chains_controller.go @@ -10,5 +10,5 @@ import ( func NewTerraChainsController(app chainlink.Application) ChainsController { parse := func(s string) (string, error) { return s, nil } return newChainsController[string, *db.ChainCfg, presenters.TerraChainResource]( - "terra", app.GetChains().Terra, ErrTerraNotEnabled, parse, presenters.NewTerraChainResource) + "terra", app.GetChains().Terra, ErrTerraNotEnabled, parse, presenters.NewTerraChainResource, app.GetLogger()) } diff --git a/core/web/terra_nodes_controller.go b/core/web/terra_nodes_controller.go index 8d042e5f49d..66eb6d3f0c7 100644 --- a/core/web/terra_nodes_controller.go +++ b/core/web/terra_nodes_controller.go @@ -37,5 +37,7 @@ func NewTerraNodesController(app chainlink.Application) NodesController { TerraChainID: request.TerraChainID, TendermintURL: request.TendermintURL, }, nil - }) + }, + app.GetLogger(), + ) } diff --git a/core/web/terra_transfer_controller.go b/core/web/terra_transfer_controller.go index 593ad2a029b..d8ed1329dec 100644 --- a/core/web/terra_transfer_controller.go +++ b/core/web/terra_transfer_controller.go @@ -12,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink/core/chains/terra" "github.com/smartcontractkit/chainlink/core/chains/terra/denom" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" terramodels "github.com/smartcontractkit/chainlink/core/store/models/terra" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -110,6 +111,10 @@ func (tc *TerraTransfersController) Create(c *gin.Context) { resource.TxHash = msg.TxHash resource.State = string(msg.State) + tc.App.GetLogger().Auditf(logger.TERRA_TRANSACTION_CREATED, map[string]interface{}{ + "terraTransactionResource": resource, + }) + jsonAPIResponse(c, resource, "terra_msg") } diff --git a/core/web/user_controller.go b/core/web/user_controller.go index c5af2b8852d..8688ed0ea18 100644 --- a/core/web/user_controller.go +++ b/core/web/user_controller.go @@ -9,6 +9,7 @@ import ( "github.com/gin-gonic/gin" "github.com/smartcontractkit/chainlink/core/auth" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" clsession "github.com/smartcontractkit/chainlink/core/sessions" "github.com/smartcontractkit/chainlink/core/utils" @@ -42,6 +43,7 @@ func (c *UserController) UpdatePassword(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.OldPassword, user.HashedPassword) { + c.App.GetLogger().Auditf(logger.PASSWORD_RESET_ATTEMPT_FAILED_MISMATCH, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusConflict, errors.New("old password does not match")) return } @@ -50,6 +52,7 @@ func (c *UserController) UpdatePassword(ctx *gin.Context) { return } + c.App.GetLogger().Auditf(logger.PASSWORD_RESET_SUCCESS, map[string]interface{}{"user": user.Email}) jsonAPIResponse(ctx, presenters.NewUserResource(user), "user") } @@ -67,6 +70,7 @@ func (c *UserController) NewAPIToken(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.Password, user.HashedPassword) { + c.App.GetLogger().Auditf(logger.API_TOKEN_CREATE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusUnauthorized, errors.New("incorrect password")) return } @@ -76,6 +80,7 @@ func (c *UserController) NewAPIToken(ctx *gin.Context) { return } + c.App.GetLogger().Auditf(logger.API_TOKEN_CREATED, map[string]interface{}{"user": user.Email}) jsonAPIResponseWithStatus(ctx, newToken, "auth_token", http.StatusCreated) } @@ -93,6 +98,7 @@ func (c *UserController) DeleteAPIToken(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.Password, user.HashedPassword) { + c.App.GetLogger().Auditf(logger.API_TOKEN_DELETE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusUnauthorized, errors.New("incorrect password")) return } @@ -101,6 +107,7 @@ func (c *UserController) DeleteAPIToken(ctx *gin.Context) { return } { + c.App.GetLogger().Auditf(logger.API_TOKEN_DELETED, map[string]interface{}{"user": user.Email}) jsonAPIResponseWithStatus(ctx, nil, "auth_token", http.StatusNoContent) } } diff --git a/core/web/vrf_keys_controller.go b/core/web/vrf_keys_controller.go index 67abdc132d2..2b1bc094f69 100644 --- a/core/web/vrf_keys_controller.go +++ b/core/web/vrf_keys_controller.go @@ -6,6 +6,7 @@ import ( "github.com/gin-gonic/gin" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/web/presenters" ) @@ -36,6 +37,12 @@ func (vrfkc *VRFKeysController) Create(c *gin.Context) { jsonAPIError(c, http.StatusInternalServerError, err) return } + + vrfkc.App.GetLogger().Auditf(logger.VRF_KEY_CREATED, map[string]interface{}{ + "vrfPublicKey": pk.PublicKey, + "vrfID": pk.ID(), + "vrfPublicKeyAddress": pk.PublicKey.Address(), + }) jsonAPIResponse(c, presenters.NewVRFKeyResource(pk, vrfkc.App.GetLogger()), "vrfKey") } @@ -55,6 +62,8 @@ func (vrfkc *VRFKeysController) Delete(c *gin.Context) { jsonAPIError(c, http.StatusInternalServerError, err) return } + + vrfkc.App.GetLogger().Auditf(logger.VRF_KEY_DELETED, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, presenters.NewVRFKeyResource(key, vrfkc.App.GetLogger()), "vrfKey") } @@ -76,6 +85,10 @@ func (vrfkc *VRFKeysController) Import(c *gin.Context) { return } + vrfkc.App.GetLogger().Auditf(logger.VRF_KEY_IMPORTED, map[string]interface{}{ + "vrfID": key.ID(), + "vrfPublicKey": key.PublicKey, + }) jsonAPIResponse(c, presenters.NewVRFKeyResource(key, vrfkc.App.GetLogger()), "vrfKey") } @@ -94,5 +107,6 @@ func (vrfkc *VRFKeysController) Export(c *gin.Context) { return } + vrfkc.App.GetLogger().Auditf(logger.VRF_KEY_EXPORTED, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/webauthn_controller.go b/core/web/webauthn_controller.go index 4432c7d3494..2409fdff6f7 100644 --- a/core/web/webauthn_controller.go +++ b/core/web/webauthn_controller.go @@ -1,12 +1,14 @@ package web import ( + "encoding/json" "errors" "fmt" "net/http" "github.com/gin-gonic/gin" + "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/sessions" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -85,5 +87,14 @@ func (c *WebAuthnController) FinishRegistration(ctx *gin.Context) { return } + // Forward registered credentials for audit logs + credj, err := json.Marshal(credential) + if err != nil { + c.App.GetLogger().Errorf("error in Marshal credentials: %s", err) + jsonAPIError(ctx, http.StatusBadRequest, errors.New("registration was unsuccessful")) + return + } + c.App.GetLogger().Auditf(logger.AUTH_2FA_ENROLLED, map[string]interface{}{"email": user.Email, "credential": string(credj)}) + ctx.String(http.StatusOK, "{}") } From 1019b4a2622e7f08076e8c8cfaadbecc92f20bce Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Fri, 10 Jun 2022 00:23:44 -0700 Subject: [PATCH 02/92] update CHANGELOG to include new SPLUNK environment variables --- docs/CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ebdcdac13a3..5a6182a7aac 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -29,12 +29,14 @@ This will prevent application boot in a future version of Chainlink. - The `p2pBootstrapPeers` property on OCR2 job specs has been renamed to `p2pv2Bootstrappers`. -### Added +### Added - Added `ETH_USE_FORWARDERS` config option to enable transactions forwarding contracts. - In job pipeline (direct request) the three new block variables are exposed: - `$(jobRun.blockReceiptsRoot)` : the root of the receipts trie of the block (hash) - `$(jobRun.blockTransactionsRoot)` : the root of the transaction trie of the block (hash) - `$(jobRun.blockStateRoot)` : the root of the final state trie of the block (hash) +- New optional Splunk logger added + - to opt in to receiving audit log events over HEC of actions performed within the node, set the `SPLUNK_URL` and `SPLUNK_TOKEN` environment variables accordingly. `SPLUNK_URL` is in the format of 'https://xxxxx.splunkcloud.com/services/collector' - `ethtx` tasks can now be configured to error if the transaction reverts on-chain. You must set `failOnRevert=true` on the task to enable this behavior, like so: `foo [type=ethtx failOnRevert=true ...]` @@ -56,7 +58,7 @@ If `minConfirmations` is not set on the task, the chain default will be used whi ### Removed - The `Optimism` OVM 1.0 `GAS_ESTIMATOR_MODE` has been removed. -- +- ## [1.4.1] - 2022-05-11 ### Fixed From 064a0e1b6d59b831919bf96d64f486432d98d6d5 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Fri, 10 Jun 2022 00:53:52 -0700 Subject: [PATCH 03/92] go generate for updates to logger interface in test harness --- core/logger/logger_mock_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/logger/logger_mock_test.go b/core/logger/logger_mock_test.go index c8f0e06a2e7..c951f4ab0bb 100644 --- a/core/logger/logger_mock_test.go +++ b/core/logger/logger_mock_test.go @@ -14,6 +14,11 @@ type MockLogger struct { mock.Mock } +// Auditf provides a mock function with given fields: eventID, data +func (_m *MockLogger) Auditf(eventID string, data map[string]interface{}) { + _m.Called(eventID, data) +} + // Critical provides a mock function with given fields: args func (_m *MockLogger) Critical(args ...interface{}) { var _ca []interface{} From d06fea20a63a6ed51ecaa8f071ad5d6f2d2850c1 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Fri, 17 Jun 2022 02:11:42 -0700 Subject: [PATCH 04/92] set environment string instead of develop bool in splunk logger constructor, log splunk logger errors back to outer logger, rename .Auditf to .Audit, reorg logger layers for splunk first, remove TODO --- core/logger/logger.go | 12 +- core/logger/logger_mock_test.go | 4 +- core/logger/null_logger.go | 18 +- core/logger/prometheus.go | 4 +- core/logger/sentry.go | 4 +- core/logger/splunk.go | 230 +++++++++++++----- core/logger/zap.go | 2 +- core/sessions/orm.go | 10 +- core/web/bridge_types_controller.go | 6 +- core/web/chains_controller.go | 6 +- core/web/config_controller.go | 4 +- core/web/csa_keys_controller.go | 6 +- core/web/eth_keys_controller.go | 10 +- core/web/evm_forwarders_controller.go | 4 +- core/web/evm_transfer_controller.go | 2 +- core/web/external_initiators_controller.go | 4 +- core/web/jobs_controller.go | 4 +- core/web/keys_controller.go | 16 +- core/web/log_controller.go | 6 +- core/web/nodes_controller.go | 4 +- core/web/ocr2_keys_controller.go | 8 +- core/web/ocr_keys_controller.go | 8 +- core/web/p2p_keys_controller.go | 8 +- .../pipeline_job_spec_errors_controller.go | 2 +- core/web/pipeline_runs_controller.go | 2 +- core/web/resolver/mutation.go | 82 +++---- core/web/sessions_controller.go | 2 +- core/web/solana_transfer_controller.go | 2 +- core/web/terra_transfer_controller.go | 2 +- core/web/user_controller.go | 12 +- core/web/vrf_keys_controller.go | 8 +- core/web/webauthn_controller.go | 2 +- docs/CHANGELOG.md | 2 +- 33 files changed, 305 insertions(+), 191 deletions(-) diff --git a/core/logger/logger.go b/core/logger/logger.go index 103bfe10cb5..482a42ac99f 100644 --- a/core/logger/logger.go +++ b/core/logger/logger.go @@ -81,7 +81,7 @@ type Logger interface { // The .Audit function here is specific to the SplunkLogger implementation // It is added to the interface to ensure that audit logs are sent regardless of log level. // All other Logger implementations should continue the pattern of propogating wrapped logger calls - Auditf(eventID string, data map[string]interface{}) + Audit(eventID string, data map[string]interface{}) Tracef(format string, values ...interface{}) Debugf(format string, values ...interface{}) @@ -270,12 +270,16 @@ func (c *Config) New() (Logger, func() error) { log.Fatal(err) } + l = newSentryLogger(l) + l = newPrometheusLogger(l) // If Splunk logging is enabled (token set), extend/wrap the logger with a splunkLogger instance if c.SplunkToken != "" { - l = newSplunkLogger(l, c.SplunkToken, c.SplunkURL, c.Hostname, c.ChainlinkDev) + env := "production" + if c.ChainlinkDev { + env = "develop" + } + l = newSplunkLogger(l, c.SplunkToken, c.SplunkURL, c.Hostname, env) } - l = newSentryLogger(l) - l = newPrometheusLogger(l) return l, close } diff --git a/core/logger/logger_mock_test.go b/core/logger/logger_mock_test.go index c951f4ab0bb..c0cada32d41 100644 --- a/core/logger/logger_mock_test.go +++ b/core/logger/logger_mock_test.go @@ -14,8 +14,8 @@ type MockLogger struct { mock.Mock } -// Auditf provides a mock function with given fields: eventID, data -func (_m *MockLogger) Auditf(eventID string, data map[string]interface{}) { +// Audit provides a mock function with given fields: eventID, data +func (_m *MockLogger) Audit(eventID string, data map[string]interface{}) { _m.Called(eventID, data) } diff --git a/core/logger/null_logger.go b/core/logger/null_logger.go index 81b32211248..5c09f404707 100644 --- a/core/logger/null_logger.go +++ b/core/logger/null_logger.go @@ -15,15 +15,15 @@ func (l *nullLogger) With(args ...interface{}) Logger { return l } func (l *nullLogger) Named(name string) Logger { return l } func (l *nullLogger) SetLogLevel(_ zapcore.Level) {} -func (l *nullLogger) Auditf(eventID string, data map[string]interface{}) {} -func (l *nullLogger) Trace(args ...interface{}) {} -func (l *nullLogger) Debug(args ...interface{}) {} -func (l *nullLogger) Info(args ...interface{}) {} -func (l *nullLogger) Warn(args ...interface{}) {} -func (l *nullLogger) Error(args ...interface{}) {} -func (l *nullLogger) Critical(args ...interface{}) {} -func (l *nullLogger) Panic(args ...interface{}) {} -func (l *nullLogger) Fatal(args ...interface{}) {} +func (l *nullLogger) Audit(eventID string, data map[string]interface{}) {} +func (l *nullLogger) Trace(args ...interface{}) {} +func (l *nullLogger) Debug(args ...interface{}) {} +func (l *nullLogger) Info(args ...interface{}) {} +func (l *nullLogger) Warn(args ...interface{}) {} +func (l *nullLogger) Error(args ...interface{}) {} +func (l *nullLogger) Critical(args ...interface{}) {} +func (l *nullLogger) Panic(args ...interface{}) {} +func (l *nullLogger) Fatal(args ...interface{}) {} func (l *nullLogger) Tracef(format string, values ...interface{}) {} func (l *nullLogger) Debugf(format string, values ...interface{}) {} diff --git a/core/logger/prometheus.go b/core/logger/prometheus.go index 9cefffa6fef..33e458c12f8 100644 --- a/core/logger/prometheus.go +++ b/core/logger/prometheus.go @@ -86,8 +86,8 @@ func (s *prometheusLogger) SetLogLevel(level zapcore.Level) { s.h.SetLogLevel(level) } -func (s *prometheusLogger) Auditf(eventID string, data map[string]interface{}) { - s.h.Auditf(eventID, data) +func (s *prometheusLogger) Audit(eventID string, data map[string]interface{}) { + s.h.Audit(eventID, data) } func (s *prometheusLogger) Trace(args ...interface{}) { diff --git a/core/logger/sentry.go b/core/logger/sentry.go index 1d7f1ab6f90..e144a1501a0 100644 --- a/core/logger/sentry.go +++ b/core/logger/sentry.go @@ -87,8 +87,8 @@ func (s *sentryLogger) SetLogLevel(level zapcore.Level) { s.h.SetLogLevel(level) } -func (s *sentryLogger) Auditf(eventID string, data map[string]interface{}) { - s.h.Auditf(eventID, data) +func (s *sentryLogger) Audit(eventID string, data map[string]interface{}) { + s.h.Audit(eventID, data) } func (s *sentryLogger) Trace(args ...interface{}) { diff --git a/core/logger/splunk.go b/core/logger/splunk.go index 576999daf14..1715b174102 100644 --- a/core/logger/splunk.go +++ b/core/logger/splunk.go @@ -122,29 +122,30 @@ const ( ) type splunkLogger struct { - logger Logger - splunkToken string - splunkURL string - developFlag bool - hostname string - localIP string + logger Logger + splunkToken string + splunkURL string + environmentName string + hostname string + localIP string } -func newSplunkLogger(logger Logger, splunkToken string, splunkURL string, hostname string, chainlinkDev bool) Logger { +func newSplunkLogger(logger Logger, splunkToken string, splunkURL string, hostname string, environment string) Logger { // Initialize and return Splunk logger struct with required state for HEC calls return &splunkLogger{ - logger: logger.Named("Splunk"), - splunkToken: splunkToken, - splunkURL: splunkURL, - developFlag: chainlinkDev, - hostname: hostname, - localIP: getLocalIP(), + logger: logger.Helper(1), + splunkToken: splunkToken, + splunkURL: splunkURL, + environmentName: environment, + hostname: hostname, + localIP: getLocalIP(), } } -func (l *splunkLogger) Auditf(eventID string, data map[string]interface{}) { +func (l *splunkLogger) Audit(eventID string, data map[string]interface{}) { // goroutine to async POST to splunk HTTP Event Collector (HEC) go l.postLogToSplunk(eventID, data) + l.logger.Audit(eventID, data) } // getLocalIP returns the first non- loopback local IP of the host @@ -165,17 +166,12 @@ func getLocalIP() string { } func (l *splunkLogger) postLogToSplunk(eventID string, data map[string]interface{}) { - env := "production" - if l.developFlag { - env = "develop" - } - // Splunk JSON data splunkLog := map[string]interface{}{ "eventID": eventID, "hostname": l.hostname, "localIP": l.localIP, - "env": env, + "env": l.environmentName, } if len(data) != 0 { splunkLog["data"] = data @@ -192,50 +188,166 @@ func (l *splunkLogger) postLogToSplunk(eventID string, data map[string]interface req.Header.Add("Authorization", "Splunk "+l.splunkToken) resp, err := httpClient.Do(req) if err != nil { - fmt.Printf("Error sending log to Splunk: %v\n", err) + l.logger.Errorw("Failed to send audit log to Splunk", "err", err, "splunkLog", splunkLog) } if resp.StatusCode != 200 { bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { - fmt.Printf("Error reading errored Splunk webhook response body: %v\n", err) + l.logger.Errorw("Error reading errored Splunk webhook response body", "err", err, "splunkLog", splunkLog) } - fmt.Printf("Error sending log to Splunk\nstatus code: %d\nbody: %s", resp.StatusCode, string(bodyBytes)) + l.logger.Errorw("Error sending log to Splunk", "statusCode", resp.StatusCode, "bodyString", string(bodyBytes)) + } +} + +func (l *splunkLogger) With(args ...interface{}) Logger { + return &splunkLogger{ + logger: l.logger.With(args...), + splunkToken: l.splunkToken, + splunkURL: l.splunkURL, + environmentName: l.environmentName, + hostname: l.hostname, + localIP: getLocalIP(), + } +} + +func (l *splunkLogger) Named(name string) Logger { + return &splunkLogger{ + logger: l.logger.Named(name), + splunkToken: l.splunkToken, + splunkURL: l.splunkURL, + environmentName: l.environmentName, + hostname: l.hostname, + localIP: getLocalIP(), + } +} + +func (l *splunkLogger) SetLogLevel(level zapcore.Level) { + l.logger.SetLogLevel(level) +} + +func (l *splunkLogger) Trace(args ...interface{}) { + l.logger.Trace(args...) +} + +func (l *splunkLogger) Debug(args ...interface{}) { + l.logger.Debug(args...) +} + +func (l *splunkLogger) Info(args ...interface{}) { + l.logger.Info(args...) +} + +func (l *splunkLogger) Warn(args ...interface{}) { + l.logger.Warn(args...) +} + +func (l *splunkLogger) Error(args ...interface{}) { + l.logger.Error(args...) +} + +func (l *splunkLogger) Critical(args ...interface{}) { + l.logger.Critical(args...) +} + +func (l *splunkLogger) Panic(args ...interface{}) { + l.logger.Panic(args...) +} + +func (l *splunkLogger) Fatal(args ...interface{}) { + l.logger.Fatal(args...) +} + +func (l *splunkLogger) Tracef(format string, values ...interface{}) { + l.logger.Tracef(format, values...) +} + +func (l *splunkLogger) Debugf(format string, values ...interface{}) { + l.logger.Debugf(format, values...) +} + +func (l *splunkLogger) Infof(format string, values ...interface{}) { + l.logger.Infof(format, values...) +} + +func (l *splunkLogger) Warnf(format string, values ...interface{}) { + l.logger.Warnf(format, values...) +} + +func (l *splunkLogger) Errorf(format string, values ...interface{}) { + l.logger.Errorf(format, values...) +} + +func (l *splunkLogger) Criticalf(format string, values ...interface{}) { + l.logger.Criticalf(format, values...) +} + +func (l *splunkLogger) Panicf(format string, values ...interface{}) { + l.logger.Panicf(format, values...) +} + +func (l *splunkLogger) Fatalf(format string, values ...interface{}) { + l.logger.Fatalf(format, values...) +} + +func (l *splunkLogger) Tracew(msg string, keysAndValues ...interface{}) { + l.logger.Tracew(msg, keysAndValues...) +} + +func (l *splunkLogger) Debugw(msg string, keysAndValues ...interface{}) { + l.logger.Debugw(msg, keysAndValues...) +} + +func (l *splunkLogger) Infow(msg string, keysAndValues ...interface{}) { + l.logger.Infow(msg, keysAndValues...) +} + +func (l *splunkLogger) Warnw(msg string, keysAndValues ...interface{}) { + l.logger.Warnw(msg, keysAndValues...) +} + +func (l *splunkLogger) Errorw(msg string, keysAndValues ...interface{}) { + l.logger.Errorw(msg, keysAndValues...) +} + +func (l *splunkLogger) Criticalw(msg string, keysAndValues ...interface{}) { + l.logger.Criticalw(msg, keysAndValues...) +} + +func (l *splunkLogger) Panicw(msg string, keysAndValues ...interface{}) { + l.logger.Panicw(msg, keysAndValues...) +} + +func (l *splunkLogger) Fatalw(msg string, keysAndValues ...interface{}) { + l.logger.Fatalw(msg, keysAndValues...) +} + +func (l *splunkLogger) ErrorIf(err error, msg string) { + if err != nil { + l.logger.Errorw(msg, "err", err) + } +} + +func (l *splunkLogger) ErrorIfClosing(c io.Closer, name string) { + if err := c.Close(); err != nil { + l.logger.Errorw(fmt.Sprintf("Error closing %s", name), "err", err) } } -// The Splunk logger should be the bottom of the nested logs, stub all other calls -func (l *splunkLogger) With(args ...interface{}) Logger { return l } -func (l *splunkLogger) Named(name string) Logger { return l } -func (l *splunkLogger) NewRootLogger(lvl zapcore.Level) (Logger, error) { return l, nil } -func (l *splunkLogger) SetLogLevel(_ zapcore.Level) {} -func (l *splunkLogger) Trace(args ...interface{}) {} -func (l *splunkLogger) Info(args ...interface{}) {} -func (l *splunkLogger) Debug(args ...interface{}) {} -func (l *splunkLogger) Warn(args ...interface{}) {} -func (l *splunkLogger) Error(args ...interface{}) {} -func (l *splunkLogger) Critical(args ...interface{}) {} -func (l *splunkLogger) Panic(args ...interface{}) {} -func (l *splunkLogger) Fatal(args ...interface{}) {} -func (l *splunkLogger) Tracef(format string, values ...interface{}) {} -func (l *splunkLogger) Debugf(format string, values ...interface{}) {} -func (l *splunkLogger) Infof(format string, values ...interface{}) {} -func (l *splunkLogger) Warnf(format string, values ...interface{}) {} -func (l *splunkLogger) Errorf(format string, values ...interface{}) {} -func (l *splunkLogger) Criticalf(format string, values ...interface{}) {} -func (l *splunkLogger) Panicf(format string, values ...interface{}) {} -func (l *splunkLogger) Fatalf(format string, values ...interface{}) {} -func (l *splunkLogger) Tracew(msg string, keysAndValues ...interface{}) {} -func (l *splunkLogger) Debugw(msg string, keysAndValues ...interface{}) {} -func (l *splunkLogger) Infow(msg string, keysAndValues ...interface{}) {} -func (l *splunkLogger) Warnw(msg string, keysAndValues ...interface{}) {} -func (l *splunkLogger) Errorw(msg string, keysAndValues ...interface{}) {} -func (l *splunkLogger) Criticalw(msg string, keysAndValues ...interface{}) {} -func (l *splunkLogger) Panicw(msg string, keysAndValues ...interface{}) {} -func (l *splunkLogger) Fatalw(msg string, keysAndValues ...interface{}) {} -func (l *splunkLogger) WarnIf(err error, msg string) {} -func (l *splunkLogger) ErrorIf(err error, msg string) {} -func (l *splunkLogger) PanicIf(err error, msg string) {} -func (l *splunkLogger) ErrorIfClosing(io.Closer, string) {} -func (l *splunkLogger) Sync() error { return nil } -func (l *splunkLogger) Helper(skip int) Logger { return l } -func (l *splunkLogger) Recover(panicErr interface{}) {} +func (l *splunkLogger) Sync() error { + return l.logger.Sync() +} + +func (l *splunkLogger) Helper(add int) Logger { + return &splunkLogger{ + logger: l.logger.Helper(add), + splunkToken: l.splunkToken, + splunkURL: l.splunkURL, + environmentName: l.environmentName, + hostname: l.hostname, + localIP: getLocalIP(), + } +} + +func (l *splunkLogger) Recover(panicErr interface{}) { + l.logger.Recover(panicErr) +} diff --git a/core/logger/zap.go b/core/logger/zap.go index c7bff77d10f..6b80ad5c4ca 100644 --- a/core/logger/zap.go +++ b/core/logger/zap.go @@ -43,7 +43,7 @@ func (l *zapLogger) With(args ...interface{}) Logger { return &newLogger } -func (l *zapLogger) Auditf(eventID string, data map[string]interface{}) { /* STUB */ } +func (l *zapLogger) Audit(eventID string, data map[string]interface{}) { /* STUB */ } // copyFields returns a copy of fields with add appended. func copyFields(fields []interface{}, add ...interface{}) []interface{} { diff --git a/core/sessions/orm.go b/core/sessions/orm.go index f568ea8e718..6168c4ec234 100644 --- a/core/sessions/orm.go +++ b/core/sessions/orm.go @@ -133,12 +133,12 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { // Do email and password check first to prevent extra database look up // for MFA tokens leaking if an account has MFA tokens or not. if !constantTimeEmailCompare(sr.Email, user.Email) { - o.lggr.Auditf(logger.AUTH_LOGIN_FAILED_EMAIL, map[string]interface{}{"email": sr.Email}) + o.lggr.Audit(logger.AUTH_LOGIN_FAILED_EMAIL, map[string]interface{}{"email": sr.Email}) return "", errors.New("Invalid email") } if !utils.CheckPasswordHash(sr.Password, user.HashedPassword) { - o.lggr.Auditf(logger.AUTH_LOGIN_FAILED_PASSWORD, map[string]interface{}{"email": sr.Email}) + o.lggr.Audit(logger.AUTH_LOGIN_FAILED_PASSWORD, map[string]interface{}{"email": sr.Email}) return "", errors.New("Invalid password") } @@ -155,7 +155,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { lggr.Infof("No MFA for user. Creating Session") session := NewSession() _, err = o.db.Exec("INSERT INTO sessions (id, last_used, created_at) VALUES ($1, now(), now())", session.ID) - o.lggr.Auditf(logger.AUTH_LOGIN_SUCCESS_NO_2FA, map[string]interface{}{"email": sr.Email}) + o.lggr.Audit(logger.AUTH_LOGIN_SUCCESS_NO_2FA, map[string]interface{}{"email": sr.Email}) return session.ID, err } @@ -186,7 +186,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { if err != nil { // The user does have WebAuthn enabled but failed the check - o.lggr.Auditf(logger.AUTH_LOGIN_FAILED_2FA, map[string]interface{}{"email": sr.Email, "error": err}) + o.lggr.Audit(logger.AUTH_LOGIN_FAILED_2FA, map[string]interface{}{"email": sr.Email, "error": err}) lggr.Errorf("User sent an invalid attestation: %v", err) return "", errors.New("MFA Error") } @@ -205,7 +205,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { lggr.Errorf("error in Marshal credentials: %s", err) return "", err } - o.lggr.Auditf(logger.AUTH_LOGIN_SUCCESS_WITH_2FA, map[string]interface{}{"email": sr.Email, "credential": string(uwasj)}) + o.lggr.Audit(logger.AUTH_LOGIN_SUCCESS_WITH_2FA, map[string]interface{}{"email": sr.Email, "credential": string(uwasj)}) return session.ID, nil } diff --git a/core/web/bridge_types_controller.go b/core/web/bridge_types_controller.go index 401f8a8b150..18be716008c 100644 --- a/core/web/bridge_types_controller.go +++ b/core/web/bridge_types_controller.go @@ -97,7 +97,7 @@ func (btc *BridgeTypesController) Create(c *gin.Context) { resource := presenters.NewBridgeResource(*bt) resource.IncomingToken = bta.IncomingToken - btc.App.GetLogger().Auditf(logger.BRIDGE_CREATED, map[string]interface{}{ + btc.App.GetLogger().Audit(logger.BRIDGE_CREATED, map[string]interface{}{ "bridgeName": bta.Name, "bridgeConfirmations": bta.Confirmations, "bridgeMinimumContractPayment": bta.MinimumContractPayment, @@ -178,7 +178,7 @@ func (btc *BridgeTypesController) Update(c *gin.Context) { return } - btc.App.GetLogger().Auditf(logger.BRIDGE_UPDATED, map[string]interface{}{ + btc.App.GetLogger().Audit(logger.BRIDGE_UPDATED, map[string]interface{}{ "bridgeName": bt.Name, "bridgeConfirmations": bt.Confirmations, "bridgeMinimumContractPayment": bt.MinimumContractPayment, @@ -222,7 +222,7 @@ func (btc *BridgeTypesController) Destroy(c *gin.Context) { return } - btc.App.GetLogger().Auditf(logger.BRIDGE_DELETED, map[string]interface{}{"name": name}) + btc.App.GetLogger().Audit(logger.BRIDGE_DELETED, map[string]interface{}{"name": name}) jsonAPIResponse(c, presenters.NewBridgeResource(bt), "bridge") } diff --git a/core/web/chains_controller.go b/core/web/chains_controller.go index ec226a9350b..ebf5e3137b5 100644 --- a/core/web/chains_controller.go +++ b/core/web/chains_controller.go @@ -96,7 +96,7 @@ func (cc *chainsController[I, C, R]) Create(c *gin.Context) { } chainj, _ := json.Marshal(chain) - cc.lggr.Auditf(logger.CHAIN_ADDED, map[string]interface{}{"chain": chainj}) + cc.lggr.Audit(logger.CHAIN_ADDED, map[string]interface{}{"chain": chainj}) jsonAPIResponseWithStatus(c, cc.newResource(chain), cc.resourceName, http.StatusCreated) } @@ -153,7 +153,7 @@ func (cc *chainsController[I, C, R]) Update(c *gin.Context) { } chainj, _ := json.Marshal(chain) - cc.lggr.Auditf(logger.CHAIN_SPEC_UPDATED, map[string]interface{}{"chain": chainj}) + cc.lggr.Audit(logger.CHAIN_SPEC_UPDATED, map[string]interface{}{"chain": chainj}) jsonAPIResponse(c, cc.newResource(chain), cc.resourceName) } @@ -177,7 +177,7 @@ func (cc *chainsController[I, C, R]) Delete(c *gin.Context) { return } - cc.lggr.Auditf(logger.CHAIN_DELETED, map[string]interface{}{"id": id}) + cc.lggr.Audit(logger.CHAIN_DELETED, map[string]interface{}{"id": id}) jsonAPIResponseWithStatus(c, nil, cc.resourceName, http.StatusNoContent) } diff --git a/core/web/config_controller.go b/core/web/config_controller.go index 45bbdd05c26..0a78bc13f78 100644 --- a/core/web/config_controller.go +++ b/core/web/config_controller.go @@ -23,7 +23,7 @@ type ConfigController struct { func (cc *ConfigController) Show(c *gin.Context) { cw := config.NewConfigPrinter(cc.App.GetConfig()) - cc.App.GetLogger().Auditf(logger.ENV_NONCRITICAL_ENV_DUMPED, map[string]interface{}{}) + cc.App.GetLogger().Audit(logger.ENV_NONCRITICAL_ENV_DUMPED, map[string]interface{}{}) jsonAPIResponse(c, cw, "config") } @@ -88,6 +88,6 @@ func (cc *ConfigController) Patch(c *gin.Context) { }, EVMChainID: utils.NewBig(chain.ID()), } - cc.App.GetLogger().Auditf(logger.CONFIG_UPDATED, map[string]interface{}{"configResponse": response}) + cc.App.GetLogger().Audit(logger.CONFIG_UPDATED, map[string]interface{}{"configResponse": response}) jsonAPIResponse(c, response, "config") } diff --git a/core/web/csa_keys_controller.go b/core/web/csa_keys_controller.go index e62a186edc3..202343d2b5e 100644 --- a/core/web/csa_keys_controller.go +++ b/core/web/csa_keys_controller.go @@ -45,7 +45,7 @@ func (ctrl *CSAKeysController) Create(c *gin.Context) { return } - ctrl.App.GetLogger().Auditf(logger.CSA_KEY_CREATED, map[string]interface{}{ + ctrl.App.GetLogger().Audit(logger.CSA_KEY_CREATED, map[string]interface{}{ "CSAPublicKey": key.PublicKey, "CSVersion": key.Version, }) @@ -69,7 +69,7 @@ func (ctrl *CSAKeysController) Import(c *gin.Context) { return } - ctrl.App.GetLogger().Auditf(logger.CSA_KEY_IMPORTED, map[string]interface{}{ + ctrl.App.GetLogger().Audit(logger.CSA_KEY_IMPORTED, map[string]interface{}{ "CSAPublicKey": key.PublicKey, "CSVersion": key.Version, }) @@ -90,6 +90,6 @@ func (ctrl *CSAKeysController) Export(c *gin.Context) { return } - ctrl.App.GetLogger().Auditf(logger.CSA_KEY_EXPORTED, map[string]interface{}{"keyID": keyID}) + ctrl.App.GetLogger().Audit(logger.CSA_KEY_EXPORTED, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/eth_keys_controller.go b/core/web/eth_keys_controller.go index a611a7168c6..c2db6b30b6d 100644 --- a/core/web/eth_keys_controller.go +++ b/core/web/eth_keys_controller.go @@ -132,7 +132,7 @@ func (ekc *ETHKeysController) Create(c *gin.Context) { return } - ekc.App.GetLogger().Auditf(logger.ETH_KEY_CREATED, map[string]interface{}{ + ekc.App.GetLogger().Audit(logger.ETH_KEY_CREATED, map[string]interface{}{ "ethPublicKey": key.Address, "ethID": key.ID(), }) @@ -187,7 +187,7 @@ func (ekc *ETHKeysController) Update(c *gin.Context) { return } - ekc.App.GetLogger().Auditf(logger.ETH_KEY_UPDATED, map[string]interface{}{ + ekc.App.GetLogger().Audit(logger.ETH_KEY_UPDATED, map[string]interface{}{ "ethPublicKey": key.Address, "ethID": key.ID(), }) @@ -243,7 +243,7 @@ func (ekc *ETHKeysController) Delete(c *gin.Context) { return } - ekc.App.GetLogger().Auditf(logger.ETH_KEY_DELETED, map[string]interface{}{"id": keyID}) + ekc.App.GetLogger().Audit(logger.ETH_KEY_DELETED, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, r, "account") } @@ -291,7 +291,7 @@ func (ekc *ETHKeysController) Import(c *gin.Context) { return } - ekc.App.GetLogger().Auditf(logger.ETH_KEY_IMPORTED, map[string]interface{}{ + ekc.App.GetLogger().Audit(logger.ETH_KEY_IMPORTED, map[string]interface{}{ "ethPublicKey": key.Address, "ethID": key.ID(), }) @@ -311,7 +311,7 @@ func (ekc *ETHKeysController) Export(c *gin.Context) { return } - ekc.App.GetLogger().Auditf(logger.ETH_KEY_EXPORTED, map[string]interface{}{"address": address}) + ekc.App.GetLogger().Audit(logger.ETH_KEY_EXPORTED, map[string]interface{}{"address": address}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/evm_forwarders_controller.go b/core/web/evm_forwarders_controller.go index a2d18c8df75..87f5ea3a2cc 100644 --- a/core/web/evm_forwarders_controller.go +++ b/core/web/evm_forwarders_controller.go @@ -59,7 +59,7 @@ func (cc *EVMForwardersController) Create(c *gin.Context) { return } - cc.App.GetLogger().Auditf(logger.FORWARDER_CREATED, map[string]interface{}{ + cc.App.GetLogger().Audit(logger.FORWARDER_CREATED, map[string]interface{}{ "forwarderID": fwd.ID, "forwarderAddress": fwd.Address, "forwarderEVMChainID": fwd.EVMChainID, @@ -83,6 +83,6 @@ func (cc *EVMForwardersController) Delete(c *gin.Context) { return } - cc.App.GetLogger().Auditf(logger.FORWARDER_DELETED, map[string]interface{}{"id": id}) + cc.App.GetLogger().Audit(logger.FORWARDER_DELETED, map[string]interface{}{"id": id}) jsonAPIResponseWithStatus(c, nil, "forwarder", http.StatusNoContent) } diff --git a/core/web/evm_transfer_controller.go b/core/web/evm_transfer_controller.go index 44379b6db7b..2cdc7fb0606 100644 --- a/core/web/evm_transfer_controller.go +++ b/core/web/evm_transfer_controller.go @@ -64,7 +64,7 @@ func (tc *EVMTransfersController) Create(c *gin.Context) { return } - tc.App.GetLogger().Auditf(logger.ETH_TRANSACTION_CREATED, map[string]interface{}{ + tc.App.GetLogger().Audit(logger.ETH_TRANSACTION_CREATED, map[string]interface{}{ "ethTX": etx, }) diff --git a/core/web/external_initiators_controller.go b/core/web/external_initiators_controller.go index c153bc858ec..15611e01773 100644 --- a/core/web/external_initiators_controller.go +++ b/core/web/external_initiators_controller.go @@ -85,7 +85,7 @@ func (eic *ExternalInitiatorsController) Create(c *gin.Context) { return } - eic.App.GetLogger().Auditf(logger.EXTERNAL_INITIATOR_CREATED, map[string]interface{}{ + eic.App.GetLogger().Audit(logger.EXTERNAL_INITIATOR_CREATED, map[string]interface{}{ "externalInitiatorID": ei.ID, "externalInitiatorName": ei.Name, "externalInitiatorURL": ei.URL, @@ -108,6 +108,6 @@ func (eic *ExternalInitiatorsController) Destroy(c *gin.Context) { return } - eic.App.GetLogger().Auditf(logger.EXTERNAL_INITIATOR_DELETED, map[string]interface{}{"name": name}) + eic.App.GetLogger().Audit(logger.EXTERNAL_INITIATOR_DELETED, map[string]interface{}{"name": name}) jsonAPIResponseWithStatus(c, nil, "external initiator", http.StatusNoContent) } diff --git a/core/web/jobs_controller.go b/core/web/jobs_controller.go index e78aab49619..a1b92025c1f 100644 --- a/core/web/jobs_controller.go +++ b/core/web/jobs_controller.go @@ -159,7 +159,7 @@ func (jc *JobsController) Create(c *gin.Context) { } jbj, _ := json.Marshal(jb) - jc.App.GetLogger().Auditf(logger.JOB_CREATED, map[string]interface{}{"job": string(jbj)}) + jc.App.GetLogger().Audit(logger.JOB_CREATED, map[string]interface{}{"job": string(jbj)}) jsonAPIResponse(c, presenters.NewJobResource(jb), jb.Type.String()) } @@ -186,6 +186,6 @@ func (jc *JobsController) Delete(c *gin.Context) { return } - jc.App.GetLogger().Auditf(logger.JOB_DELETED, map[string]interface{}{"id": j.ID}) + jc.App.GetLogger().Audit(logger.JOB_DELETED, map[string]interface{}{"id": j.ID}) jsonAPIResponseWithStatus(c, nil, "job", http.StatusNoContent) } diff --git a/core/web/keys_controller.go b/core/web/keys_controller.go index e9f74212f14..4a55529ff77 100644 --- a/core/web/keys_controller.go +++ b/core/web/keys_controller.go @@ -74,12 +74,12 @@ func (kc *keysController[K, R]) Create(c *gin.Context) { // Emit audit log, determine if Terra or Solana key switch unwrappedKey := any(key).(type) { case terrakey.Key: - kc.lggr.Auditf(logger.TERRA_KEY_CREATED, map[string]interface{}{ + kc.lggr.Audit(logger.TERRA_KEY_CREATED, map[string]interface{}{ "terraPublicKey": unwrappedKey.PublicKey(), "terraID": unwrappedKey.ID(), }) case solkey.Key: - kc.lggr.Auditf(logger.SOLANA_KEY_CREATED, map[string]interface{}{ + kc.lggr.Audit(logger.SOLANA_KEY_CREATED, map[string]interface{}{ "solanaPublicKey": unwrappedKey.PublicKey(), "solanaID": unwrappedKey.ID(), }) @@ -104,9 +104,9 @@ func (kc *keysController[K, R]) Delete(c *gin.Context) { // Emit audit log, determine if Terra or Solana key switch any(key).(type) { case terrakey.Key: - kc.lggr.Auditf(logger.TERRA_KEY_DELETED, map[string]interface{}{"terraID": keyID}) + kc.lggr.Audit(logger.TERRA_KEY_DELETED, map[string]interface{}{"terraID": keyID}) case solkey.Key: - kc.lggr.Auditf(logger.SOLANA_KEY_DELETED, map[string]interface{}{"solanaID": keyID}) + kc.lggr.Audit(logger.SOLANA_KEY_DELETED, map[string]interface{}{"solanaID": keyID}) } jsonAPIResponse(c, kc.newResource(key), kc.resourceName) @@ -130,12 +130,12 @@ func (kc *keysController[K, R]) Import(c *gin.Context) { // Emit audit log, determine if Terra or Solana key switch unwrappedKey := any(key).(type) { case terrakey.Key: - kc.lggr.Auditf(logger.TERRA_KEY_IMPORTED, map[string]interface{}{ + kc.lggr.Audit(logger.TERRA_KEY_IMPORTED, map[string]interface{}{ "terraPublicKey": unwrappedKey.PublicKey(), "terraID": unwrappedKey.ID(), }) case solkey.Key: - kc.lggr.Auditf(logger.SOLANA_KEY_IMPORTED, map[string]interface{}{ + kc.lggr.Audit(logger.SOLANA_KEY_IMPORTED, map[string]interface{}{ "solanaPublicKey": unwrappedKey.PublicKey(), "solanaID": unwrappedKey.ID(), }) @@ -156,9 +156,9 @@ func (kc *keysController[K, R]) Export(c *gin.Context) { } if strings.HasPrefix(c.Request.URL.Path, "/v2/keys/terra") { - kc.lggr.Auditf(logger.TERRA_KEY_EXPORTED, map[string]interface{}{"terraID": keyID}) + kc.lggr.Audit(logger.TERRA_KEY_EXPORTED, map[string]interface{}{"terraID": keyID}) } else if strings.HasPrefix(c.Request.URL.Path, "/v2/keys/solana") { - kc.lggr.Auditf(logger.SOLANA_KEY_EXPORTED, map[string]interface{}{"solanaID": keyID}) + kc.lggr.Audit(logger.SOLANA_KEY_EXPORTED, map[string]interface{}{"solanaID": keyID}) } c.Data(http.StatusOK, MediaType, bytes) diff --git a/core/web/log_controller.go b/core/web/log_controller.go index 12a2c98d213..883c8ef13ec 100644 --- a/core/web/log_controller.go +++ b/core/web/log_controller.go @@ -91,13 +91,13 @@ func (cc *LogController) Patch(c *gin.Context) { LogLevel: lvls, } - cc.App.GetLogger().Auditf(logger.GLOBAL_LOG_LEVEL_SET, map[string]interface{}{"logLevel": request.Level}) + cc.App.GetLogger().Audit(logger.GLOBAL_LOG_LEVEL_SET, map[string]interface{}{"logLevel": request.Level}) if request.Level == "debug" { if request.SqlEnabled != nil && *request.SqlEnabled { - cc.App.GetLogger().Auditf(logger.CONFIG_SQL_LOGGING_ENABLED, map[string]interface{}{}) + cc.App.GetLogger().Audit(logger.CONFIG_SQL_LOGGING_ENABLED, map[string]interface{}{}) } else { - cc.App.GetLogger().Auditf(logger.CONFIG_SQL_LOGGING_DISABLED, map[string]interface{}{}) + cc.App.GetLogger().Audit(logger.CONFIG_SQL_LOGGING_DISABLED, map[string]interface{}{}) } } diff --git a/core/web/nodes_controller.go b/core/web/nodes_controller.go index f888bbb148b..a292618b4ee 100644 --- a/core/web/nodes_controller.go +++ b/core/web/nodes_controller.go @@ -99,7 +99,7 @@ func (n *nodesController[I, N, R]) Create(c *gin.Context) { return } - n.lggr.Auditf(logger.CHAIN_RPC_NODE_ADDED, map[string]interface{}{}) + n.lggr.Audit(logger.CHAIN_RPC_NODE_ADDED, map[string]interface{}{}) jsonAPIResponse(c, n.newResource(node), "node") } @@ -123,7 +123,7 @@ func (n *nodesController[I, N, R]) Delete(c *gin.Context) { return } - n.lggr.Auditf(logger.CHAIN_DELETED, map[string]interface{}{"id": id}) + n.lggr.Audit(logger.CHAIN_DELETED, map[string]interface{}{"id": id}) jsonAPIResponseWithStatus(c, nil, "node", http.StatusNoContent) } diff --git a/core/web/ocr2_keys_controller.go b/core/web/ocr2_keys_controller.go index 21a336ca771..c9e34f4d0b4 100644 --- a/core/web/ocr2_keys_controller.go +++ b/core/web/ocr2_keys_controller.go @@ -44,7 +44,7 @@ func (ocr2kc *OCR2KeysController) Create(c *gin.Context) { return } - ocr2kc.App.GetLogger().Auditf(logger.OCR2_KEY_BUNDLE_CREATED, map[string]interface{}{ + ocr2kc.App.GetLogger().Audit(logger.OCR2_KEY_BUNDLE_CREATED, map[string]interface{}{ "ocr2KeyID": key.ID(), "ocr2KeyChainType": key.ChainType(), "ocr2KeyConfigEncryptionPublicKey": key.ConfigEncryptionPublicKey(), @@ -71,7 +71,7 @@ func (ocr2kc *OCR2KeysController) Delete(c *gin.Context) { return } - ocr2kc.App.GetLogger().Auditf(logger.OCR2_KEY_BUNDLE_DELETED, map[string]interface{}{"id": id}) + ocr2kc.App.GetLogger().Audit(logger.OCR2_KEY_BUNDLE_DELETED, map[string]interface{}{"id": id}) jsonAPIResponse(c, presenters.NewOCR2KeysBundleResource(key), "offChainReporting2KeyBundle") } @@ -93,7 +93,7 @@ func (ocr2kc *OCR2KeysController) Import(c *gin.Context) { return } - ocr2kc.App.GetLogger().Auditf(logger.OCR2_KEY_BUNDLE_IMPORTED, map[string]interface{}{ + ocr2kc.App.GetLogger().Audit(logger.OCR2_KEY_BUNDLE_IMPORTED, map[string]interface{}{ "ocr2KeyID": keyBundle.ID(), "ocr2KeyChainType": keyBundle.ChainType(), "ocr2KeyConfigEncryptionPublicKey": keyBundle.ConfigEncryptionPublicKey(), @@ -119,6 +119,6 @@ func (ocr2kc *OCR2KeysController) Export(c *gin.Context) { return } - ocr2kc.App.GetLogger().Auditf(logger.OCR2_KEY_BUNDLE_EXPORTED, map[string]interface{}{"keyID": stringID}) + ocr2kc.App.GetLogger().Audit(logger.OCR2_KEY_BUNDLE_EXPORTED, map[string]interface{}{"keyID": stringID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/ocr_keys_controller.go b/core/web/ocr_keys_controller.go index ea19c229085..285b27276f5 100644 --- a/core/web/ocr_keys_controller.go +++ b/core/web/ocr_keys_controller.go @@ -37,7 +37,7 @@ func (ocrkc *OCRKeysController) Create(c *gin.Context) { return } - ocrkc.App.GetLogger().Auditf(logger.OCR_KEY_BUNDLE_CREATED, map[string]interface{}{ + ocrkc.App.GetLogger().Audit(logger.OCR_KEY_BUNDLE_CREATED, map[string]interface{}{ "ocrKeyBundleID": key.ID(), "ocrKeyBundlePublicKeyAddressOnChain": key.PublicKeyAddressOnChain(), }) @@ -61,7 +61,7 @@ func (ocrkc *OCRKeysController) Delete(c *gin.Context) { return } - ocrkc.App.GetLogger().Auditf(logger.OCR_KEY_BUNDLE_DELETED, map[string]interface{}{"id": id}) + ocrkc.App.GetLogger().Audit(logger.OCR_KEY_BUNDLE_DELETED, map[string]interface{}{"id": id}) jsonAPIResponse(c, presenters.NewOCRKeysBundleResource(key), "offChainReportingKeyBundle") } @@ -83,7 +83,7 @@ func (ocrkc *OCRKeysController) Import(c *gin.Context) { return } - ocrkc.App.GetLogger().Auditf(logger.OCR_KEY_BUNDLE_IMPORTED, map[string]interface{}{ + ocrkc.App.GetLogger().Audit(logger.OCR_KEY_BUNDLE_IMPORTED, map[string]interface{}{ "OCRID": encryptedOCRKeyBundle.GetID(), "OCRPublicKeyAddressOnChain": encryptedOCRKeyBundle.PublicKeyAddressOnChain(), "OCRPublicKeyOffChain": encryptedOCRKeyBundle.PublicKeyOffChain(), @@ -106,6 +106,6 @@ func (ocrkc *OCRKeysController) Export(c *gin.Context) { return } - ocrkc.App.GetLogger().Auditf(logger.OCR_KEY_BUNDLE_EXPORTED, map[string]interface{}{"keyID": stringID}) + ocrkc.App.GetLogger().Audit(logger.OCR_KEY_BUNDLE_EXPORTED, map[string]interface{}{"keyID": stringID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/p2p_keys_controller.go b/core/web/p2p_keys_controller.go index 5c3b3304035..8a9fa2649eb 100644 --- a/core/web/p2p_keys_controller.go +++ b/core/web/p2p_keys_controller.go @@ -39,7 +39,7 @@ func (p2pkc *P2PKeysController) Create(c *gin.Context) { return } - p2pkc.App.GetLogger().Auditf(logger.P2P_KEY_CREATED, map[string]interface{}{ + p2pkc.App.GetLogger().Audit(logger.P2P_KEY_CREATED, map[string]interface{}{ "p2pPublicKey": key.PublicKeyHex(), "p2pID": key.ID(), "p2pPeerID": key.PeerID(), @@ -69,7 +69,7 @@ func (p2pkc *P2PKeysController) Delete(c *gin.Context) { return } - p2pkc.App.GetLogger().Auditf(logger.P2P_KEY_DELETED, map[string]interface{}{"id": keyID}) + p2pkc.App.GetLogger().Audit(logger.P2P_KEY_DELETED, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, presenters.NewP2PKeyResource(key), "p2pKey") } @@ -91,7 +91,7 @@ func (p2pkc *P2PKeysController) Import(c *gin.Context) { return } - p2pkc.App.GetLogger().Auditf(logger.P2P_KEY_IMPORTED, map[string]interface{}{ + p2pkc.App.GetLogger().Audit(logger.P2P_KEY_IMPORTED, map[string]interface{}{ "p2pPublicKey": key.PublicKeyHex(), "p2pID": key.ID(), "p2pPeerID": key.PeerID(), @@ -119,6 +119,6 @@ func (p2pkc *P2PKeysController) Export(c *gin.Context) { return } - p2pkc.App.GetLogger().Auditf(logger.P2P_KEY_EXPORTED, map[string]interface{}{"keyID": keyID}) + p2pkc.App.GetLogger().Audit(logger.P2P_KEY_EXPORTED, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/pipeline_job_spec_errors_controller.go b/core/web/pipeline_job_spec_errors_controller.go index e2b1be5d7f4..c8b1a286ad6 100644 --- a/core/web/pipeline_job_spec_errors_controller.go +++ b/core/web/pipeline_job_spec_errors_controller.go @@ -38,6 +38,6 @@ func (psec *PipelineJobSpecErrorsController) Destroy(c *gin.Context) { return } - psec.App.GetLogger().Auditf(logger.JOB_ERROR_DISMISSED, map[string]interface{}{"id": jobSpec.ID}) + psec.App.GetLogger().Audit(logger.JOB_ERROR_DISMISSED, map[string]interface{}{"id": jobSpec.ID}) jsonAPIResponseWithStatus(c, nil, "job", http.StatusNoContent) } diff --git a/core/web/pipeline_runs_controller.go b/core/web/pipeline_runs_controller.go index 724041bf826..417a6d88bd5 100644 --- a/core/web/pipeline_runs_controller.go +++ b/core/web/pipeline_runs_controller.go @@ -180,6 +180,6 @@ func (prc *PipelineRunsController) Resume(c *gin.Context) { return } - prc.App.GetLogger().Auditf(logger.UNAUTHED_RUN_RESUMED, map[string]interface{}{"runID": c.Param("runID")}) + prc.App.GetLogger().Audit(logger.UNAUTHED_RUN_RESUMED, map[string]interface{}{"runID": c.Param("runID")}) c.Status(http.StatusOK) } diff --git a/core/web/resolver/mutation.go b/core/web/resolver/mutation.go index c34031aebb0..bd465401495 100644 --- a/core/web/resolver/mutation.go +++ b/core/web/resolver/mutation.go @@ -1,7 +1,5 @@ package resolver -// TODO: Andrew backup and revert this whole file - import ( "context" "database/sql" @@ -98,7 +96,7 @@ func (r *Resolver) CreateBridge(ctx context.Context, args struct{ Input createBr return nil, err } - r.App.GetLogger().Auditf(logger.BRIDGE_CREATED, map[string]interface{}{ + r.App.GetLogger().Audit(logger.BRIDGE_CREATED, map[string]interface{}{ "bridgeName": bta.Name, "bridgeConfirmations": bta.Confirmations, "bridgeMinimumContractPayment": bta.MinimumContractPayment, @@ -122,7 +120,7 @@ func (r *Resolver) CreateCSAKey(ctx context.Context) (*CreateCSAKeyPayloadResolv return nil, err } - r.App.GetLogger().Auditf(logger.CSA_KEY_CREATED, map[string]interface{}{ + r.App.GetLogger().Audit(logger.CSA_KEY_CREATED, map[string]interface{}{ "CSAPublicKey": key.PublicKey, "CSVersion": key.Version, }) @@ -146,7 +144,7 @@ func (r *Resolver) DeleteCSAKey(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.CSA_KEY_DELETED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(logger.CSA_KEY_DELETED, map[string]interface{}{"id": args.ID}) return NewDeleteCSAKeyPayload(key, nil), nil } @@ -239,7 +237,7 @@ func (r *Resolver) CreateFeedsManagerChainConfig(ctx context.Context, args struc } fmj, _ := json.Marshal(ccfg) - r.App.GetLogger().Auditf(logger.FEEDS_MAN_CHAIN_CONFIG_CREATED, map[string]interface{}{"feedsManager": fmj}) + r.App.GetLogger().Audit(logger.FEEDS_MAN_CHAIN_CONFIG_CREATED, map[string]interface{}{"feedsManager": fmj}) return NewCreateFeedsManagerChainConfigPayload(ccfg, nil, nil), nil } @@ -275,7 +273,7 @@ func (r *Resolver) DeleteFeedsManagerChainConfig(ctx context.Context, args struc return nil, err } - r.App.GetLogger().Auditf(logger.FEEDS_MAN_CHAIN_CONFIG_DELETED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(logger.FEEDS_MAN_CHAIN_CONFIG_DELETED, map[string]interface{}{"id": args.ID}) return NewDeleteFeedsManagerChainConfigPayload(ccfg, nil), nil } @@ -359,7 +357,7 @@ func (r *Resolver) UpdateFeedsManagerChainConfig(ctx context.Context, args struc } fmj, _ := json.Marshal(ccfg) - r.App.GetLogger().Auditf(logger.FEEDS_MAN_CHAIN_CONFIG_UPDATED, map[string]interface{}{"feedsManager": fmj}) + r.App.GetLogger().Audit(logger.FEEDS_MAN_CHAIN_CONFIG_UPDATED, map[string]interface{}{"feedsManager": fmj}) return NewUpdateFeedsManagerChainConfigPayload(ccfg, nil, nil), nil } @@ -410,7 +408,7 @@ func (r *Resolver) CreateFeedsManager(ctx context.Context, args struct { } mgrj, _ := json.Marshal(mgr) - r.App.GetLogger().Auditf(logger.FEEDS_MAN_CREATED, map[string]interface{}{"mgrj": mgrj}) + r.App.GetLogger().Audit(logger.FEEDS_MAN_CREATED, map[string]interface{}{"mgrj": mgrj}) return NewCreateFeedsManagerPayload(mgr, nil, nil), nil } @@ -474,7 +472,7 @@ func (r *Resolver) UpdateBridge(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.BRIDGE_UPDATED, map[string]interface{}{ + r.App.GetLogger().Audit(logger.BRIDGE_UPDATED, map[string]interface{}{ "bridgeName": bridge.Name, "bridgeConfirmations": bridge.Confirmations, "bridgeMinimumContractPayment": bridge.MinimumContractPayment, @@ -533,7 +531,7 @@ func (r *Resolver) UpdateFeedsManager(ctx context.Context, args struct { } mgrj, _ := json.Marshal(mgr) - r.App.GetLogger().Auditf(logger.FEEDS_MAN_UPDATED, map[string]interface{}{"mgrj": mgrj}) + r.App.GetLogger().Audit(logger.FEEDS_MAN_UPDATED, map[string]interface{}{"mgrj": mgrj}) return NewUpdateFeedsManagerPayload(mgr, nil, nil), nil } @@ -548,7 +546,7 @@ func (r *Resolver) CreateOCRKeyBundle(ctx context.Context) (*CreateOCRKeyBundleP return nil, err } - r.App.GetLogger().Auditf(logger.OCR_KEY_BUNDLE_CREATED, map[string]interface{}{ + r.App.GetLogger().Audit(logger.OCR_KEY_BUNDLE_CREATED, map[string]interface{}{ "ocrKeyBundleID": key.ID(), "ocrKeyBundlePublicKeyAddressOnChain": key.PublicKeyAddressOnChain(), }) @@ -571,7 +569,7 @@ func (r *Resolver) DeleteOCRKeyBundle(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.OCR_KEY_BUNDLE_DELETED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(logger.OCR_KEY_BUNDLE_DELETED, map[string]interface{}{"id": args.ID}) return NewDeleteOCRKeyBundlePayloadResolver(deletedKey, nil), nil } @@ -595,7 +593,7 @@ func (r *Resolver) CreateNode(ctx context.Context, args struct { wsURL, _ := url.Parse(args.Input.WSURL.String) // Forward only RPC host to logs httpURL, _ := url.Parse(args.Input.HTTPURL.String) - r.App.GetLogger().Auditf(logger.CHAIN_RPC_NODE_ADDED, map[string]interface{}{ + r.App.GetLogger().Audit(logger.CHAIN_RPC_NODE_ADDED, map[string]interface{}{ "chainNodeName": args.Input.Name, "chainNodeEvmChainID": args.Input.EVMChainID, "chainNodeRPCWebSocketHost": wsURL.Host, @@ -639,7 +637,7 @@ func (r *Resolver) DeleteNode(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.CHAIN_RPC_NODE_DELETED, map[string]interface{}{"id": id}) + r.App.GetLogger().Audit(logger.CHAIN_RPC_NODE_DELETED, map[string]interface{}{"id": id}) return NewDeleteNodePayloadResolver(&node, nil), nil } @@ -677,7 +675,7 @@ func (r *Resolver) DeleteBridge(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.BRIDGE_DELETED, map[string]interface{}{"name": bt.Name}) + r.App.GetLogger().Audit(logger.BRIDGE_DELETED, map[string]interface{}{"name": bt.Name}) return NewDeleteBridgePayload(&bt, nil), nil } @@ -691,7 +689,7 @@ func (r *Resolver) CreateP2PKey(ctx context.Context) (*CreateP2PKeyPayloadResolv return nil, err } - r.App.GetLogger().Auditf(logger.P2P_KEY_CREATED, map[string]interface{}{ + r.App.GetLogger().Audit(logger.P2P_KEY_CREATED, map[string]interface{}{ "p2pPublicKey": key.PublicKeyHex(), "p2pID": key.ID(), "p2pPeerID": key.PeerID(), @@ -721,7 +719,7 @@ func (r *Resolver) DeleteP2PKey(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.P2P_KEY_DELETED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(logger.P2P_KEY_DELETED, map[string]interface{}{"id": args.ID}) return NewDeleteP2PKeyPayload(key, nil), nil } @@ -735,7 +733,7 @@ func (r *Resolver) CreateVRFKey(ctx context.Context) (*CreateVRFKeyPayloadResolv return nil, err } - r.App.GetLogger().Auditf(logger.VRF_KEY_CREATED, map[string]interface{}{ + r.App.GetLogger().Audit(logger.VRF_KEY_CREATED, map[string]interface{}{ "vrfPublicKey": key.PublicKey, "vrfID": key.ID(), "vrfPublicKeyAddress": key.PublicKey.Address(), @@ -759,7 +757,7 @@ func (r *Resolver) DeleteVRFKey(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.VRF_KEY_DELETED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(logger.VRF_KEY_DELETED, map[string]interface{}{"id": args.ID}) return NewDeleteVRFKeyPayloadResolver(key, nil), nil } @@ -798,7 +796,7 @@ func (r *Resolver) ApproveJobProposalSpec(ctx context.Context, args struct { } specj, _ := json.Marshal(spec) - r.App.GetLogger().Auditf(logger.JOB_PROPOSAL_SPEC_APPROVED, map[string]interface{}{"spec": specj}) + r.App.GetLogger().Audit(logger.JOB_PROPOSAL_SPEC_APPROVED, map[string]interface{}{"spec": specj}) return NewApproveJobProposalSpecPayload(spec, err), nil } @@ -833,7 +831,7 @@ func (r *Resolver) CancelJobProposalSpec(ctx context.Context, args struct { } specj, _ := json.Marshal(spec) - r.App.GetLogger().Auditf(logger.JOB_PROPOSAL_SPEC_CANCELED, map[string]interface{}{"spec": specj}) + r.App.GetLogger().Audit(logger.JOB_PROPOSAL_SPEC_CANCELED, map[string]interface{}{"spec": specj}) return NewCancelJobProposalSpecPayload(spec, err), nil } @@ -868,7 +866,7 @@ func (r *Resolver) RejectJobProposalSpec(ctx context.Context, args struct { } specj, _ := json.Marshal(spec) - r.App.GetLogger().Auditf(logger.JOB_PROPOSAL_SPEC_REJECTED, map[string]interface{}{"spec": specj}) + r.App.GetLogger().Audit(logger.JOB_PROPOSAL_SPEC_REJECTED, map[string]interface{}{"spec": specj}) return NewRejectJobProposalSpecPayload(spec, err), nil } @@ -906,7 +904,7 @@ func (r *Resolver) UpdateJobProposalSpecDefinition(ctx context.Context, args str } specj, _ := json.Marshal(spec) - r.App.GetLogger().Auditf(logger.JOB_PROPOSAL_SPEC_UPDATED, map[string]interface{}{"spec": specj}) + r.App.GetLogger().Audit(logger.JOB_PROPOSAL_SPEC_UPDATED, map[string]interface{}{"spec": specj}) return NewUpdateJobProposalSpecDefinitionPayload(spec, err), nil } @@ -929,7 +927,7 @@ func (r *Resolver) UpdateUserPassword(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.OldPassword, dbUser.HashedPassword) { - r.App.GetLogger().Auditf(logger.PASSWORD_RESET_ATTEMPT_FAILED_MISMATCH, map[string]interface{}{"user": dbUser.Email}) + r.App.GetLogger().Audit(logger.PASSWORD_RESET_ATTEMPT_FAILED_MISMATCH, map[string]interface{}{"user": dbUser.Email}) return NewUpdatePasswordPayload(nil, map[string]string{ "oldPassword": "old password does not match", @@ -945,7 +943,7 @@ func (r *Resolver) UpdateUserPassword(ctx context.Context, args struct { return nil, failedPasswordUpdateError{} } - r.App.GetLogger().Auditf(logger.PASSWORD_RESET_SUCCESS, map[string]interface{}{"user": dbUser.Email}) + r.App.GetLogger().Audit(logger.PASSWORD_RESET_SUCCESS, map[string]interface{}{"user": dbUser.Email}) return NewUpdatePasswordPayload(session.User, nil), nil } @@ -959,9 +957,9 @@ func (r *Resolver) SetSQLLogging(ctx context.Context, args struct { r.App.GetConfig().SetLogSQL(args.Input.Enabled) if args.Input.Enabled { - r.App.GetLogger().Auditf(logger.CONFIG_SQL_LOGGING_ENABLED, map[string]interface{}{}) + r.App.GetLogger().Audit(logger.CONFIG_SQL_LOGGING_ENABLED, map[string]interface{}{}) } else { - r.App.GetLogger().Auditf(logger.CONFIG_SQL_LOGGING_DISABLED, map[string]interface{}{}) + r.App.GetLogger().Audit(logger.CONFIG_SQL_LOGGING_DISABLED, map[string]interface{}{}) } return NewSetSQLLoggingPayload(args.Input.Enabled), nil @@ -980,7 +978,7 @@ func (r *Resolver) CreateAPIToken(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.Password, dbUser.HashedPassword) { - r.App.GetLogger().Auditf(logger.API_TOKEN_CREATE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": dbUser.Email}) + r.App.GetLogger().Audit(logger.API_TOKEN_CREATE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": dbUser.Email}) return NewCreateAPITokenPayload(nil, map[string]string{ "password": "incorrect password", @@ -992,7 +990,7 @@ func (r *Resolver) CreateAPIToken(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.API_TOKEN_CREATED, map[string]interface{}{"user": dbUser.Email}) + r.App.GetLogger().Audit(logger.API_TOKEN_CREATED, map[string]interface{}{"user": dbUser.Email}) return NewCreateAPITokenPayload(newToken, nil), nil } @@ -1009,7 +1007,7 @@ func (r *Resolver) DeleteAPIToken(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.Password, dbUser.HashedPassword) { - r.App.GetLogger().Auditf(logger.API_TOKEN_DELETE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": dbUser.Email}) + r.App.GetLogger().Audit(logger.API_TOKEN_DELETE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": dbUser.Email}) return NewDeleteAPITokenPayload(nil, map[string]string{ "password": "incorrect password", @@ -1021,7 +1019,7 @@ func (r *Resolver) DeleteAPIToken(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.API_TOKEN_DELETED, map[string]interface{}{"user": dbUser.Email}) + r.App.GetLogger().Audit(logger.API_TOKEN_DELETED, map[string]interface{}{"user": dbUser.Email}) return NewDeleteAPITokenPayload(&auth.Token{ AccessKey: dbUser.TokenKey.String, @@ -1073,7 +1071,7 @@ func (r *Resolver) CreateChain(ctx context.Context, args struct { } chainj, _ := json.Marshal(chain) - r.App.GetLogger().Auditf(logger.CHAIN_ADDED, map[string]interface{}{"chain": chainj}) + r.App.GetLogger().Audit(logger.CHAIN_ADDED, map[string]interface{}{"chain": chainj}) return NewCreateChainPayload(&chain, nil), nil } @@ -1128,7 +1126,7 @@ func (r *Resolver) UpdateChain(ctx context.Context, args struct { } chainj, _ := json.Marshal(chain) - r.App.GetLogger().Auditf(logger.CHAIN_SPEC_UPDATED, map[string]interface{}{"chainj": chainj}) + r.App.GetLogger().Audit(logger.CHAIN_SPEC_UPDATED, map[string]interface{}{"chainj": chainj}) return NewUpdateChainPayload(&chain, nil, nil), nil } @@ -1160,7 +1158,7 @@ func (r *Resolver) DeleteChain(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.CHAIN_DELETED, map[string]interface{}{"id": id}) + r.App.GetLogger().Audit(logger.CHAIN_DELETED, map[string]interface{}{"id": id}) return NewDeleteChainPayload(&chain, nil), nil } @@ -1227,7 +1225,7 @@ func (r *Resolver) CreateJob(ctx context.Context, args struct { } jbj, _ := json.Marshal(jb) - r.App.GetLogger().Auditf(logger.JOB_CREATED, map[string]interface{}{"job": string(jbj)}) + r.App.GetLogger().Audit(logger.JOB_CREATED, map[string]interface{}{"job": string(jbj)}) return NewCreateJobPayload(r.App, &jb, nil), nil } @@ -1262,7 +1260,7 @@ func (r *Resolver) DeleteJob(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.JOB_DELETED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(logger.JOB_DELETED, map[string]interface{}{"id": args.ID}) return NewDeleteJobPayload(r.App, &j, nil), nil } @@ -1296,7 +1294,7 @@ func (r *Resolver) DismissJobError(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.JOB_ERROR_DISMISSED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(logger.JOB_ERROR_DISMISSED, map[string]interface{}{"id": args.ID}) return NewDismissJobErrorPayload(&specErr, nil), nil } @@ -1326,7 +1324,7 @@ func (r *Resolver) RunJob(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.JOB_RUN_SET, map[string]interface{}{"jobID": args.ID, "jobRunID": jobRunID, "planRunID": plnRun}) + r.App.GetLogger().Audit(logger.JOB_RUN_SET, map[string]interface{}{"jobID": args.ID, "jobRunID": jobRunID, "planRunID": plnRun}) return NewRunJobPayload(&plnRun, r.App, nil), nil } @@ -1351,7 +1349,7 @@ func (r *Resolver) SetGlobalLogLevel(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.GLOBAL_LOG_LEVEL_SET, map[string]interface{}{"logLevel": args.Level}) + r.App.GetLogger().Audit(logger.GLOBAL_LOG_LEVEL_SET, map[string]interface{}{"logLevel": args.Level}) return NewSetGlobalLogLevelPayload(args.Level, nil), nil } @@ -1370,7 +1368,7 @@ func (r *Resolver) CreateOCR2KeyBundle(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.OCR2_KEY_BUNDLE_CREATED, map[string]interface{}{ + r.App.GetLogger().Audit(logger.OCR2_KEY_BUNDLE_CREATED, map[string]interface{}{ "ocrKeyID": key.ID(), "ocrKeyChainType": key.ChainType(), "ocrKeyConfigEncryptionPublicKey": key.ConfigEncryptionPublicKey(), @@ -1401,6 +1399,6 @@ func (r *Resolver) DeleteOCR2KeyBundle(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Auditf(logger.OCR2_KEY_BUNDLE_DELETED, map[string]interface{}{"id": id}) + r.App.GetLogger().Audit(logger.OCR2_KEY_BUNDLE_DELETED, map[string]interface{}{"id": id}) return NewDeleteOCR2KeyBundlePayloadResolver(&key, nil), nil } diff --git a/core/web/sessions_controller.go b/core/web/sessions_controller.go index 3b8915fffd6..6b9f9098918 100644 --- a/core/web/sessions_controller.go +++ b/core/web/sessions_controller.go @@ -84,7 +84,7 @@ func (sc *SessionsController) Destroy(c *gin.Context) { return } - sc.App.GetLogger().Auditf(logger.AUTH_SESSION_DELETED, map[string]interface{}{"removedSessionID": sessionID}) + sc.App.GetLogger().Audit(logger.AUTH_SESSION_DELETED, map[string]interface{}{"removedSessionID": sessionID}) jsonAPIResponse(c, Session{Authenticated: false}, "session") } diff --git a/core/web/solana_transfer_controller.go b/core/web/solana_transfer_controller.go index c8c03213d2a..eefac735135 100644 --- a/core/web/solana_transfer_controller.go +++ b/core/web/solana_transfer_controller.go @@ -111,7 +111,7 @@ func (tc *SolanaTransfersController) Create(c *gin.Context) { resource.From = tr.From.String() resource.To = tr.To.String() - tc.App.GetLogger().Auditf(logger.SOLANA_TRANSACTION_CREATED, map[string]interface{}{ + tc.App.GetLogger().Audit(logger.SOLANA_TRANSACTION_CREATED, map[string]interface{}{ "solanaTransactionResource": resource, }) jsonAPIResponse(c, resource, "solana_tx") diff --git a/core/web/terra_transfer_controller.go b/core/web/terra_transfer_controller.go index d8ed1329dec..08723117b3d 100644 --- a/core/web/terra_transfer_controller.go +++ b/core/web/terra_transfer_controller.go @@ -111,7 +111,7 @@ func (tc *TerraTransfersController) Create(c *gin.Context) { resource.TxHash = msg.TxHash resource.State = string(msg.State) - tc.App.GetLogger().Auditf(logger.TERRA_TRANSACTION_CREATED, map[string]interface{}{ + tc.App.GetLogger().Audit(logger.TERRA_TRANSACTION_CREATED, map[string]interface{}{ "terraTransactionResource": resource, }) diff --git a/core/web/user_controller.go b/core/web/user_controller.go index 8688ed0ea18..56e477293f8 100644 --- a/core/web/user_controller.go +++ b/core/web/user_controller.go @@ -43,7 +43,7 @@ func (c *UserController) UpdatePassword(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.OldPassword, user.HashedPassword) { - c.App.GetLogger().Auditf(logger.PASSWORD_RESET_ATTEMPT_FAILED_MISMATCH, map[string]interface{}{"user": user.Email}) + c.App.GetLogger().Audit(logger.PASSWORD_RESET_ATTEMPT_FAILED_MISMATCH, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusConflict, errors.New("old password does not match")) return } @@ -52,7 +52,7 @@ func (c *UserController) UpdatePassword(ctx *gin.Context) { return } - c.App.GetLogger().Auditf(logger.PASSWORD_RESET_SUCCESS, map[string]interface{}{"user": user.Email}) + c.App.GetLogger().Audit(logger.PASSWORD_RESET_SUCCESS, map[string]interface{}{"user": user.Email}) jsonAPIResponse(ctx, presenters.NewUserResource(user), "user") } @@ -70,7 +70,7 @@ func (c *UserController) NewAPIToken(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.Password, user.HashedPassword) { - c.App.GetLogger().Auditf(logger.API_TOKEN_CREATE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": user.Email}) + c.App.GetLogger().Audit(logger.API_TOKEN_CREATE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusUnauthorized, errors.New("incorrect password")) return } @@ -80,7 +80,7 @@ func (c *UserController) NewAPIToken(ctx *gin.Context) { return } - c.App.GetLogger().Auditf(logger.API_TOKEN_CREATED, map[string]interface{}{"user": user.Email}) + c.App.GetLogger().Audit(logger.API_TOKEN_CREATED, map[string]interface{}{"user": user.Email}) jsonAPIResponseWithStatus(ctx, newToken, "auth_token", http.StatusCreated) } @@ -98,7 +98,7 @@ func (c *UserController) DeleteAPIToken(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.Password, user.HashedPassword) { - c.App.GetLogger().Auditf(logger.API_TOKEN_DELETE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": user.Email}) + c.App.GetLogger().Audit(logger.API_TOKEN_DELETE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusUnauthorized, errors.New("incorrect password")) return } @@ -107,7 +107,7 @@ func (c *UserController) DeleteAPIToken(ctx *gin.Context) { return } { - c.App.GetLogger().Auditf(logger.API_TOKEN_DELETED, map[string]interface{}{"user": user.Email}) + c.App.GetLogger().Audit(logger.API_TOKEN_DELETED, map[string]interface{}{"user": user.Email}) jsonAPIResponseWithStatus(ctx, nil, "auth_token", http.StatusNoContent) } } diff --git a/core/web/vrf_keys_controller.go b/core/web/vrf_keys_controller.go index 2b1bc094f69..c10ffad9fe6 100644 --- a/core/web/vrf_keys_controller.go +++ b/core/web/vrf_keys_controller.go @@ -38,7 +38,7 @@ func (vrfkc *VRFKeysController) Create(c *gin.Context) { return } - vrfkc.App.GetLogger().Auditf(logger.VRF_KEY_CREATED, map[string]interface{}{ + vrfkc.App.GetLogger().Audit(logger.VRF_KEY_CREATED, map[string]interface{}{ "vrfPublicKey": pk.PublicKey, "vrfID": pk.ID(), "vrfPublicKeyAddress": pk.PublicKey.Address(), @@ -63,7 +63,7 @@ func (vrfkc *VRFKeysController) Delete(c *gin.Context) { return } - vrfkc.App.GetLogger().Auditf(logger.VRF_KEY_DELETED, map[string]interface{}{"id": keyID}) + vrfkc.App.GetLogger().Audit(logger.VRF_KEY_DELETED, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, presenters.NewVRFKeyResource(key, vrfkc.App.GetLogger()), "vrfKey") } @@ -85,7 +85,7 @@ func (vrfkc *VRFKeysController) Import(c *gin.Context) { return } - vrfkc.App.GetLogger().Auditf(logger.VRF_KEY_IMPORTED, map[string]interface{}{ + vrfkc.App.GetLogger().Audit(logger.VRF_KEY_IMPORTED, map[string]interface{}{ "vrfID": key.ID(), "vrfPublicKey": key.PublicKey, }) @@ -107,6 +107,6 @@ func (vrfkc *VRFKeysController) Export(c *gin.Context) { return } - vrfkc.App.GetLogger().Auditf(logger.VRF_KEY_EXPORTED, map[string]interface{}{"keyID": keyID}) + vrfkc.App.GetLogger().Audit(logger.VRF_KEY_EXPORTED, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/webauthn_controller.go b/core/web/webauthn_controller.go index 2409fdff6f7..64241e6c798 100644 --- a/core/web/webauthn_controller.go +++ b/core/web/webauthn_controller.go @@ -94,7 +94,7 @@ func (c *WebAuthnController) FinishRegistration(ctx *gin.Context) { jsonAPIError(ctx, http.StatusBadRequest, errors.New("registration was unsuccessful")) return } - c.App.GetLogger().Auditf(logger.AUTH_2FA_ENROLLED, map[string]interface{}{"email": user.Email, "credential": string(credj)}) + c.App.GetLogger().Audit(logger.AUTH_2FA_ENROLLED, map[string]interface{}{"email": user.Email, "credential": string(credj)}) ctx.String(http.StatusOK, "{}") } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 5a6182a7aac..3dece08615c 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -36,7 +36,7 @@ This will prevent application boot in a future version of Chainlink. - `$(jobRun.blockTransactionsRoot)` : the root of the transaction trie of the block (hash) - `$(jobRun.blockStateRoot)` : the root of the final state trie of the block (hash) - New optional Splunk logger added - - to opt in to receiving audit log events over HEC of actions performed within the node, set the `SPLUNK_URL` and `SPLUNK_TOKEN` environment variables accordingly. `SPLUNK_URL` is in the format of 'https://xxxxx.splunkcloud.com/services/collector' + - To opt in to receiving audit log events over HEC of actions performed within the node, set the `SPLUNK_URL` and `SPLUNK_TOKEN` environment variables accordingly. `SPLUNK_URL` is in the format of 'https://xxxxx.splunkcloud.com/services/collector' - `ethtx` tasks can now be configured to error if the transaction reverts on-chain. You must set `failOnRevert=true` on the task to enable this behavior, like so: `foo [type=ethtx failOnRevert=true ...]` From 44be94844aeb2cb00b6d90906473ba8854d75270 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Fri, 17 Jun 2022 14:55:20 -0700 Subject: [PATCH 05/92] Fix resolver_test harness with GetLogger return test logger in app setup --- core/web/resolver/resolver_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go index b1e56de5d59..a384db731b2 100644 --- a/core/web/resolver/resolver_test.go +++ b/core/web/resolver/resolver_test.go @@ -17,6 +17,7 @@ import ( configMocks "github.com/smartcontractkit/chainlink/core/config/mocks" coremocks "github.com/smartcontractkit/chainlink/core/internal/mocks" "github.com/smartcontractkit/chainlink/core/internal/testutils/evmtest" + "github.com/smartcontractkit/chainlink/core/logger" feedsMocks "github.com/smartcontractkit/chainlink/core/services/feeds/mocks" jobORMMocks "github.com/smartcontractkit/chainlink/core/services/job/mocks" keystoreMocks "github.com/smartcontractkit/chainlink/core/services/keystore/mocks" @@ -84,6 +85,9 @@ func setupFramework(t *testing.T) *gqlTestFramework { ctx = loader.InjectDataloader(context.Background(), app) ) + l := logger.TestLogger(t) + app.On("GetLogger").Return(l).Maybe() + // Setup mocks // Note - If you add a new mock make sure you assert it's expectation below. m := &mocks{ From 67141e65e8e86b543f5258abd88c9875ecad2b2a Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Mon, 20 Jun 2022 03:44:53 -0700 Subject: [PATCH 06/92] Move audit types to package audit, capital camel case audit type constants, and create type for audit strings --- core/logger/audit/audit_types.go | 111 +++++++++++++++++ core/logger/logger.go | 31 ++--- core/logger/logger_mock_test.go | 4 +- core/logger/null_logger.go | 19 +-- core/logger/prometheus.go | 4 +- core/logger/sentry.go | 3 +- core/logger/splunk.go | 114 +----------------- core/logger/zap.go | 3 +- core/sessions/orm.go | 11 +- core/web/bridge_types_controller.go | 8 +- core/web/chains_controller.go | 7 +- core/web/config_controller.go | 6 +- core/web/csa_keys_controller.go | 8 +- core/web/eth_keys_controller.go | 12 +- core/web/evm_forwarders_controller.go | 6 +- core/web/evm_transfer_controller.go | 4 +- core/web/external_initiators_controller.go | 6 +- core/web/jobs_controller.go | 6 +- core/web/keys_controller.go | 33 ++--- core/web/log_controller.go | 8 +- core/web/nodes_controller.go | 5 +- core/web/ocr2_keys_controller.go | 10 +- core/web/ocr_keys_controller.go | 10 +- core/web/p2p_keys_controller.go | 10 +- .../pipeline_job_spec_errors_controller.go | 4 +- core/web/pipeline_runs_controller.go | 4 +- core/web/resolver/mutation.go | 82 ++++++------- core/web/sessions_controller.go | 4 +- core/web/solana_transfer_controller.go | 4 +- core/web/terra_transfer_controller.go | 4 +- core/web/user_controller.go | 14 +-- core/web/vrf_keys_controller.go | 10 +- core/web/webauthn_controller.go | 4 +- 33 files changed, 293 insertions(+), 276 deletions(-) create mode 100644 core/logger/audit/audit_types.go diff --git a/core/logger/audit/audit_types.go b/core/logger/audit/audit_types.go new file mode 100644 index 00000000000..ca581fc4d2a --- /dev/null +++ b/core/logger/audit/audit_types.go @@ -0,0 +1,111 @@ +package audit + +type EventID string + +// Static audit log event type constants +const ( + AuthLoginFailedEmail EventID = "AUTH_LOGIN_FAILED_EMAIL" + AuthLoginFailedPassword EventID = "AUTH_LOGIN_FAILED_PASSWORD" + AuthLoginFailed2FA EventID = "AUTH_LOGIN_FAILED_2FA" + AuthLoginSuccessWith2FA EventID = "AUTH_LOGIN_SUCCESS_WITH_2FA" + AuthLoginSuccessNo2FA EventID = "AUTH_LOGIN_SUCCESS_NO_2FA" + Auth2FAEnrolled EventID = "AUTH_2FA_ENROLLED" + AuthSessionDeleted EventID = "SESSION_DELETED" + + PasswordResetAttemptFailedMismatch EventID = "PASSWORD_RESET_ATTEMPT_FAILED_MISMATCH" + PasswordResetSuccess EventID = "PASSWORD_RESET_SUCCESS" + + APITokenCreateAttemptPasswordMismatch EventID = "API_TOKEN_CREATE_ATTEMPT_PASSWORD_MISMATCH" + APITokenCreated EventID = "API_TOKEN_CREATED" + APITokenDeleteAttemptPasswordMismatch EventID = "API_TOKEN_DELETE_ATTEMPT_PASSWORD_MISMATCH" + APITokenDeleted EventID = "API_TOKEN_DELETED" + + FeedsManCreated EventID = "FEEDS_MAN_CREATED" + FeedsManUpdated EventID = "FEEDS_MAN_UPDATED" + + FeedsManChainConfigCreated EventID = "FEEDS_MAN_CHAIN_CONFIG_CREATED" + FeedsManChainConfigUpdated EventID = "FEEDS_MAN_CHAIN_CONFIG_UPDATED" + FeedsManChainConfigDeleted EventID = "FEEDS_MAN_CHAIN_CONFIG_DELETED" + + CSAKeyCreated EventID = "CSA_KEY_CREATED" + CSAKeyImported EventID = "CSA_KEY_IMPORTED" + CSAKeyExported EventID = "CSA_KEY_EXPORTED" + CSAKeyDeleted EventID = "CSA_KEY_DELETED" + + OCRKeyBundleCreated EventID = "OCR_KEY_BUNDLE_CREATED" + OCRKeyBundleImported EventID = "OCR_KEY_BUNDLE_IMPORTED" + OCRKeyBundleExported EventID = "OCR_KEY_BUNDLE_EXPORTED" + OCRKeyBundleDeleted EventID = "OCR_KEY_BUNDLE_DELETED" + + OCR2KeyBundleCreated EventID = "OCR2_KEY_BUNDLE_CREATED" + OCR2KeyBundleImported EventID = "OCR2_KEY_BUNDLE_IMPORTED" + OCR2KeyBundleExported EventID = "OCR2_KEY_BUNDLE_EXPORTED" + OCR2KeyBundleDeleted EventID = "OCR2_KEY_BUNDLE_DELETED" + + ETHKeyCreated EventID = "ETH_KEY_CREATED" + ETHKeyUpdated EventID = "ETH_KEY_UPDATED" + ETHKeyImported EventID = "ETH_KEY_IMPORTED" + ETHKeyExported EventID = "ETH_KEY_EXPORTED" + ETHKeyDeleted EventID = "ETH_KEY_DELETED" + + P2PKeyCreated EventID = "P2P_KEY_CREATED" + P2PKeyImported EventID = "P2P_KEY_IMPORTED" + P2PKeyExported EventID = "P2P_KEY_EXPORTED" + P2PKeyDeleted EventID = "P2P_KEY_DELETED" + + VRFKeyCreated EventID = "VRF_KEY_CREATED" + VRFKeyImported EventID = "VRF_KEY_IMPORTED" + VRFKeyExported EventID = "VRF_KEY_EXPORTED" + VRFKeyDeleted EventID = "VRF_KEY_DELETED" + + TerraKeyCreated EventID = "TERRA_KEY_CREATED" + TerraKeyImported EventID = "TERRA_KEY_IMPORTED" + TerraKeyExported EventID = "TERRA_KEY_EXPORTED" + TerraKeyDeleted EventID = "TERRA_KEY_DELETED" + + SolanaKeyCreated EventID = "SOLANA_KEY_CREATED" + SolanaKeyImported EventID = "SOLANA_KEY_IMPORTED" + SolanaKeyExported EventID = "SOLANA_KEY_EXPORTED" + SolanaKeyDeleted EventID = "SOLANA_KEY_DELETED" + + EthTransactionCreated EventID = "ETH_TRANSACTION_CREATED" + TerraTransactionCreated EventID = "TERRA_TRANSACTION_CREATED" + SolanaTransactionCreated EventID = "SOLANA_TRANSACTION_CREATED" + + JobCreated EventID = "JOB_CREATED" + JobDeleted EventID = "JOB_DELETED" + + ChainAdded EventID = "CHAIN_ADDED" + ChainSpecUpdated EventID = "CHAIN_SPEC_UPDATED" + ChainDeleted EventID = "CHAIN_DELETED" + + ChainRpcNodeAdded EventID = "CHAIN_RPC_NODE_ADDED" + ChainRpcNodeDeleted EventID = "CHAIN_RPC_NODE_DELETED" + + BridgeCreated EventID = "BRIDGE_CREATED" + BridgeUpdated EventID = "BRIDGE_UPDATED" + BridgeDeleted EventID = "BRIDGE_DELETED" + + ForwarderCreated EventID = "FORWARDER_CREATED" + ForwarderDeleted EventID = "FORWARDER_DELETED" + + ExternalInitiatorCreated EventID = "EXTERNAL_INITIATOR_CREATED" + ExternalInitiatorDeleted EventID = "EXTERNAL_INITIATOR_DELETED" + + JobProposalSpecApproved EventID = "JOB_PROPOSAL_SPEC_APPROVED" + JobProposalSpecUpdated EventID = "JOB_PROPOSAL_SPEC_UPDATED" + JobProposalSpecCanceled EventID = "JOB_PROPOSAL_SPEC_CANCELED" + JobProposalSpecRejected EventID = "JOB_PROPOSAL_SPEC_REJECTED" + + ConfigUpdated EventID = "CONFIG_UPDATED" + ConfigSqlLoggingEnabled EventID = "CONFIG_SQL_LOGGING_ENABLED" + ConfigSqlLoggingDisabled EventID = "CONFIG_SQL_LOGGING_DISABLED" + GlobalLogLevelSet EventID = "GLOBAL_LOG_LEVEL_SET" + + JobErrorDismissed EventID = "JOB_ERROR_DISMISSED" + JobRunSet EventID = "JOB_RUN_SET" + + EnvNoncriticalEnvDumped EventID = "ENV_NONCRITICAL_ENV_DUMPED" + + UnauthedRunResumed EventID = "UNAUTHED_RUN_RESUMED" +) diff --git a/core/logger/logger.go b/core/logger/logger.go index 482a42ac99f..c3e72d2840f 100644 --- a/core/logger/logger.go +++ b/core/logger/logger.go @@ -11,6 +11,7 @@ import ( "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink/core/config/envvar" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/static" "github.com/smartcontractkit/chainlink/core/utils" ) @@ -81,7 +82,7 @@ type Logger interface { // The .Audit function here is specific to the SplunkLogger implementation // It is added to the interface to ensure that audit logs are sent regardless of log level. // All other Logger implementations should continue the pattern of propogating wrapped logger calls - Audit(eventID string, data map[string]interface{}) + Audit(eventID audit.EventID, data map[string]interface{}) Tracef(format string, values ...interface{}) Debugf(format string, values ...interface{}) @@ -120,20 +121,6 @@ type Logger interface { Recover(panicErr interface{}) } -type Config struct { - LogLevel zapcore.Level - Dir string - JsonConsole bool - UnixTS bool - FileMaxSizeMB int - FileMaxAgeDays int - FileMaxBackups int // files - Hostname string - ChainlinkDev bool - SplunkToken string // enables splunk logging if not empty - SplunkURL string -} - // newZapConfigProd returns a new production zap.Config. func newZapConfigProd(jsonConsole bool, unixTS bool) zap.Config { config := newZapConfigBase() @@ -256,6 +243,20 @@ func NewLogger() (Logger, func() error) { return l.Named(verShaNameStatic()), close } +type Config struct { + LogLevel zapcore.Level + Dir string + JsonConsole bool + UnixTS bool + FileMaxSizeMB int + FileMaxAgeDays int + FileMaxBackups int // files + Hostname string + ChainlinkDev bool + SplunkToken string // enables splunk logging if not empty + SplunkURL string +} + // New returns a new Logger with pretty printing to stdout, prometheus counters, and sentry forwarding. // Tests should use TestLogger. func (c *Config) New() (Logger, func() error) { diff --git a/core/logger/logger_mock_test.go b/core/logger/logger_mock_test.go index c0cada32d41..2cf93ade083 100644 --- a/core/logger/logger_mock_test.go +++ b/core/logger/logger_mock_test.go @@ -5,6 +5,8 @@ package logger import ( io "io" + "github.com/smartcontractkit/chainlink/core/logger/audit" + mock "github.com/stretchr/testify/mock" zapcore "go.uber.org/zap/zapcore" ) @@ -15,7 +17,7 @@ type MockLogger struct { } // Audit provides a mock function with given fields: eventID, data -func (_m *MockLogger) Audit(eventID string, data map[string]interface{}) { +func (_m *MockLogger) Audit(eventID audit.EventID, data map[string]interface{}) { _m.Called(eventID, data) } diff --git a/core/logger/null_logger.go b/core/logger/null_logger.go index 5c09f404707..e8b096b96db 100644 --- a/core/logger/null_logger.go +++ b/core/logger/null_logger.go @@ -3,6 +3,7 @@ package logger import ( "io" + "github.com/smartcontractkit/chainlink/core/logger/audit" "go.uber.org/zap/zapcore" ) @@ -15,15 +16,15 @@ func (l *nullLogger) With(args ...interface{}) Logger { return l } func (l *nullLogger) Named(name string) Logger { return l } func (l *nullLogger) SetLogLevel(_ zapcore.Level) {} -func (l *nullLogger) Audit(eventID string, data map[string]interface{}) {} -func (l *nullLogger) Trace(args ...interface{}) {} -func (l *nullLogger) Debug(args ...interface{}) {} -func (l *nullLogger) Info(args ...interface{}) {} -func (l *nullLogger) Warn(args ...interface{}) {} -func (l *nullLogger) Error(args ...interface{}) {} -func (l *nullLogger) Critical(args ...interface{}) {} -func (l *nullLogger) Panic(args ...interface{}) {} -func (l *nullLogger) Fatal(args ...interface{}) {} +func (l *nullLogger) Audit(eventID audit.EventID, data map[string]interface{}) {} +func (l *nullLogger) Trace(args ...interface{}) {} +func (l *nullLogger) Debug(args ...interface{}) {} +func (l *nullLogger) Info(args ...interface{}) {} +func (l *nullLogger) Warn(args ...interface{}) {} +func (l *nullLogger) Error(args ...interface{}) {} +func (l *nullLogger) Critical(args ...interface{}) {} +func (l *nullLogger) Panic(args ...interface{}) {} +func (l *nullLogger) Fatal(args ...interface{}) {} func (l *nullLogger) Tracef(format string, values ...interface{}) {} func (l *nullLogger) Debugf(format string, values ...interface{}) {} diff --git a/core/logger/prometheus.go b/core/logger/prometheus.go index 33e458c12f8..da234d2cdfe 100644 --- a/core/logger/prometheus.go +++ b/core/logger/prometheus.go @@ -4,6 +4,8 @@ import ( "fmt" "io" + "github.com/smartcontractkit/chainlink/core/logger/audit" + "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/zap/zapcore" @@ -86,7 +88,7 @@ func (s *prometheusLogger) SetLogLevel(level zapcore.Level) { s.h.SetLogLevel(level) } -func (s *prometheusLogger) Audit(eventID string, data map[string]interface{}) { +func (s *prometheusLogger) Audit(eventID audit.EventID, data map[string]interface{}) { s.h.Audit(eventID, data) } diff --git a/core/logger/sentry.go b/core/logger/sentry.go index e144a1501a0..4abc1ea05ab 100644 --- a/core/logger/sentry.go +++ b/core/logger/sentry.go @@ -10,6 +10,7 @@ import ( "github.com/getsentry/sentry-go" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/static" ) @@ -87,7 +88,7 @@ func (s *sentryLogger) SetLogLevel(level zapcore.Level) { s.h.SetLogLevel(level) } -func (s *sentryLogger) Audit(eventID string, data map[string]interface{}) { +func (s *sentryLogger) Audit(eventID audit.EventID, data map[string]interface{}) { s.h.Audit(eventID, data) } diff --git a/core/logger/splunk.go b/core/logger/splunk.go index 1715b174102..9c8ae353596 100644 --- a/core/logger/splunk.go +++ b/core/logger/splunk.go @@ -10,115 +10,9 @@ import ( "net/http" "time" - "go.uber.org/zap/zapcore" -) + "github.com/smartcontractkit/chainlink/core/logger/audit" -// Static audit log event type constants -const ( - AUTH_LOGIN_FAILED_EMAIL = "AUTH_LOGIN_FAILED_EMAIL" - AUTH_LOGIN_FAILED_PASSWORD = "AUTH_LOGIN_FAILED_PASSWORD" - AUTH_LOGIN_FAILED_2FA = "AUTH_LOGIN_FAILED_2FA" - AUTH_LOGIN_SUCCESS_WITH_2FA = "AUTH_LOGIN_SUCCESS_WITH_2FA" - AUTH_LOGIN_SUCCESS_NO_2FA = "AUTH_LOGIN_SUCCESS_NO_2FA" - AUTH_2FA_ENROLLED = "AUTH_2FA_ENROLLED" - AUTH_SESSION_DELETED = "SESSION_DELETED" - - PASSWORD_RESET_ATTEMPT_FAILED_MISMATCH = "PASSWORD_RESET_ATTEMPT_FAILED_MISMATCH" - PASSWORD_RESET_SUCCESS = "PASSWORD_RESET_SUCCESS" - - API_TOKEN_CREATE_ATTEMPT_PASSWORD_MISMATCH = "API_TOKEN_CREATE_ATTEMPT_PASSWORD_MISMATCH" - API_TOKEN_CREATED = "API_TOKEN_CREATED" - API_TOKEN_DELETE_ATTEMPT_PASSWORD_MISMATCH = "API_TOKEN_DELETE_ATTEMPT_PASSWORD_MISMATCH" - API_TOKEN_DELETED = "API_TOKEN_DELETED" - - CSA_KEY_CREATED = "CSA_KEY_CREATED" - CSA_KEY_IMPORTED = "CSA_KEY_IMPORTED" - CSA_KEY_EXPORTED = "CSA_KEY_EXPORTED" - CSA_KEY_DELETED = "CSA_KEY_DELETED" - - FEEDS_MAN_CREATED = "FEEDS_MAN_CREATED" - FEEDS_MAN_UPDATED = "FEEDS_MAN_UPDATED" - - FEEDS_MAN_CHAIN_CONFIG_CREATED = "FEEDS_MAN_CHAIN_CONFIG_CREATED" - FEEDS_MAN_CHAIN_CONFIG_UPDATED = "FEEDS_MAN_CHAIN_CONFIG_UPDATED" - FEEDS_MAN_CHAIN_CONFIG_DELETED = "FEEDS_MAN_CHAIN_CONFIG_DELETED" - - OCR_KEY_BUNDLE_CREATED = "OCR_KEY_BUNDLE_CREATED" - OCR_KEY_BUNDLE_IMPORTED = "OCR_KEY_BUNDLE_IMPORTED" - OCR_KEY_BUNDLE_EXPORTED = "OCR_KEY_BUNDLE_EXPORTED" - OCR_KEY_BUNDLE_DELETED = "OCR_KEY_BUNDLE_DELETED" - - OCR2_KEY_BUNDLE_CREATED = "OCR2_KEY_BUNDLE_CREATED" - OCR2_KEY_BUNDLE_IMPORTED = "OCR2_KEY_BUNDLE_IMPORTED" - OCR2_KEY_BUNDLE_EXPORTED = "OCR2_KEY_BUNDLE_EXPORTED" - OCR2_KEY_BUNDLE_DELETED = "OCR2_KEY_BUNDLE_DELETED" - - ETH_KEY_CREATED = "ETH_KEY_CREATED" - ETH_KEY_UPDATED = "ETH_KEY_UPDATED" - ETH_KEY_IMPORTED = "ETH_KEY_IMPORTED" - ETH_KEY_EXPORTED = "ETH_KEY_EXPORTED" - ETH_KEY_DELETED = "ETH_KEY_DELETED" - - P2P_KEY_CREATED = "P2P_KEY_CREATED" - P2P_KEY_IMPORTED = "P2P_KEY_IMPORTED" - P2P_KEY_EXPORTED = "P2P_KEY_EXPORTED" - P2P_KEY_DELETED = "P2P_KEY_DELETED" - - VRF_KEY_CREATED = "VRF_KEY_CREATED" - VRF_KEY_IMPORTED = "VRF_KEY_IMPORTED" - VRF_KEY_EXPORTED = "VRF_KEY_EXPORTED" - VRF_KEY_DELETED = "VRF_KEY_DELETED" - - TERRA_KEY_CREATED = "TERRA_KEY_CREATED" - TERRA_KEY_IMPORTED = "TERRA_KEY_IMPORTED" - TERRA_KEY_EXPORTED = "TERRA_KEY_EXPORTED" - TERRA_KEY_DELETED = "TERRA_KEY_DELETED" - - SOLANA_KEY_CREATED = "SOLANA_KEY_CREATED" - SOLANA_KEY_IMPORTED = "SOLANA_KEY_IMPORTED" - SOLANA_KEY_EXPORTED = "SOLANA_KEY_EXPORTED" - SOLANA_KEY_DELETED = "SOLANA_KEY_DELETED" - - ETH_TRANSACTION_CREATED = "ETH_TRANSACTION_CREATED" - TERRA_TRANSACTION_CREATED = "TERRA_TRANSACTION_CREATED" - SOLANA_TRANSACTION_CREATED = "SOLANA_TRANSACTION_CREATED" - - JOB_CREATED = "JOB_CREATED" - JOB_DELETED = "JOB_DELETED" - - CHAIN_ADDED = "CHAIN_ADDED" - CHAIN_SPEC_UPDATED = "CHAIN_SPEC_UPDATED" - CHAIN_DELETED = "CHAIN_DELETED" - - CHAIN_RPC_NODE_ADDED = "CHAIN_RPC_NODE_ADDED" - CHAIN_RPC_NODE_DELETED = "CHAIN_RPC_NODE_DELETED" - - BRIDGE_CREATED = "BRIDGE_CREATED" - BRIDGE_UPDATED = "BRIDGE_UPDATED" - BRIDGE_DELETED = "BRIDGE_DELETED" - - FORWARDER_CREATED = "FORWARDER_CREATED" - FORWARDER_DELETED = "FORWARDER_DELETED" - - EXTERNAL_INITIATOR_CREATED = "EXTERNAL_INITIATOR_CREATED" - EXTERNAL_INITIATOR_DELETED = "EXTERNAL_INITIATOR_DELETED" - - JOB_PROPOSAL_SPEC_APPROVED = "JOB_PROPOSAL_SPEC_APPROVED" - JOB_PROPOSAL_SPEC_UPDATED = "JOB_PROPOSAL_SPEC_UPDATED" - JOB_PROPOSAL_SPEC_CANCELED = "JOB_PROPOSAL_SPEC_CANCELED" - JOB_PROPOSAL_SPEC_REJECTED = "JOB_PROPOSAL_SPEC_REJECTED" - - CONFIG_UPDATED = "CONFIG_UPDATED" - CONFIG_SQL_LOGGING_ENABLED = "CONFIG_SQL_LOGGING_ENABLED" - CONFIG_SQL_LOGGING_DISABLED = "CONFIG_SQL_LOGGING_DISABLED" - GLOBAL_LOG_LEVEL_SET = "GLOBAL_LOG_LEVEL_SET" - - JOB_ERROR_DISMISSED = "JOB_ERROR_DISMISSED" - JOB_RUN_SET = "JOB_RUN_SET" - - ENV_NONCRITICAL_ENV_DUMPED = "ENV_NONCRITICAL_ENV_DUMPED" - - UNAUTHED_RUN_RESUMED = "UNAUTHED_RUN_RESUMED" + "go.uber.org/zap/zapcore" ) type splunkLogger struct { @@ -142,7 +36,7 @@ func newSplunkLogger(logger Logger, splunkToken string, splunkURL string, hostna } } -func (l *splunkLogger) Audit(eventID string, data map[string]interface{}) { +func (l *splunkLogger) Audit(eventID audit.EventID, data map[string]interface{}) { // goroutine to async POST to splunk HTTP Event Collector (HEC) go l.postLogToSplunk(eventID, data) l.logger.Audit(eventID, data) @@ -165,7 +59,7 @@ func getLocalIP() string { return "" } -func (l *splunkLogger) postLogToSplunk(eventID string, data map[string]interface{}) { +func (l *splunkLogger) postLogToSplunk(eventID audit.EventID, data map[string]interface{}) { // Splunk JSON data splunkLog := map[string]interface{}{ "eventID": eventID, diff --git a/core/logger/zap.go b/core/logger/zap.go index 6b80ad5c4ca..320e04b2d0c 100644 --- a/core/logger/zap.go +++ b/core/logger/zap.go @@ -6,6 +6,7 @@ import ( "os" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/core/logger/audit" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) @@ -43,7 +44,7 @@ func (l *zapLogger) With(args ...interface{}) Logger { return &newLogger } -func (l *zapLogger) Audit(eventID string, data map[string]interface{}) { /* STUB */ } +func (l *zapLogger) Audit(eventID audit.EventID, data map[string]interface{}) { /* STUB */ } // copyFields returns a copy of fields with add appended. func copyFields(fields []interface{}, add ...interface{}) []interface{} { diff --git a/core/sessions/orm.go b/core/sessions/orm.go index 6168c4ec234..4a85ec75792 100644 --- a/core/sessions/orm.go +++ b/core/sessions/orm.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/core/auth" "github.com/smartcontractkit/chainlink/core/bridges" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/pg" "github.com/smartcontractkit/chainlink/core/utils" "github.com/smartcontractkit/chainlink/core/utils/mathutil" @@ -133,12 +134,12 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { // Do email and password check first to prevent extra database look up // for MFA tokens leaking if an account has MFA tokens or not. if !constantTimeEmailCompare(sr.Email, user.Email) { - o.lggr.Audit(logger.AUTH_LOGIN_FAILED_EMAIL, map[string]interface{}{"email": sr.Email}) + o.lggr.Audit(audit.AuthLoginFailedEmail, map[string]interface{}{"email": sr.Email}) return "", errors.New("Invalid email") } if !utils.CheckPasswordHash(sr.Password, user.HashedPassword) { - o.lggr.Audit(logger.AUTH_LOGIN_FAILED_PASSWORD, map[string]interface{}{"email": sr.Email}) + o.lggr.Audit(audit.AuthLoginFailedPassword, map[string]interface{}{"email": sr.Email}) return "", errors.New("Invalid password") } @@ -155,7 +156,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { lggr.Infof("No MFA for user. Creating Session") session := NewSession() _, err = o.db.Exec("INSERT INTO sessions (id, last_used, created_at) VALUES ($1, now(), now())", session.ID) - o.lggr.Audit(logger.AUTH_LOGIN_SUCCESS_NO_2FA, map[string]interface{}{"email": sr.Email}) + o.lggr.Audit(audit.AuthLoginSuccessNo2FA, map[string]interface{}{"email": sr.Email}) return session.ID, err } @@ -186,7 +187,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { if err != nil { // The user does have WebAuthn enabled but failed the check - o.lggr.Audit(logger.AUTH_LOGIN_FAILED_2FA, map[string]interface{}{"email": sr.Email, "error": err}) + o.lggr.Audit(audit.AuthLoginFailed2FA, map[string]interface{}{"email": sr.Email, "error": err}) lggr.Errorf("User sent an invalid attestation: %v", err) return "", errors.New("MFA Error") } @@ -205,7 +206,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { lggr.Errorf("error in Marshal credentials: %s", err) return "", err } - o.lggr.Audit(logger.AUTH_LOGIN_SUCCESS_WITH_2FA, map[string]interface{}{"email": sr.Email, "credential": string(uwasj)}) + o.lggr.Audit(audit.AuthLoginSuccessWith2FA, map[string]interface{}{"email": sr.Email, "credential": string(uwasj)}) return session.ID, nil } diff --git a/core/web/bridge_types_controller.go b/core/web/bridge_types_controller.go index 18be716008c..266c7f143a5 100644 --- a/core/web/bridge_types_controller.go +++ b/core/web/bridge_types_controller.go @@ -10,7 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/core/assets" "github.com/smartcontractkit/chainlink/core/bridges" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/store/models" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -97,7 +97,7 @@ func (btc *BridgeTypesController) Create(c *gin.Context) { resource := presenters.NewBridgeResource(*bt) resource.IncomingToken = bta.IncomingToken - btc.App.GetLogger().Audit(logger.BRIDGE_CREATED, map[string]interface{}{ + btc.App.GetLogger().Audit(audit.BridgeCreated, map[string]interface{}{ "bridgeName": bta.Name, "bridgeConfirmations": bta.Confirmations, "bridgeMinimumContractPayment": bta.MinimumContractPayment, @@ -178,7 +178,7 @@ func (btc *BridgeTypesController) Update(c *gin.Context) { return } - btc.App.GetLogger().Audit(logger.BRIDGE_UPDATED, map[string]interface{}{ + btc.App.GetLogger().Audit(audit.BridgeUpdated, map[string]interface{}{ "bridgeName": bt.Name, "bridgeConfirmations": bt.Confirmations, "bridgeMinimumContractPayment": bt.MinimumContractPayment, @@ -222,7 +222,7 @@ func (btc *BridgeTypesController) Destroy(c *gin.Context) { return } - btc.App.GetLogger().Audit(logger.BRIDGE_DELETED, map[string]interface{}{"name": name}) + btc.App.GetLogger().Audit(audit.BridgeDeleted, map[string]interface{}{"name": name}) jsonAPIResponse(c, presenters.NewBridgeResource(bt), "bridge") } diff --git a/core/web/chains_controller.go b/core/web/chains_controller.go index ebf5e3137b5..6c442fb4bee 100644 --- a/core/web/chains_controller.go +++ b/core/web/chains_controller.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink/core/chains" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" ) type ChainsController interface { @@ -96,7 +97,7 @@ func (cc *chainsController[I, C, R]) Create(c *gin.Context) { } chainj, _ := json.Marshal(chain) - cc.lggr.Audit(logger.CHAIN_ADDED, map[string]interface{}{"chain": chainj}) + cc.lggr.Audit(audit.ChainAdded, map[string]interface{}{"chain": chainj}) jsonAPIResponseWithStatus(c, cc.newResource(chain), cc.resourceName, http.StatusCreated) } @@ -153,7 +154,7 @@ func (cc *chainsController[I, C, R]) Update(c *gin.Context) { } chainj, _ := json.Marshal(chain) - cc.lggr.Audit(logger.CHAIN_SPEC_UPDATED, map[string]interface{}{"chain": chainj}) + cc.lggr.Audit(audit.ChainSpecUpdated, map[string]interface{}{"chain": chainj}) jsonAPIResponse(c, cc.newResource(chain), cc.resourceName) } @@ -177,7 +178,7 @@ func (cc *chainsController[I, C, R]) Delete(c *gin.Context) { return } - cc.lggr.Audit(logger.CHAIN_DELETED, map[string]interface{}{"id": id}) + cc.lggr.Audit(audit.ChainDeleted, map[string]interface{}{"id": id}) jsonAPIResponseWithStatus(c, nil, cc.resourceName, http.StatusNoContent) } diff --git a/core/web/config_controller.go b/core/web/config_controller.go index 0a78bc13f78..e1a8c5cb3cd 100644 --- a/core/web/config_controller.go +++ b/core/web/config_controller.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/smartcontractkit/chainlink/core/config" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/utils" @@ -23,7 +23,7 @@ type ConfigController struct { func (cc *ConfigController) Show(c *gin.Context) { cw := config.NewConfigPrinter(cc.App.GetConfig()) - cc.App.GetLogger().Audit(logger.ENV_NONCRITICAL_ENV_DUMPED, map[string]interface{}{}) + cc.App.GetLogger().Audit(audit.EnvNoncriticalEnvDumped, map[string]interface{}{}) jsonAPIResponse(c, cw, "config") } @@ -88,6 +88,6 @@ func (cc *ConfigController) Patch(c *gin.Context) { }, EVMChainID: utils.NewBig(chain.ID()), } - cc.App.GetLogger().Audit(logger.CONFIG_UPDATED, map[string]interface{}{"configResponse": response}) + cc.App.GetLogger().Audit(audit.ConfigUpdated, map[string]interface{}{"configResponse": response}) jsonAPIResponse(c, response, "config") } diff --git a/core/web/csa_keys_controller.go b/core/web/csa_keys_controller.go index 202343d2b5e..550229c6662 100644 --- a/core/web/csa_keys_controller.go +++ b/core/web/csa_keys_controller.go @@ -7,7 +7,7 @@ import ( "github.com/gin-gonic/gin" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/keystore" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -45,7 +45,7 @@ func (ctrl *CSAKeysController) Create(c *gin.Context) { return } - ctrl.App.GetLogger().Audit(logger.CSA_KEY_CREATED, map[string]interface{}{ + ctrl.App.GetLogger().Audit(audit.CSAKeyCreated, map[string]interface{}{ "CSAPublicKey": key.PublicKey, "CSVersion": key.Version, }) @@ -69,7 +69,7 @@ func (ctrl *CSAKeysController) Import(c *gin.Context) { return } - ctrl.App.GetLogger().Audit(logger.CSA_KEY_IMPORTED, map[string]interface{}{ + ctrl.App.GetLogger().Audit(audit.CSAKeyImported, map[string]interface{}{ "CSAPublicKey": key.PublicKey, "CSVersion": key.Version, }) @@ -90,6 +90,6 @@ func (ctrl *CSAKeysController) Export(c *gin.Context) { return } - ctrl.App.GetLogger().Audit(logger.CSA_KEY_EXPORTED, map[string]interface{}{"keyID": keyID}) + ctrl.App.GetLogger().Audit(audit.CSAKeyExported, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/eth_keys_controller.go b/core/web/eth_keys_controller.go index c2db6b30b6d..5f5218f66c4 100644 --- a/core/web/eth_keys_controller.go +++ b/core/web/eth_keys_controller.go @@ -10,7 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/core/assets" "github.com/smartcontractkit/chainlink/core/chains/evm" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/core/utils" @@ -132,7 +132,7 @@ func (ekc *ETHKeysController) Create(c *gin.Context) { return } - ekc.App.GetLogger().Audit(logger.ETH_KEY_CREATED, map[string]interface{}{ + ekc.App.GetLogger().Audit(audit.ETHKeyCreated, map[string]interface{}{ "ethPublicKey": key.Address, "ethID": key.ID(), }) @@ -187,7 +187,7 @@ func (ekc *ETHKeysController) Update(c *gin.Context) { return } - ekc.App.GetLogger().Audit(logger.ETH_KEY_UPDATED, map[string]interface{}{ + ekc.App.GetLogger().Audit(audit.ETHKeyUpdated, map[string]interface{}{ "ethPublicKey": key.Address, "ethID": key.ID(), }) @@ -243,7 +243,7 @@ func (ekc *ETHKeysController) Delete(c *gin.Context) { return } - ekc.App.GetLogger().Audit(logger.ETH_KEY_DELETED, map[string]interface{}{"id": keyID}) + ekc.App.GetLogger().Audit(audit.ETHKeyDeleted, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, r, "account") } @@ -291,7 +291,7 @@ func (ekc *ETHKeysController) Import(c *gin.Context) { return } - ekc.App.GetLogger().Audit(logger.ETH_KEY_IMPORTED, map[string]interface{}{ + ekc.App.GetLogger().Audit(audit.ETHKeyImported, map[string]interface{}{ "ethPublicKey": key.Address, "ethID": key.ID(), }) @@ -311,7 +311,7 @@ func (ekc *ETHKeysController) Export(c *gin.Context) { return } - ekc.App.GetLogger().Audit(logger.ETH_KEY_EXPORTED, map[string]interface{}{"address": address}) + ekc.App.GetLogger().Audit(audit.ETHKeyExported, map[string]interface{}{"address": address}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/evm_forwarders_controller.go b/core/web/evm_forwarders_controller.go index 87f5ea3a2cc..6897a52cb85 100644 --- a/core/web/evm_forwarders_controller.go +++ b/core/web/evm_forwarders_controller.go @@ -5,7 +5,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink/core/chains/evm/forwarders" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/utils" "github.com/smartcontractkit/chainlink/core/utils/stringutils" @@ -59,7 +59,7 @@ func (cc *EVMForwardersController) Create(c *gin.Context) { return } - cc.App.GetLogger().Audit(logger.FORWARDER_CREATED, map[string]interface{}{ + cc.App.GetLogger().Audit(audit.ForwarderCreated, map[string]interface{}{ "forwarderID": fwd.ID, "forwarderAddress": fwd.Address, "forwarderEVMChainID": fwd.EVMChainID, @@ -83,6 +83,6 @@ func (cc *EVMForwardersController) Delete(c *gin.Context) { return } - cc.App.GetLogger().Audit(logger.FORWARDER_DELETED, map[string]interface{}{"id": id}) + cc.App.GetLogger().Audit(audit.ForwarderDeleted, map[string]interface{}{"id": id}) jsonAPIResponseWithStatus(c, nil, "forwarder", http.StatusNoContent) } diff --git a/core/web/evm_transfer_controller.go b/core/web/evm_transfer_controller.go index 2cdc7fb0606..ba5c0ad57a0 100644 --- a/core/web/evm_transfer_controller.go +++ b/core/web/evm_transfer_controller.go @@ -9,7 +9,7 @@ import ( "github.com/smartcontractkit/chainlink/core/assets" "github.com/smartcontractkit/chainlink/core/chains/evm" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/store/models" "github.com/smartcontractkit/chainlink/core/utils" @@ -64,7 +64,7 @@ func (tc *EVMTransfersController) Create(c *gin.Context) { return } - tc.App.GetLogger().Audit(logger.ETH_TRANSACTION_CREATED, map[string]interface{}{ + tc.App.GetLogger().Audit(audit.EthTransactionCreated, map[string]interface{}{ "ethTX": etx, }) diff --git a/core/web/external_initiators_controller.go b/core/web/external_initiators_controller.go index 15611e01773..85c5484fb37 100644 --- a/core/web/external_initiators_controller.go +++ b/core/web/external_initiators_controller.go @@ -8,7 +8,7 @@ import ( "github.com/smartcontractkit/chainlink/core/auth" "github.com/smartcontractkit/chainlink/core/bridges" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/store/models" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -85,7 +85,7 @@ func (eic *ExternalInitiatorsController) Create(c *gin.Context) { return } - eic.App.GetLogger().Audit(logger.EXTERNAL_INITIATOR_CREATED, map[string]interface{}{ + eic.App.GetLogger().Audit(audit.ExternalInitiatorCreated, map[string]interface{}{ "externalInitiatorID": ei.ID, "externalInitiatorName": ei.Name, "externalInitiatorURL": ei.URL, @@ -108,6 +108,6 @@ func (eic *ExternalInitiatorsController) Destroy(c *gin.Context) { return } - eic.App.GetLogger().Audit(logger.EXTERNAL_INITIATOR_DELETED, map[string]interface{}{"name": name}) + eic.App.GetLogger().Audit(audit.ExternalInitiatorDeleted, map[string]interface{}{"name": name}) jsonAPIResponseWithStatus(c, nil, "external initiator", http.StatusNoContent) } diff --git a/core/web/jobs_controller.go b/core/web/jobs_controller.go index a1b92025c1f..dac86b1edf4 100644 --- a/core/web/jobs_controller.go +++ b/core/web/jobs_controller.go @@ -11,7 +11,7 @@ import ( "github.com/pkg/errors" uuid "github.com/satori/go.uuid" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/blockhashstore" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/cron" @@ -159,7 +159,7 @@ func (jc *JobsController) Create(c *gin.Context) { } jbj, _ := json.Marshal(jb) - jc.App.GetLogger().Audit(logger.JOB_CREATED, map[string]interface{}{"job": string(jbj)}) + jc.App.GetLogger().Audit(audit.JobCreated, map[string]interface{}{"job": string(jbj)}) jsonAPIResponse(c, presenters.NewJobResource(jb), jb.Type.String()) } @@ -186,6 +186,6 @@ func (jc *JobsController) Delete(c *gin.Context) { return } - jc.App.GetLogger().Audit(logger.JOB_DELETED, map[string]interface{}{"id": j.ID}) + jc.App.GetLogger().Audit(audit.JobDeleted, map[string]interface{}{"id": j.ID}) jsonAPIResponseWithStatus(c, nil, "job", http.StatusNoContent) } diff --git a/core/web/keys_controller.go b/core/web/keys_controller.go index 4a55529ff77..8307eb427f7 100644 --- a/core/web/keys_controller.go +++ b/core/web/keys_controller.go @@ -9,6 +9,7 @@ import ( "github.com/manyminds/api2go/jsonapi" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/keystore" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/solkey" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/terrakey" @@ -74,14 +75,14 @@ func (kc *keysController[K, R]) Create(c *gin.Context) { // Emit audit log, determine if Terra or Solana key switch unwrappedKey := any(key).(type) { case terrakey.Key: - kc.lggr.Audit(logger.TERRA_KEY_CREATED, map[string]interface{}{ - "terraPublicKey": unwrappedKey.PublicKey(), - "terraID": unwrappedKey.ID(), + kc.lggr.Audit(audit.TerraKeyCreated, map[string]interface{}{ + "publicKey": unwrappedKey.PublicKey(), + "id": unwrappedKey.ID(), }) case solkey.Key: - kc.lggr.Audit(logger.SOLANA_KEY_CREATED, map[string]interface{}{ - "solanaPublicKey": unwrappedKey.PublicKey(), - "solanaID": unwrappedKey.ID(), + kc.lggr.Audit(audit.SolanaKeyCreated, map[string]interface{}{ + "publicKey": unwrappedKey.PublicKey(), + "id": unwrappedKey.ID(), }) } @@ -104,9 +105,9 @@ func (kc *keysController[K, R]) Delete(c *gin.Context) { // Emit audit log, determine if Terra or Solana key switch any(key).(type) { case terrakey.Key: - kc.lggr.Audit(logger.TERRA_KEY_DELETED, map[string]interface{}{"terraID": keyID}) + kc.lggr.Audit(audit.TerraKeyDeleted, map[string]interface{}{"id": keyID}) case solkey.Key: - kc.lggr.Audit(logger.SOLANA_KEY_DELETED, map[string]interface{}{"solanaID": keyID}) + kc.lggr.Audit(audit.SolanaKeyDeleted, map[string]interface{}{"id": keyID}) } jsonAPIResponse(c, kc.newResource(key), kc.resourceName) @@ -130,14 +131,14 @@ func (kc *keysController[K, R]) Import(c *gin.Context) { // Emit audit log, determine if Terra or Solana key switch unwrappedKey := any(key).(type) { case terrakey.Key: - kc.lggr.Audit(logger.TERRA_KEY_IMPORTED, map[string]interface{}{ - "terraPublicKey": unwrappedKey.PublicKey(), - "terraID": unwrappedKey.ID(), + kc.lggr.Audit(audit.TerraKeyImported, map[string]interface{}{ + "publicKey": unwrappedKey.PublicKey(), + "id": unwrappedKey.ID(), }) case solkey.Key: - kc.lggr.Audit(logger.SOLANA_KEY_IMPORTED, map[string]interface{}{ - "solanaPublicKey": unwrappedKey.PublicKey(), - "solanaID": unwrappedKey.ID(), + kc.lggr.Audit(audit.SolanaKeyImported, map[string]interface{}{ + "publicKey": unwrappedKey.PublicKey(), + "id": unwrappedKey.ID(), }) } @@ -156,9 +157,9 @@ func (kc *keysController[K, R]) Export(c *gin.Context) { } if strings.HasPrefix(c.Request.URL.Path, "/v2/keys/terra") { - kc.lggr.Audit(logger.TERRA_KEY_EXPORTED, map[string]interface{}{"terraID": keyID}) + kc.lggr.Audit(audit.TerraKeyExported, map[string]interface{}{"id": keyID}) } else if strings.HasPrefix(c.Request.URL.Path, "/v2/keys/solana") { - kc.lggr.Audit(logger.SOLANA_KEY_EXPORTED, map[string]interface{}{"solanaID": keyID}) + kc.lggr.Audit(audit.SolanaKeyExported, map[string]interface{}{"id": keyID}) } c.Data(http.StatusOK, MediaType, bytes) diff --git a/core/web/log_controller.go b/core/web/log_controller.go index 883c8ef13ec..4392dcde636 100644 --- a/core/web/log_controller.go +++ b/core/web/log_controller.go @@ -8,7 +8,7 @@ import ( "github.com/gin-gonic/gin" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/web/presenters" ) @@ -91,13 +91,13 @@ func (cc *LogController) Patch(c *gin.Context) { LogLevel: lvls, } - cc.App.GetLogger().Audit(logger.GLOBAL_LOG_LEVEL_SET, map[string]interface{}{"logLevel": request.Level}) + cc.App.GetLogger().Audit(audit.GlobalLogLevelSet, map[string]interface{}{"logLevel": request.Level}) if request.Level == "debug" { if request.SqlEnabled != nil && *request.SqlEnabled { - cc.App.GetLogger().Audit(logger.CONFIG_SQL_LOGGING_ENABLED, map[string]interface{}{}) + cc.App.GetLogger().Audit(audit.ConfigSqlLoggingEnabled, map[string]interface{}{}) } else { - cc.App.GetLogger().Audit(logger.CONFIG_SQL_LOGGING_DISABLED, map[string]interface{}{}) + cc.App.GetLogger().Audit(audit.ConfigSqlLoggingDisabled, map[string]interface{}{}) } } diff --git a/core/web/nodes_controller.go b/core/web/nodes_controller.go index a292618b4ee..6086b6f056e 100644 --- a/core/web/nodes_controller.go +++ b/core/web/nodes_controller.go @@ -9,6 +9,7 @@ import ( "github.com/smartcontractkit/chainlink/core/chains" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" ) type NodesController interface { @@ -99,7 +100,7 @@ func (n *nodesController[I, N, R]) Create(c *gin.Context) { return } - n.lggr.Audit(logger.CHAIN_RPC_NODE_ADDED, map[string]interface{}{}) + n.lggr.Audit(audit.ChainRpcNodeAdded, map[string]interface{}{}) jsonAPIResponse(c, n.newResource(node), "node") } @@ -123,7 +124,7 @@ func (n *nodesController[I, N, R]) Delete(c *gin.Context) { return } - n.lggr.Audit(logger.CHAIN_DELETED, map[string]interface{}{"id": id}) + n.lggr.Audit(audit.ChainDeleted, map[string]interface{}{"id": id}) jsonAPIResponseWithStatus(c, nil, "node", http.StatusNoContent) } diff --git a/core/web/ocr2_keys_controller.go b/core/web/ocr2_keys_controller.go index c9e34f4d0b4..a1d88d0f5f1 100644 --- a/core/web/ocr2_keys_controller.go +++ b/core/web/ocr2_keys_controller.go @@ -6,7 +6,7 @@ import ( "github.com/gin-gonic/gin" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -44,7 +44,7 @@ func (ocr2kc *OCR2KeysController) Create(c *gin.Context) { return } - ocr2kc.App.GetLogger().Audit(logger.OCR2_KEY_BUNDLE_CREATED, map[string]interface{}{ + ocr2kc.App.GetLogger().Audit(audit.OCR2KeyBundleCreated, map[string]interface{}{ "ocr2KeyID": key.ID(), "ocr2KeyChainType": key.ChainType(), "ocr2KeyConfigEncryptionPublicKey": key.ConfigEncryptionPublicKey(), @@ -71,7 +71,7 @@ func (ocr2kc *OCR2KeysController) Delete(c *gin.Context) { return } - ocr2kc.App.GetLogger().Audit(logger.OCR2_KEY_BUNDLE_DELETED, map[string]interface{}{"id": id}) + ocr2kc.App.GetLogger().Audit(audit.OCR2KeyBundleDeleted, map[string]interface{}{"id": id}) jsonAPIResponse(c, presenters.NewOCR2KeysBundleResource(key), "offChainReporting2KeyBundle") } @@ -93,7 +93,7 @@ func (ocr2kc *OCR2KeysController) Import(c *gin.Context) { return } - ocr2kc.App.GetLogger().Audit(logger.OCR2_KEY_BUNDLE_IMPORTED, map[string]interface{}{ + ocr2kc.App.GetLogger().Audit(audit.OCR2KeyBundleImported, map[string]interface{}{ "ocr2KeyID": keyBundle.ID(), "ocr2KeyChainType": keyBundle.ChainType(), "ocr2KeyConfigEncryptionPublicKey": keyBundle.ConfigEncryptionPublicKey(), @@ -119,6 +119,6 @@ func (ocr2kc *OCR2KeysController) Export(c *gin.Context) { return } - ocr2kc.App.GetLogger().Audit(logger.OCR2_KEY_BUNDLE_EXPORTED, map[string]interface{}{"keyID": stringID}) + ocr2kc.App.GetLogger().Audit(audit.OCR2KeyBundleExported, map[string]interface{}{"keyID": stringID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/ocr_keys_controller.go b/core/web/ocr_keys_controller.go index 285b27276f5..15cb40b36d2 100644 --- a/core/web/ocr_keys_controller.go +++ b/core/web/ocr_keys_controller.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/web/presenters" ) @@ -37,7 +37,7 @@ func (ocrkc *OCRKeysController) Create(c *gin.Context) { return } - ocrkc.App.GetLogger().Audit(logger.OCR_KEY_BUNDLE_CREATED, map[string]interface{}{ + ocrkc.App.GetLogger().Audit(audit.OCRKeyBundleCreated, map[string]interface{}{ "ocrKeyBundleID": key.ID(), "ocrKeyBundlePublicKeyAddressOnChain": key.PublicKeyAddressOnChain(), }) @@ -61,7 +61,7 @@ func (ocrkc *OCRKeysController) Delete(c *gin.Context) { return } - ocrkc.App.GetLogger().Audit(logger.OCR_KEY_BUNDLE_DELETED, map[string]interface{}{"id": id}) + ocrkc.App.GetLogger().Audit(audit.OCRKeyBundleDeleted, map[string]interface{}{"id": id}) jsonAPIResponse(c, presenters.NewOCRKeysBundleResource(key), "offChainReportingKeyBundle") } @@ -83,7 +83,7 @@ func (ocrkc *OCRKeysController) Import(c *gin.Context) { return } - ocrkc.App.GetLogger().Audit(logger.OCR_KEY_BUNDLE_IMPORTED, map[string]interface{}{ + ocrkc.App.GetLogger().Audit(audit.OCRKeyBundleImported, map[string]interface{}{ "OCRID": encryptedOCRKeyBundle.GetID(), "OCRPublicKeyAddressOnChain": encryptedOCRKeyBundle.PublicKeyAddressOnChain(), "OCRPublicKeyOffChain": encryptedOCRKeyBundle.PublicKeyOffChain(), @@ -106,6 +106,6 @@ func (ocrkc *OCRKeysController) Export(c *gin.Context) { return } - ocrkc.App.GetLogger().Audit(logger.OCR_KEY_BUNDLE_EXPORTED, map[string]interface{}{"keyID": stringID}) + ocrkc.App.GetLogger().Audit(audit.OCRKeyBundleExported, map[string]interface{}{"keyID": stringID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/p2p_keys_controller.go b/core/web/p2p_keys_controller.go index 8a9fa2649eb..b12babc6f2a 100644 --- a/core/web/p2p_keys_controller.go +++ b/core/web/p2p_keys_controller.go @@ -6,7 +6,7 @@ import ( "github.com/gin-gonic/gin" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -39,7 +39,7 @@ func (p2pkc *P2PKeysController) Create(c *gin.Context) { return } - p2pkc.App.GetLogger().Audit(logger.P2P_KEY_CREATED, map[string]interface{}{ + p2pkc.App.GetLogger().Audit(audit.P2PKeyCreated, map[string]interface{}{ "p2pPublicKey": key.PublicKeyHex(), "p2pID": key.ID(), "p2pPeerID": key.PeerID(), @@ -69,7 +69,7 @@ func (p2pkc *P2PKeysController) Delete(c *gin.Context) { return } - p2pkc.App.GetLogger().Audit(logger.P2P_KEY_DELETED, map[string]interface{}{"id": keyID}) + p2pkc.App.GetLogger().Audit(audit.P2PKeyDeleted, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, presenters.NewP2PKeyResource(key), "p2pKey") } @@ -91,7 +91,7 @@ func (p2pkc *P2PKeysController) Import(c *gin.Context) { return } - p2pkc.App.GetLogger().Audit(logger.P2P_KEY_IMPORTED, map[string]interface{}{ + p2pkc.App.GetLogger().Audit(audit.P2PKeyImported, map[string]interface{}{ "p2pPublicKey": key.PublicKeyHex(), "p2pID": key.ID(), "p2pPeerID": key.PeerID(), @@ -119,6 +119,6 @@ func (p2pkc *P2PKeysController) Export(c *gin.Context) { return } - p2pkc.App.GetLogger().Audit(logger.P2P_KEY_EXPORTED, map[string]interface{}{"keyID": keyID}) + p2pkc.App.GetLogger().Audit(audit.P2PKeyExported, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/pipeline_job_spec_errors_controller.go b/core/web/pipeline_job_spec_errors_controller.go index c8b1a286ad6..d3d2423348c 100644 --- a/core/web/pipeline_job_spec_errors_controller.go +++ b/core/web/pipeline_job_spec_errors_controller.go @@ -8,7 +8,7 @@ import ( "github.com/gin-gonic/gin" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/job" ) @@ -38,6 +38,6 @@ func (psec *PipelineJobSpecErrorsController) Destroy(c *gin.Context) { return } - psec.App.GetLogger().Audit(logger.JOB_ERROR_DISMISSED, map[string]interface{}{"id": jobSpec.ID}) + psec.App.GetLogger().Audit(audit.JobErrorDismissed, map[string]interface{}{"id": jobSpec.ID}) jsonAPIResponseWithStatus(c, nil, "job", http.StatusNoContent) } diff --git a/core/web/pipeline_runs_controller.go b/core/web/pipeline_runs_controller.go index 417a6d88bd5..1e7cf8339ad 100644 --- a/core/web/pipeline_runs_controller.go +++ b/core/web/pipeline_runs_controller.go @@ -11,7 +11,7 @@ import ( "github.com/pkg/errors" uuid "github.com/satori/go.uuid" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/job" "github.com/smartcontractkit/chainlink/core/services/pipeline" @@ -180,6 +180,6 @@ func (prc *PipelineRunsController) Resume(c *gin.Context) { return } - prc.App.GetLogger().Audit(logger.UNAUTHED_RUN_RESUMED, map[string]interface{}{"runID": c.Param("runID")}) + prc.App.GetLogger().Audit(audit.UnauthedRunResumed, map[string]interface{}{"runID": c.Param("runID")}) c.Status(http.StatusOK) } diff --git a/core/web/resolver/mutation.go b/core/web/resolver/mutation.go index bd465401495..047d2c959f7 100644 --- a/core/web/resolver/mutation.go +++ b/core/web/resolver/mutation.go @@ -17,7 +17,7 @@ import ( "github.com/smartcontractkit/chainlink/core/auth" "github.com/smartcontractkit/chainlink/core/bridges" "github.com/smartcontractkit/chainlink/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/blockhashstore" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/cron" @@ -96,7 +96,7 @@ func (r *Resolver) CreateBridge(ctx context.Context, args struct{ Input createBr return nil, err } - r.App.GetLogger().Audit(logger.BRIDGE_CREATED, map[string]interface{}{ + r.App.GetLogger().Audit(audit.BridgeCreated, map[string]interface{}{ "bridgeName": bta.Name, "bridgeConfirmations": bta.Confirmations, "bridgeMinimumContractPayment": bta.MinimumContractPayment, @@ -120,7 +120,7 @@ func (r *Resolver) CreateCSAKey(ctx context.Context) (*CreateCSAKeyPayloadResolv return nil, err } - r.App.GetLogger().Audit(logger.CSA_KEY_CREATED, map[string]interface{}{ + r.App.GetLogger().Audit(audit.CSAKeyCreated, map[string]interface{}{ "CSAPublicKey": key.PublicKey, "CSVersion": key.Version, }) @@ -144,7 +144,7 @@ func (r *Resolver) DeleteCSAKey(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.CSA_KEY_DELETED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(audit.CSAKeyDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteCSAKeyPayload(key, nil), nil } @@ -237,7 +237,7 @@ func (r *Resolver) CreateFeedsManagerChainConfig(ctx context.Context, args struc } fmj, _ := json.Marshal(ccfg) - r.App.GetLogger().Audit(logger.FEEDS_MAN_CHAIN_CONFIG_CREATED, map[string]interface{}{"feedsManager": fmj}) + r.App.GetLogger().Audit(audit.FeedsManChainConfigCreated, map[string]interface{}{"feedsManager": fmj}) return NewCreateFeedsManagerChainConfigPayload(ccfg, nil, nil), nil } @@ -273,7 +273,7 @@ func (r *Resolver) DeleteFeedsManagerChainConfig(ctx context.Context, args struc return nil, err } - r.App.GetLogger().Audit(logger.FEEDS_MAN_CHAIN_CONFIG_DELETED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(audit.FeedsManChainConfigDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteFeedsManagerChainConfigPayload(ccfg, nil), nil } @@ -357,7 +357,7 @@ func (r *Resolver) UpdateFeedsManagerChainConfig(ctx context.Context, args struc } fmj, _ := json.Marshal(ccfg) - r.App.GetLogger().Audit(logger.FEEDS_MAN_CHAIN_CONFIG_UPDATED, map[string]interface{}{"feedsManager": fmj}) + r.App.GetLogger().Audit(audit.FeedsManChainConfigUpdated, map[string]interface{}{"feedsManager": fmj}) return NewUpdateFeedsManagerChainConfigPayload(ccfg, nil, nil), nil } @@ -408,7 +408,7 @@ func (r *Resolver) CreateFeedsManager(ctx context.Context, args struct { } mgrj, _ := json.Marshal(mgr) - r.App.GetLogger().Audit(logger.FEEDS_MAN_CREATED, map[string]interface{}{"mgrj": mgrj}) + r.App.GetLogger().Audit(audit.FeedsManCreated, map[string]interface{}{"mgrj": mgrj}) return NewCreateFeedsManagerPayload(mgr, nil, nil), nil } @@ -472,7 +472,7 @@ func (r *Resolver) UpdateBridge(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.BRIDGE_UPDATED, map[string]interface{}{ + r.App.GetLogger().Audit(audit.BridgeUpdated, map[string]interface{}{ "bridgeName": bridge.Name, "bridgeConfirmations": bridge.Confirmations, "bridgeMinimumContractPayment": bridge.MinimumContractPayment, @@ -531,7 +531,7 @@ func (r *Resolver) UpdateFeedsManager(ctx context.Context, args struct { } mgrj, _ := json.Marshal(mgr) - r.App.GetLogger().Audit(logger.FEEDS_MAN_UPDATED, map[string]interface{}{"mgrj": mgrj}) + r.App.GetLogger().Audit(audit.FeedsManUpdated, map[string]interface{}{"mgrj": mgrj}) return NewUpdateFeedsManagerPayload(mgr, nil, nil), nil } @@ -546,7 +546,7 @@ func (r *Resolver) CreateOCRKeyBundle(ctx context.Context) (*CreateOCRKeyBundleP return nil, err } - r.App.GetLogger().Audit(logger.OCR_KEY_BUNDLE_CREATED, map[string]interface{}{ + r.App.GetLogger().Audit(audit.OCRKeyBundleCreated, map[string]interface{}{ "ocrKeyBundleID": key.ID(), "ocrKeyBundlePublicKeyAddressOnChain": key.PublicKeyAddressOnChain(), }) @@ -569,7 +569,7 @@ func (r *Resolver) DeleteOCRKeyBundle(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.OCR_KEY_BUNDLE_DELETED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(audit.OCRKeyBundleDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteOCRKeyBundlePayloadResolver(deletedKey, nil), nil } @@ -593,7 +593,7 @@ func (r *Resolver) CreateNode(ctx context.Context, args struct { wsURL, _ := url.Parse(args.Input.WSURL.String) // Forward only RPC host to logs httpURL, _ := url.Parse(args.Input.HTTPURL.String) - r.App.GetLogger().Audit(logger.CHAIN_RPC_NODE_ADDED, map[string]interface{}{ + r.App.GetLogger().Audit(audit.ChainRpcNodeAdded, map[string]interface{}{ "chainNodeName": args.Input.Name, "chainNodeEvmChainID": args.Input.EVMChainID, "chainNodeRPCWebSocketHost": wsURL.Host, @@ -637,7 +637,7 @@ func (r *Resolver) DeleteNode(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.CHAIN_RPC_NODE_DELETED, map[string]interface{}{"id": id}) + r.App.GetLogger().Audit(audit.ChainRpcNodeDeleted, map[string]interface{}{"id": id}) return NewDeleteNodePayloadResolver(&node, nil), nil } @@ -675,7 +675,7 @@ func (r *Resolver) DeleteBridge(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.BRIDGE_DELETED, map[string]interface{}{"name": bt.Name}) + r.App.GetLogger().Audit(audit.BridgeDeleted, map[string]interface{}{"name": bt.Name}) return NewDeleteBridgePayload(&bt, nil), nil } @@ -689,7 +689,7 @@ func (r *Resolver) CreateP2PKey(ctx context.Context) (*CreateP2PKeyPayloadResolv return nil, err } - r.App.GetLogger().Audit(logger.P2P_KEY_CREATED, map[string]interface{}{ + r.App.GetLogger().Audit(audit.P2PKeyCreated, map[string]interface{}{ "p2pPublicKey": key.PublicKeyHex(), "p2pID": key.ID(), "p2pPeerID": key.PeerID(), @@ -719,7 +719,7 @@ func (r *Resolver) DeleteP2PKey(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.P2P_KEY_DELETED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(audit.P2PKeyDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteP2PKeyPayload(key, nil), nil } @@ -733,7 +733,7 @@ func (r *Resolver) CreateVRFKey(ctx context.Context) (*CreateVRFKeyPayloadResolv return nil, err } - r.App.GetLogger().Audit(logger.VRF_KEY_CREATED, map[string]interface{}{ + r.App.GetLogger().Audit(audit.VRFKeyCreated, map[string]interface{}{ "vrfPublicKey": key.PublicKey, "vrfID": key.ID(), "vrfPublicKeyAddress": key.PublicKey.Address(), @@ -757,7 +757,7 @@ func (r *Resolver) DeleteVRFKey(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.VRF_KEY_DELETED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(audit.VRFKeyDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteVRFKeyPayloadResolver(key, nil), nil } @@ -796,7 +796,7 @@ func (r *Resolver) ApproveJobProposalSpec(ctx context.Context, args struct { } specj, _ := json.Marshal(spec) - r.App.GetLogger().Audit(logger.JOB_PROPOSAL_SPEC_APPROVED, map[string]interface{}{"spec": specj}) + r.App.GetLogger().Audit(audit.JobProposalSpecApproved, map[string]interface{}{"spec": specj}) return NewApproveJobProposalSpecPayload(spec, err), nil } @@ -831,7 +831,7 @@ func (r *Resolver) CancelJobProposalSpec(ctx context.Context, args struct { } specj, _ := json.Marshal(spec) - r.App.GetLogger().Audit(logger.JOB_PROPOSAL_SPEC_CANCELED, map[string]interface{}{"spec": specj}) + r.App.GetLogger().Audit(audit.JobProposalSpecCanceled, map[string]interface{}{"spec": specj}) return NewCancelJobProposalSpecPayload(spec, err), nil } @@ -866,7 +866,7 @@ func (r *Resolver) RejectJobProposalSpec(ctx context.Context, args struct { } specj, _ := json.Marshal(spec) - r.App.GetLogger().Audit(logger.JOB_PROPOSAL_SPEC_REJECTED, map[string]interface{}{"spec": specj}) + r.App.GetLogger().Audit(audit.JobProposalSpecRejected, map[string]interface{}{"spec": specj}) return NewRejectJobProposalSpecPayload(spec, err), nil } @@ -904,7 +904,7 @@ func (r *Resolver) UpdateJobProposalSpecDefinition(ctx context.Context, args str } specj, _ := json.Marshal(spec) - r.App.GetLogger().Audit(logger.JOB_PROPOSAL_SPEC_UPDATED, map[string]interface{}{"spec": specj}) + r.App.GetLogger().Audit(audit.JobProposalSpecUpdated, map[string]interface{}{"spec": specj}) return NewUpdateJobProposalSpecDefinitionPayload(spec, err), nil } @@ -927,7 +927,7 @@ func (r *Resolver) UpdateUserPassword(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.OldPassword, dbUser.HashedPassword) { - r.App.GetLogger().Audit(logger.PASSWORD_RESET_ATTEMPT_FAILED_MISMATCH, map[string]interface{}{"user": dbUser.Email}) + r.App.GetLogger().Audit(audit.PasswordResetAttemptFailedMismatch, map[string]interface{}{"user": dbUser.Email}) return NewUpdatePasswordPayload(nil, map[string]string{ "oldPassword": "old password does not match", @@ -943,7 +943,7 @@ func (r *Resolver) UpdateUserPassword(ctx context.Context, args struct { return nil, failedPasswordUpdateError{} } - r.App.GetLogger().Audit(logger.PASSWORD_RESET_SUCCESS, map[string]interface{}{"user": dbUser.Email}) + r.App.GetLogger().Audit(audit.PasswordResetSuccess, map[string]interface{}{"user": dbUser.Email}) return NewUpdatePasswordPayload(session.User, nil), nil } @@ -957,9 +957,9 @@ func (r *Resolver) SetSQLLogging(ctx context.Context, args struct { r.App.GetConfig().SetLogSQL(args.Input.Enabled) if args.Input.Enabled { - r.App.GetLogger().Audit(logger.CONFIG_SQL_LOGGING_ENABLED, map[string]interface{}{}) + r.App.GetLogger().Audit(audit.ConfigSqlLoggingEnabled, map[string]interface{}{}) } else { - r.App.GetLogger().Audit(logger.CONFIG_SQL_LOGGING_DISABLED, map[string]interface{}{}) + r.App.GetLogger().Audit(audit.ConfigSqlLoggingDisabled, map[string]interface{}{}) } return NewSetSQLLoggingPayload(args.Input.Enabled), nil @@ -978,7 +978,7 @@ func (r *Resolver) CreateAPIToken(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.Password, dbUser.HashedPassword) { - r.App.GetLogger().Audit(logger.API_TOKEN_CREATE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": dbUser.Email}) + r.App.GetLogger().Audit(audit.APITokenCreateAttemptPasswordMismatch, map[string]interface{}{"user": dbUser.Email}) return NewCreateAPITokenPayload(nil, map[string]string{ "password": "incorrect password", @@ -990,7 +990,7 @@ func (r *Resolver) CreateAPIToken(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.API_TOKEN_CREATED, map[string]interface{}{"user": dbUser.Email}) + r.App.GetLogger().Audit(audit.APITokenCreated, map[string]interface{}{"user": dbUser.Email}) return NewCreateAPITokenPayload(newToken, nil), nil } @@ -1007,7 +1007,7 @@ func (r *Resolver) DeleteAPIToken(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.Password, dbUser.HashedPassword) { - r.App.GetLogger().Audit(logger.API_TOKEN_DELETE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": dbUser.Email}) + r.App.GetLogger().Audit(audit.APITokenDeleteAttemptPasswordMismatch, map[string]interface{}{"user": dbUser.Email}) return NewDeleteAPITokenPayload(nil, map[string]string{ "password": "incorrect password", @@ -1019,7 +1019,7 @@ func (r *Resolver) DeleteAPIToken(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.API_TOKEN_DELETED, map[string]interface{}{"user": dbUser.Email}) + r.App.GetLogger().Audit(audit.APITokenDeleted, map[string]interface{}{"user": dbUser.Email}) return NewDeleteAPITokenPayload(&auth.Token{ AccessKey: dbUser.TokenKey.String, @@ -1071,7 +1071,7 @@ func (r *Resolver) CreateChain(ctx context.Context, args struct { } chainj, _ := json.Marshal(chain) - r.App.GetLogger().Audit(logger.CHAIN_ADDED, map[string]interface{}{"chain": chainj}) + r.App.GetLogger().Audit(audit.ChainAdded, map[string]interface{}{"chain": chainj}) return NewCreateChainPayload(&chain, nil), nil } @@ -1126,7 +1126,7 @@ func (r *Resolver) UpdateChain(ctx context.Context, args struct { } chainj, _ := json.Marshal(chain) - r.App.GetLogger().Audit(logger.CHAIN_SPEC_UPDATED, map[string]interface{}{"chainj": chainj}) + r.App.GetLogger().Audit(audit.ChainSpecUpdated, map[string]interface{}{"chainj": chainj}) return NewUpdateChainPayload(&chain, nil, nil), nil } @@ -1158,7 +1158,7 @@ func (r *Resolver) DeleteChain(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.CHAIN_DELETED, map[string]interface{}{"id": id}) + r.App.GetLogger().Audit(audit.ChainDeleted, map[string]interface{}{"id": id}) return NewDeleteChainPayload(&chain, nil), nil } @@ -1225,7 +1225,7 @@ func (r *Resolver) CreateJob(ctx context.Context, args struct { } jbj, _ := json.Marshal(jb) - r.App.GetLogger().Audit(logger.JOB_CREATED, map[string]interface{}{"job": string(jbj)}) + r.App.GetLogger().Audit(audit.JobCreated, map[string]interface{}{"job": string(jbj)}) return NewCreateJobPayload(r.App, &jb, nil), nil } @@ -1260,7 +1260,7 @@ func (r *Resolver) DeleteJob(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.JOB_DELETED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(audit.JobDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteJobPayload(r.App, &j, nil), nil } @@ -1294,7 +1294,7 @@ func (r *Resolver) DismissJobError(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.JOB_ERROR_DISMISSED, map[string]interface{}{"id": args.ID}) + r.App.GetLogger().Audit(audit.JobErrorDismissed, map[string]interface{}{"id": args.ID}) return NewDismissJobErrorPayload(&specErr, nil), nil } @@ -1324,7 +1324,7 @@ func (r *Resolver) RunJob(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.JOB_RUN_SET, map[string]interface{}{"jobID": args.ID, "jobRunID": jobRunID, "planRunID": plnRun}) + r.App.GetLogger().Audit(audit.JobRunSet, map[string]interface{}{"jobID": args.ID, "jobRunID": jobRunID, "planRunID": plnRun}) return NewRunJobPayload(&plnRun, r.App, nil), nil } @@ -1349,7 +1349,7 @@ func (r *Resolver) SetGlobalLogLevel(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.GLOBAL_LOG_LEVEL_SET, map[string]interface{}{"logLevel": args.Level}) + r.App.GetLogger().Audit(audit.GlobalLogLevelSet, map[string]interface{}{"logLevel": args.Level}) return NewSetGlobalLogLevelPayload(args.Level, nil), nil } @@ -1368,7 +1368,7 @@ func (r *Resolver) CreateOCR2KeyBundle(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.OCR2_KEY_BUNDLE_CREATED, map[string]interface{}{ + r.App.GetLogger().Audit(audit.OCR2KeyBundleCreated, map[string]interface{}{ "ocrKeyID": key.ID(), "ocrKeyChainType": key.ChainType(), "ocrKeyConfigEncryptionPublicKey": key.ConfigEncryptionPublicKey(), @@ -1399,6 +1399,6 @@ func (r *Resolver) DeleteOCR2KeyBundle(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(logger.OCR2_KEY_BUNDLE_DELETED, map[string]interface{}{"id": id}) + r.App.GetLogger().Audit(audit.OCR2KeyBundleDeleted, map[string]interface{}{"id": id}) return NewDeleteOCR2KeyBundlePayloadResolver(&key, nil), nil } diff --git a/core/web/sessions_controller.go b/core/web/sessions_controller.go index 6b9f9098918..421b43985a4 100644 --- a/core/web/sessions_controller.go +++ b/core/web/sessions_controller.go @@ -9,7 +9,7 @@ import ( "github.com/gin-gonic/gin" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" clsessions "github.com/smartcontractkit/chainlink/core/sessions" "github.com/smartcontractkit/chainlink/core/web/auth" @@ -84,7 +84,7 @@ func (sc *SessionsController) Destroy(c *gin.Context) { return } - sc.App.GetLogger().Audit(logger.AUTH_SESSION_DELETED, map[string]interface{}{"removedSessionID": sessionID}) + sc.App.GetLogger().Audit(audit.AuthSessionDeleted, map[string]interface{}{"sessionID": sessionID}) jsonAPIResponse(c, Session{Authenticated: false}, "session") } diff --git a/core/web/solana_transfer_controller.go b/core/web/solana_transfer_controller.go index eefac735135..b9fa04ffcf4 100644 --- a/core/web/solana_transfer_controller.go +++ b/core/web/solana_transfer_controller.go @@ -13,7 +13,7 @@ import ( solanaGo "github.com/gagliardetto/solana-go" "github.com/smartcontractkit/chainlink/core/chains" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" solanamodels "github.com/smartcontractkit/chainlink/core/store/models/solana" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -111,7 +111,7 @@ func (tc *SolanaTransfersController) Create(c *gin.Context) { resource.From = tr.From.String() resource.To = tr.To.String() - tc.App.GetLogger().Audit(logger.SOLANA_TRANSACTION_CREATED, map[string]interface{}{ + tc.App.GetLogger().Audit(audit.SolanaTransactionCreated, map[string]interface{}{ "solanaTransactionResource": resource, }) jsonAPIResponse(c, resource, "solana_tx") diff --git a/core/web/terra_transfer_controller.go b/core/web/terra_transfer_controller.go index 08723117b3d..0e458cfa30b 100644 --- a/core/web/terra_transfer_controller.go +++ b/core/web/terra_transfer_controller.go @@ -12,7 +12,7 @@ import ( "github.com/smartcontractkit/chainlink/core/chains/terra" "github.com/smartcontractkit/chainlink/core/chains/terra/denom" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" terramodels "github.com/smartcontractkit/chainlink/core/store/models/terra" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -111,7 +111,7 @@ func (tc *TerraTransfersController) Create(c *gin.Context) { resource.TxHash = msg.TxHash resource.State = string(msg.State) - tc.App.GetLogger().Audit(logger.TERRA_TRANSACTION_CREATED, map[string]interface{}{ + tc.App.GetLogger().Audit(audit.TerraTransactionCreated, map[string]interface{}{ "terraTransactionResource": resource, }) diff --git a/core/web/user_controller.go b/core/web/user_controller.go index 56e477293f8..0d6db6b55a7 100644 --- a/core/web/user_controller.go +++ b/core/web/user_controller.go @@ -9,7 +9,7 @@ import ( "github.com/gin-gonic/gin" "github.com/smartcontractkit/chainlink/core/auth" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" clsession "github.com/smartcontractkit/chainlink/core/sessions" "github.com/smartcontractkit/chainlink/core/utils" @@ -43,7 +43,7 @@ func (c *UserController) UpdatePassword(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.OldPassword, user.HashedPassword) { - c.App.GetLogger().Audit(logger.PASSWORD_RESET_ATTEMPT_FAILED_MISMATCH, map[string]interface{}{"user": user.Email}) + c.App.GetLogger().Audit(audit.PasswordResetAttemptFailedMismatch, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusConflict, errors.New("old password does not match")) return } @@ -52,7 +52,7 @@ func (c *UserController) UpdatePassword(ctx *gin.Context) { return } - c.App.GetLogger().Audit(logger.PASSWORD_RESET_SUCCESS, map[string]interface{}{"user": user.Email}) + c.App.GetLogger().Audit(audit.PasswordResetSuccess, map[string]interface{}{"user": user.Email}) jsonAPIResponse(ctx, presenters.NewUserResource(user), "user") } @@ -70,7 +70,7 @@ func (c *UserController) NewAPIToken(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.Password, user.HashedPassword) { - c.App.GetLogger().Audit(logger.API_TOKEN_CREATE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": user.Email}) + c.App.GetLogger().Audit(audit.APITokenCreateAttemptPasswordMismatch, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusUnauthorized, errors.New("incorrect password")) return } @@ -80,7 +80,7 @@ func (c *UserController) NewAPIToken(ctx *gin.Context) { return } - c.App.GetLogger().Audit(logger.API_TOKEN_CREATED, map[string]interface{}{"user": user.Email}) + c.App.GetLogger().Audit(audit.APITokenCreated, map[string]interface{}{"user": user.Email}) jsonAPIResponseWithStatus(ctx, newToken, "auth_token", http.StatusCreated) } @@ -98,7 +98,7 @@ func (c *UserController) DeleteAPIToken(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.Password, user.HashedPassword) { - c.App.GetLogger().Audit(logger.API_TOKEN_DELETE_ATTEMPT_PASSWORD_MISMATCH, map[string]interface{}{"user": user.Email}) + c.App.GetLogger().Audit(audit.APITokenDeleteAttemptPasswordMismatch, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusUnauthorized, errors.New("incorrect password")) return } @@ -107,7 +107,7 @@ func (c *UserController) DeleteAPIToken(ctx *gin.Context) { return } { - c.App.GetLogger().Audit(logger.API_TOKEN_DELETED, map[string]interface{}{"user": user.Email}) + c.App.GetLogger().Audit(audit.APITokenDeleted, map[string]interface{}{"user": user.Email}) jsonAPIResponseWithStatus(ctx, nil, "auth_token", http.StatusNoContent) } } diff --git a/core/web/vrf_keys_controller.go b/core/web/vrf_keys_controller.go index c10ffad9fe6..81625331066 100644 --- a/core/web/vrf_keys_controller.go +++ b/core/web/vrf_keys_controller.go @@ -6,7 +6,7 @@ import ( "github.com/gin-gonic/gin" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/web/presenters" ) @@ -38,7 +38,7 @@ func (vrfkc *VRFKeysController) Create(c *gin.Context) { return } - vrfkc.App.GetLogger().Audit(logger.VRF_KEY_CREATED, map[string]interface{}{ + vrfkc.App.GetLogger().Audit(audit.VRFKeyCreated, map[string]interface{}{ "vrfPublicKey": pk.PublicKey, "vrfID": pk.ID(), "vrfPublicKeyAddress": pk.PublicKey.Address(), @@ -63,7 +63,7 @@ func (vrfkc *VRFKeysController) Delete(c *gin.Context) { return } - vrfkc.App.GetLogger().Audit(logger.VRF_KEY_DELETED, map[string]interface{}{"id": keyID}) + vrfkc.App.GetLogger().Audit(audit.VRFKeyDeleted, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, presenters.NewVRFKeyResource(key, vrfkc.App.GetLogger()), "vrfKey") } @@ -85,7 +85,7 @@ func (vrfkc *VRFKeysController) Import(c *gin.Context) { return } - vrfkc.App.GetLogger().Audit(logger.VRF_KEY_IMPORTED, map[string]interface{}{ + vrfkc.App.GetLogger().Audit(audit.VRFKeyImported, map[string]interface{}{ "vrfID": key.ID(), "vrfPublicKey": key.PublicKey, }) @@ -107,6 +107,6 @@ func (vrfkc *VRFKeysController) Export(c *gin.Context) { return } - vrfkc.App.GetLogger().Audit(logger.VRF_KEY_EXPORTED, map[string]interface{}{"keyID": keyID}) + vrfkc.App.GetLogger().Audit(audit.VRFKeyExported, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/webauthn_controller.go b/core/web/webauthn_controller.go index 64241e6c798..b02eb5d58e9 100644 --- a/core/web/webauthn_controller.go +++ b/core/web/webauthn_controller.go @@ -8,7 +8,7 @@ import ( "github.com/gin-gonic/gin" - "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/sessions" "github.com/smartcontractkit/chainlink/core/web/presenters" @@ -94,7 +94,7 @@ func (c *WebAuthnController) FinishRegistration(ctx *gin.Context) { jsonAPIError(ctx, http.StatusBadRequest, errors.New("registration was unsuccessful")) return } - c.App.GetLogger().Audit(logger.AUTH_2FA_ENROLLED, map[string]interface{}{"email": user.Email, "credential": string(credj)}) + c.App.GetLogger().Audit(audit.Auth2FAEnrolled, map[string]interface{}{"email": user.Email, "credential": string(credj)}) ctx.String(http.StatusOK, "{}") } From d7ef189196de27d090749d61b4471d82de5ffd09 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Mon, 20 Jun 2022 03:48:59 -0700 Subject: [PATCH 07/92] Update generated mock code --- core/logger/logger_mock_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/logger/logger_mock_test.go b/core/logger/logger_mock_test.go index 2cf93ade083..38ed377b414 100644 --- a/core/logger/logger_mock_test.go +++ b/core/logger/logger_mock_test.go @@ -5,9 +5,10 @@ package logger import ( io "io" - "github.com/smartcontractkit/chainlink/core/logger/audit" + audit "github.com/smartcontractkit/chainlink/core/logger/audit" mock "github.com/stretchr/testify/mock" + zapcore "go.uber.org/zap/zapcore" ) From 51a3301e105fa37df9b8cc14d618fb141aebea09 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Wed, 29 Jun 2022 00:22:23 -0700 Subject: [PATCH 08/92] rework splunk logger forwarder goroutine per functionc all, implement single thread buffer push channels, fix getLocalIP comment --- core/logger/splunk.go | 72 ++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/core/logger/splunk.go b/core/logger/splunk.go index 9c8ae353596..c03b301af64 100644 --- a/core/logger/splunk.go +++ b/core/logger/splunk.go @@ -16,40 +16,76 @@ import ( ) type splunkLogger struct { - logger Logger - splunkToken string - splunkURL string - environmentName string - hostname string - localIP string + logger Logger + logBuffer chan splunkLogItem + serviceDoneSignal chan struct{} + splunkToken string + splunkURL string + environmentName string + hostname string + localIP string +} + +type splunkLogItem struct { + eventID audit.EventID + data map[string]interface{} } func newSplunkLogger(logger Logger, splunkToken string, splunkURL string, hostname string, environment string) Logger { + // This logger implements a single goroutine buffer/queue system to enable fire and forget + // dispatch of audit log events within web controllers. The async http post to collector has + // a timeout of 30 seconds, so internal API responses won't hang indefinitely. + logBuff := make(chan splunkLogItem, 10) + doneSignal := make(chan struct{}) + + sLogger := splunkLogger{ + logger: logger.Helper(1), + logBuffer: logBuff, + serviceDoneSignal: doneSignal, + splunkToken: splunkToken, + splunkURL: splunkURL, + environmentName: environment, + hostname: hostname, + localIP: getLocalIP(), + } + + // Start single async forwarder thread + go sLogger.StartLogForwarder() + // Initialize and return Splunk logger struct with required state for HEC calls - return &splunkLogger{ - logger: logger.Helper(1), - splunkToken: splunkToken, - splunkURL: splunkURL, - environmentName: environment, - hostname: hostname, - localIP: getLocalIP(), + return &sLogger +} + +// StartLogForwarder is a goroutine with buffer limit to process and forward log events async +func (l *splunkLogger) StartLogForwarder() { + for { + select { + case event := <-l.logBuffer: + l.postLogToSplunk(event.eventID, event.data) + case <-l.serviceDoneSignal: + return + } } } func (l *splunkLogger) Audit(eventID audit.EventID, data map[string]interface{}) { - // goroutine to async POST to splunk HTTP Event Collector (HEC) - go l.postLogToSplunk(eventID, data) + // Queue event data in logBuffer for forwarder to pick up and send + event := splunkLogItem{ + eventID: eventID, + data: data, + } + l.logBuffer <- event l.logger.Audit(eventID, data) } -// getLocalIP returns the first non- loopback local IP of the host +// getLocalIP returns the first non-loopback local IP of the host func getLocalIP() string { addrs, err := net.InterfaceAddrs() if err != nil { return "" } for _, address := range addrs { - // check the address type and if it is not a loopback the display it + // filter and return address types for first non loopback address if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { if ipnet.IP.To4() != nil { return ipnet.IP.String() @@ -77,7 +113,7 @@ func (l *splunkLogger) postLogToSplunk(eventID audit.EventID, data map[string]in serializedSplunkLog, _ := json.Marshal(splunkLog) // Send up to HEC log collector - httpClient := &http.Client{Timeout: time.Second * 60} + httpClient := &http.Client{Timeout: time.Second * 30} req, _ := http.NewRequest("POST", l.splunkURL, bytes.NewReader(serializedSplunkLog)) req.Header.Add("Authorization", "Splunk "+l.splunkToken) resp, err := httpClient.Do(req) From ce7a4463efcf8a89777127740c00c9fc4803369a Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Wed, 29 Jun 2022 10:48:34 -0700 Subject: [PATCH 09/92] Handle serialize error for chains, move comment/documentation --- core/logger/logger.go | 4 +--- core/logger/splunk.go | 6 +++++- core/web/chains_controller.go | 10 ++++++++-- core/web/resolver/mutation.go | 10 ++++++++-- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/core/logger/logger.go b/core/logger/logger.go index c3e72d2840f..3b6eca5f405 100644 --- a/core/logger/logger.go +++ b/core/logger/logger.go @@ -79,9 +79,7 @@ type Logger interface { // exit uncleanly Fatal(args ...interface{}) - // The .Audit function here is specific to the SplunkLogger implementation - // It is added to the interface to ensure that audit logs are sent regardless of log level. - // All other Logger implementations should continue the pattern of propogating wrapped logger calls + // Audit logs an audit event, regardless of level. Audit(eventID audit.EventID, data map[string]interface{}) Tracef(format string, values ...interface{}) diff --git a/core/logger/splunk.go b/core/logger/splunk.go index c03b301af64..0a904620582 100644 --- a/core/logger/splunk.go +++ b/core/logger/splunk.go @@ -31,6 +31,9 @@ type splunkLogItem struct { data map[string]interface{} } +// The .Audit function in the logger interface is exclusively used by this SplunkLogger implementation. +// .Auditf function implementations should continue the pattern. Audit logs here must be emitted +// regardless of log level, hence the separate 'Audit' log level func newSplunkLogger(logger Logger, splunkToken string, splunkURL string, hostname string, environment string) Logger { // This logger implements a single goroutine buffer/queue system to enable fire and forget // dispatch of audit log events within web controllers. The async http post to collector has @@ -74,7 +77,8 @@ func (l *splunkLogger) Audit(eventID audit.EventID, data map[string]interface{}) eventID: eventID, data: data, } - l.logBuffer <- event + // l.logBuffer <- event + l.postLogToSplunk(event.eventID, event.data) // TODO: Andrew l.logger.Audit(eventID, data) } diff --git a/core/web/chains_controller.go b/core/web/chains_controller.go index 6c442fb4bee..e6240854aa9 100644 --- a/core/web/chains_controller.go +++ b/core/web/chains_controller.go @@ -96,7 +96,10 @@ func (cc *chainsController[I, C, R]) Create(c *gin.Context) { return } - chainj, _ := json.Marshal(chain) + chainj, err := json.Marshal(chain) + if err != nil { + cc.lggr.Errorf("Unable to marshal chain to json", "err", err) + } cc.lggr.Audit(audit.ChainAdded, map[string]interface{}{"chain": chainj}) jsonAPIResponseWithStatus(c, cc.newResource(chain), cc.resourceName, http.StatusCreated) @@ -153,7 +156,10 @@ func (cc *chainsController[I, C, R]) Update(c *gin.Context) { return } - chainj, _ := json.Marshal(chain) + chainj, err := json.Marshal(chain) + if err != nil { + cc.lggr.Errorf("Unable to marshal chain to json", "err", err) + } cc.lggr.Audit(audit.ChainSpecUpdated, map[string]interface{}{"chain": chainj}) jsonAPIResponse(c, cc.newResource(chain), cc.resourceName) diff --git a/core/web/resolver/mutation.go b/core/web/resolver/mutation.go index 047d2c959f7..c864eb29ad8 100644 --- a/core/web/resolver/mutation.go +++ b/core/web/resolver/mutation.go @@ -1070,7 +1070,10 @@ func (r *Resolver) CreateChain(ctx context.Context, args struct { return nil, err } - chainj, _ := json.Marshal(chain) + chainj, err := json.Marshal(chain) + if err != nil { + r.App.GetLogger().Errorf("Unable to marshal chain to json", "err", err) + } r.App.GetLogger().Audit(audit.ChainAdded, map[string]interface{}{"chain": chainj}) return NewCreateChainPayload(&chain, nil), nil @@ -1125,7 +1128,10 @@ func (r *Resolver) UpdateChain(ctx context.Context, args struct { return nil, err } - chainj, _ := json.Marshal(chain) + chainj, err := json.Marshal(chain) + if err != nil { + r.App.GetLogger().Errorf("Unable to marshal chain to json", "err", err) + } r.App.GetLogger().Audit(audit.ChainSpecUpdated, map[string]interface{}{"chainj": chainj}) return NewUpdateChainPayload(&chain, nil, nil), nil From 3afe925f2a7514dbb972473cb855b3f18f35be29 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Thu, 30 Jun 2022 10:32:05 -0700 Subject: [PATCH 10/92] Rework splunk logger to general http log service logger, add parameterized headers in env var and optional nested key structure. Reworkd audit logger config, now takes function to parse and validate input, raising errors on init instead --- core/logger/audit_logger.go | 287 ++++++++++++++++++++++++++++++++++++ core/logger/logger.go | 54 ++++--- core/logger/splunk.go | 287 ------------------------------------ 3 files changed, 317 insertions(+), 311 deletions(-) create mode 100644 core/logger/audit_logger.go delete mode 100644 core/logger/splunk.go diff --git a/core/logger/audit_logger.go b/core/logger/audit_logger.go new file mode 100644 index 00000000000..b2b88630b24 --- /dev/null +++ b/core/logger/audit_logger.go @@ -0,0 +1,287 @@ +package logger + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "strings" + "time" + + "github.com/pkg/errors" + + "github.com/smartcontractkit/chainlink/core/logger/audit" + + "go.uber.org/zap/zapcore" +) + +type auditLogger struct { + logger Logger + AuditLoggerConfig +} + +type AuditLoggerConfig struct { + serviceURL string + serviceHeaders []serviceHeader + jsonWrapperKey string + environmentName string + hostname string + localIP string +} + +// Configurable headers to include in POST to log service +type serviceHeader struct { + header string + value string +} + +// NewAuditLoggerConfig parses and validatesthe passed AUDIT_LOGS_* environment values and populates fields +func NewAuditLoggerConfig(serviceURL string, headersEncoded, jsonWrapperKey, hostname, environment string) (AuditLoggerConfig, error) { + newConfig := AuditLoggerConfig{} + + // Split and prepare optional service client headers from env variable + headers := []serviceHeader{} + if headersEncoded != "" { + headerLines := strings.Split(headersEncoded, "\\") + for _, header := range headerLines { + keyValue := strings.Split(header, "||") + if len(keyValue) != 2 { + return AuditLoggerConfig{}, errors.Errorf("Invalid AUDIT_LOGS_FORWARDER_HEADERS value, single pair split on || required, got: %s", keyValue) + } + headers = append(headers, serviceHeader{ + header: keyValue[0], + value: keyValue[1], + }) + } + } + + newConfig.serviceURL = serviceURL + newConfig.serviceHeaders = headers + newConfig.jsonWrapperKey = jsonWrapperKey + newConfig.environmentName = environment + newConfig.hostname = hostname + newConfig.localIP = getLocalIP() + + return newConfig, nil +} + +// The .Audit function in the logger interface is exclusively used by this AuditLogger implementation. +// .Auditf function implementations should continue the pattern. Audit logs here must be emitted +// regardless of log level, hence the separate 'Audit' log level +// Audit log events are posted up to with an HTTP forwarder +func newAuditLogger(auditLoggerCfg AuditLoggerConfig, logger Logger) Logger { + sLogger := auditLogger{ + logger: logger.Helper(1), + } + sLogger.AuditLoggerConfig = auditLoggerCfg + return &sLogger +} + +func (l *auditLogger) Audit(eventID audit.EventID, data map[string]interface{}) { + l.postLogToLogService(eventID, data) + l.logger.Audit(eventID, data) +} + +// getLocalIP returns the first non-loopback local IP of the host +func getLocalIP() string { + addrs, err := net.InterfaceAddrs() + if err != nil { + return "" + } + for _, address := range addrs { + // filter and return address types for first non loopback address + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + return ipnet.IP.String() + } + } + } + return "" +} + +func (l *auditLogger) postLogToLogService(eventID audit.EventID, data map[string]interface{}) { + // Audit log JSON data + logItem := map[string]interface{}{ + "eventID": eventID, + "hostname": l.hostname, + "localIP": l.localIP, + "env": l.environmentName, + "data": data, + } + + // Optionally wrap audit log data into JSON object to help dynamically structure for an HTTP log service call + if l.jsonWrapperKey != "" { + logItem = map[string]interface{}{l.jsonWrapperKey: logItem} + } + + serializedLog, err := json.Marshal(logItem) + if err != nil { + l.logger.Errorw("Unable to serialize wrapped audit log item to JSON", "err", err, "logItem", logItem) + return + } + + // Send up to HEC log collector + httpClient := &http.Client{Timeout: time.Second * 10} + req, _ := http.NewRequest("POST", l.serviceURL, bytes.NewReader(serializedLog)) + for _, header := range l.serviceHeaders { + req.Header.Add(header.header, header.value) + } + resp, err := httpClient.Do(req) + if err != nil { + l.logger.Errorw("Failed to send audit log to HTTP log service", "err", err, "logItem", logItem) + return + } + if resp.StatusCode != 200 { + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + l.logger.Errorw("Error reading errored HTTP log service webhook response body", "err", err, "logItem", logItem) + return + } + l.logger.Errorw("Error sending log to HTTP log service", "statusCode", resp.StatusCode, "bodyString", string(bodyBytes)) + return + } +} + +func (l *auditLogger) With(args ...interface{}) Logger { + return &auditLogger{ + logger: l.logger.With(args...), + AuditLoggerConfig: l.AuditLoggerConfig, + } +} + +func (l *auditLogger) Named(name string) Logger { + return &auditLogger{ + logger: l.logger.Named(name), + AuditLoggerConfig: l.AuditLoggerConfig, + } +} + +func (l *auditLogger) SetLogLevel(level zapcore.Level) { + l.logger.SetLogLevel(level) +} + +func (l *auditLogger) Trace(args ...interface{}) { + l.logger.Trace(args...) +} + +func (l *auditLogger) Debug(args ...interface{}) { + l.logger.Debug(args...) +} + +func (l *auditLogger) Info(args ...interface{}) { + l.logger.Info(args...) +} + +func (l *auditLogger) Warn(args ...interface{}) { + l.logger.Warn(args...) +} + +func (l *auditLogger) Error(args ...interface{}) { + l.logger.Error(args...) +} + +func (l *auditLogger) Critical(args ...interface{}) { + l.logger.Critical(args...) +} + +func (l *auditLogger) Panic(args ...interface{}) { + l.logger.Panic(args...) +} + +func (l *auditLogger) Fatal(args ...interface{}) { + l.logger.Fatal(args...) +} + +func (l *auditLogger) Tracef(format string, values ...interface{}) { + l.logger.Tracef(format, values...) +} + +func (l *auditLogger) Debugf(format string, values ...interface{}) { + l.logger.Debugf(format, values...) +} + +func (l *auditLogger) Infof(format string, values ...interface{}) { + l.logger.Infof(format, values...) +} + +func (l *auditLogger) Warnf(format string, values ...interface{}) { + l.logger.Warnf(format, values...) +} + +func (l *auditLogger) Errorf(format string, values ...interface{}) { + l.logger.Errorf(format, values...) +} + +func (l *auditLogger) Criticalf(format string, values ...interface{}) { + l.logger.Criticalf(format, values...) +} + +func (l *auditLogger) Panicf(format string, values ...interface{}) { + l.logger.Panicf(format, values...) +} + +func (l *auditLogger) Fatalf(format string, values ...interface{}) { + l.logger.Fatalf(format, values...) +} + +func (l *auditLogger) Tracew(msg string, keysAndValues ...interface{}) { + l.logger.Tracew(msg, keysAndValues...) +} + +func (l *auditLogger) Debugw(msg string, keysAndValues ...interface{}) { + l.logger.Debugw(msg, keysAndValues...) +} + +func (l *auditLogger) Infow(msg string, keysAndValues ...interface{}) { + l.logger.Infow(msg, keysAndValues...) +} + +func (l *auditLogger) Warnw(msg string, keysAndValues ...interface{}) { + l.logger.Warnw(msg, keysAndValues...) +} + +func (l *auditLogger) Errorw(msg string, keysAndValues ...interface{}) { + l.logger.Errorw(msg, keysAndValues...) +} + +func (l *auditLogger) Criticalw(msg string, keysAndValues ...interface{}) { + l.logger.Criticalw(msg, keysAndValues...) +} + +func (l *auditLogger) Panicw(msg string, keysAndValues ...interface{}) { + l.logger.Panicw(msg, keysAndValues...) +} + +func (l *auditLogger) Fatalw(msg string, keysAndValues ...interface{}) { + l.logger.Fatalw(msg, keysAndValues...) +} + +func (l *auditLogger) ErrorIf(err error, msg string) { + if err != nil { + l.logger.Errorw(msg, "err", err) + } +} + +func (l *auditLogger) ErrorIfClosing(c io.Closer, name string) { + if err := c.Close(); err != nil { + l.logger.Errorw(fmt.Sprintf("Error closing %s", name), "err", err) + } +} + +func (l *auditLogger) Sync() error { + return l.logger.Sync() +} + +func (l *auditLogger) Helper(add int) Logger { + return &auditLogger{ + logger: l.logger.Helper(add), + AuditLoggerConfig: l.AuditLoggerConfig, + } +} + +func (l *auditLogger) Recover(panicErr interface{}) { + l.logger.Recover(panicErr) +} diff --git a/core/logger/logger.go b/core/logger/logger.go index 3b6eca5f405..4ddcdd205c7 100644 --- a/core/logger/logger.go +++ b/core/logger/logger.go @@ -207,25 +207,34 @@ func NewLogger() (Logger, func() error) { } } - // Set Splunk values in Config if any defined in env - c.SplunkToken = os.Getenv("SPLUNK_TOKEN") - if c.SplunkToken != "" { - c.SplunkURL = os.Getenv("SPLUNK_URL") - if c.SplunkURL == "" { - errString := "Misconfigured Splunk environment variables set. SPLUNK_URL is required if SPLUNK_TOKEN is not empty, unset the SPLUNK_URL environment variable or set SPLUNK_TOKEN to enable Splunk audit logging." + // Set optional Audit Logger values in Config if any defined in env + // Disabled on empty config + auditLogsURL := os.Getenv("AUDIT_LOGS_FORWARDER_URL") + if auditLogsURL != "" { + // Audit logger environment variables set, enable by initializing and storing config + env := "production" + if os.Getenv("CHAINLINK_DEV") == "true" { + env = "develop" + } + hostname, err := os.Hostname() + if err != nil { + errString := fmt.Sprintf("Error get hostname for Logger config: %s", err) parseErrs = append(parseErrs, errString) - c.SplunkToken = "" } + auditCfg, err := NewAuditLoggerConfig( + auditLogsURL, + os.Getenv("AUDIT_LOGS_FORWARDER_HEADERS"), + os.Getenv("AUDIT_LOGS_FORWARDER_JSON_WRAPPER_KEY"), + hostname, + env, + ) + if err != nil { + errString := fmt.Sprintf("Error creating Audit Logger: %s.", err) + parseErrs = append(parseErrs, errString) + } + c.AuditConfig = auditCfg } - c.ChainlinkDev = os.Getenv("CHAINLINK_DEV") == "true" - hostname, err := os.Hostname() - if err != nil { - errString := fmt.Sprintf("Error get hostname for Logger config: %s", err) - parseErrs = append(parseErrs, errString) - } - c.Hostname = hostname - c.UnixTS, invalid = envvar.LogUnixTS.Parse() if invalid != "" { parseErrs = append(parseErrs, invalid) @@ -251,8 +260,7 @@ type Config struct { FileMaxBackups int // files Hostname string ChainlinkDev bool - SplunkToken string // enables splunk logging if not empty - SplunkURL string + AuditConfig AuditLoggerConfig } // New returns a new Logger with pretty printing to stdout, prometheus counters, and sentry forwarding. @@ -271,13 +279,11 @@ func (c *Config) New() (Logger, func() error) { l = newSentryLogger(l) l = newPrometheusLogger(l) - // If Splunk logging is enabled (token set), extend/wrap the logger with a splunkLogger instance - if c.SplunkToken != "" { - env := "production" - if c.ChainlinkDev { - env = "develop" - } - l = newSplunkLogger(l, c.SplunkToken, c.SplunkURL, c.Hostname, env) + + // If Audit Logging HTTP forwarder is enabled if config is populated with a URL + // Extend/wrap the logger with an auditLogger instance + if c.AuditConfig.serviceURL != "" { + l = newAuditLogger(c.AuditConfig, l) } return l, close } diff --git a/core/logger/splunk.go b/core/logger/splunk.go deleted file mode 100644 index 0a904620582..00000000000 --- a/core/logger/splunk.go +++ /dev/null @@ -1,287 +0,0 @@ -package logger - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net" - "net/http" - "time" - - "github.com/smartcontractkit/chainlink/core/logger/audit" - - "go.uber.org/zap/zapcore" -) - -type splunkLogger struct { - logger Logger - logBuffer chan splunkLogItem - serviceDoneSignal chan struct{} - splunkToken string - splunkURL string - environmentName string - hostname string - localIP string -} - -type splunkLogItem struct { - eventID audit.EventID - data map[string]interface{} -} - -// The .Audit function in the logger interface is exclusively used by this SplunkLogger implementation. -// .Auditf function implementations should continue the pattern. Audit logs here must be emitted -// regardless of log level, hence the separate 'Audit' log level -func newSplunkLogger(logger Logger, splunkToken string, splunkURL string, hostname string, environment string) Logger { - // This logger implements a single goroutine buffer/queue system to enable fire and forget - // dispatch of audit log events within web controllers. The async http post to collector has - // a timeout of 30 seconds, so internal API responses won't hang indefinitely. - logBuff := make(chan splunkLogItem, 10) - doneSignal := make(chan struct{}) - - sLogger := splunkLogger{ - logger: logger.Helper(1), - logBuffer: logBuff, - serviceDoneSignal: doneSignal, - splunkToken: splunkToken, - splunkURL: splunkURL, - environmentName: environment, - hostname: hostname, - localIP: getLocalIP(), - } - - // Start single async forwarder thread - go sLogger.StartLogForwarder() - - // Initialize and return Splunk logger struct with required state for HEC calls - return &sLogger -} - -// StartLogForwarder is a goroutine with buffer limit to process and forward log events async -func (l *splunkLogger) StartLogForwarder() { - for { - select { - case event := <-l.logBuffer: - l.postLogToSplunk(event.eventID, event.data) - case <-l.serviceDoneSignal: - return - } - } -} - -func (l *splunkLogger) Audit(eventID audit.EventID, data map[string]interface{}) { - // Queue event data in logBuffer for forwarder to pick up and send - event := splunkLogItem{ - eventID: eventID, - data: data, - } - // l.logBuffer <- event - l.postLogToSplunk(event.eventID, event.data) // TODO: Andrew - l.logger.Audit(eventID, data) -} - -// getLocalIP returns the first non-loopback local IP of the host -func getLocalIP() string { - addrs, err := net.InterfaceAddrs() - if err != nil { - return "" - } - for _, address := range addrs { - // filter and return address types for first non loopback address - if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil { - return ipnet.IP.String() - } - } - } - return "" -} - -func (l *splunkLogger) postLogToSplunk(eventID audit.EventID, data map[string]interface{}) { - // Splunk JSON data - splunkLog := map[string]interface{}{ - "eventID": eventID, - "hostname": l.hostname, - "localIP": l.localIP, - "env": l.environmentName, - } - if len(data) != 0 { - splunkLog["data"] = data - } - - // Wrap serialized audit log map into JSON object `event` for API call - serializedArgs, _ := json.Marshal(splunkLog) - splunkLog = map[string]interface{}{"event": string(serializedArgs)} - serializedSplunkLog, _ := json.Marshal(splunkLog) - - // Send up to HEC log collector - httpClient := &http.Client{Timeout: time.Second * 30} - req, _ := http.NewRequest("POST", l.splunkURL, bytes.NewReader(serializedSplunkLog)) - req.Header.Add("Authorization", "Splunk "+l.splunkToken) - resp, err := httpClient.Do(req) - if err != nil { - l.logger.Errorw("Failed to send audit log to Splunk", "err", err, "splunkLog", splunkLog) - } - if resp.StatusCode != 200 { - bodyBytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - l.logger.Errorw("Error reading errored Splunk webhook response body", "err", err, "splunkLog", splunkLog) - } - l.logger.Errorw("Error sending log to Splunk", "statusCode", resp.StatusCode, "bodyString", string(bodyBytes)) - } -} - -func (l *splunkLogger) With(args ...interface{}) Logger { - return &splunkLogger{ - logger: l.logger.With(args...), - splunkToken: l.splunkToken, - splunkURL: l.splunkURL, - environmentName: l.environmentName, - hostname: l.hostname, - localIP: getLocalIP(), - } -} - -func (l *splunkLogger) Named(name string) Logger { - return &splunkLogger{ - logger: l.logger.Named(name), - splunkToken: l.splunkToken, - splunkURL: l.splunkURL, - environmentName: l.environmentName, - hostname: l.hostname, - localIP: getLocalIP(), - } -} - -func (l *splunkLogger) SetLogLevel(level zapcore.Level) { - l.logger.SetLogLevel(level) -} - -func (l *splunkLogger) Trace(args ...interface{}) { - l.logger.Trace(args...) -} - -func (l *splunkLogger) Debug(args ...interface{}) { - l.logger.Debug(args...) -} - -func (l *splunkLogger) Info(args ...interface{}) { - l.logger.Info(args...) -} - -func (l *splunkLogger) Warn(args ...interface{}) { - l.logger.Warn(args...) -} - -func (l *splunkLogger) Error(args ...interface{}) { - l.logger.Error(args...) -} - -func (l *splunkLogger) Critical(args ...interface{}) { - l.logger.Critical(args...) -} - -func (l *splunkLogger) Panic(args ...interface{}) { - l.logger.Panic(args...) -} - -func (l *splunkLogger) Fatal(args ...interface{}) { - l.logger.Fatal(args...) -} - -func (l *splunkLogger) Tracef(format string, values ...interface{}) { - l.logger.Tracef(format, values...) -} - -func (l *splunkLogger) Debugf(format string, values ...interface{}) { - l.logger.Debugf(format, values...) -} - -func (l *splunkLogger) Infof(format string, values ...interface{}) { - l.logger.Infof(format, values...) -} - -func (l *splunkLogger) Warnf(format string, values ...interface{}) { - l.logger.Warnf(format, values...) -} - -func (l *splunkLogger) Errorf(format string, values ...interface{}) { - l.logger.Errorf(format, values...) -} - -func (l *splunkLogger) Criticalf(format string, values ...interface{}) { - l.logger.Criticalf(format, values...) -} - -func (l *splunkLogger) Panicf(format string, values ...interface{}) { - l.logger.Panicf(format, values...) -} - -func (l *splunkLogger) Fatalf(format string, values ...interface{}) { - l.logger.Fatalf(format, values...) -} - -func (l *splunkLogger) Tracew(msg string, keysAndValues ...interface{}) { - l.logger.Tracew(msg, keysAndValues...) -} - -func (l *splunkLogger) Debugw(msg string, keysAndValues ...interface{}) { - l.logger.Debugw(msg, keysAndValues...) -} - -func (l *splunkLogger) Infow(msg string, keysAndValues ...interface{}) { - l.logger.Infow(msg, keysAndValues...) -} - -func (l *splunkLogger) Warnw(msg string, keysAndValues ...interface{}) { - l.logger.Warnw(msg, keysAndValues...) -} - -func (l *splunkLogger) Errorw(msg string, keysAndValues ...interface{}) { - l.logger.Errorw(msg, keysAndValues...) -} - -func (l *splunkLogger) Criticalw(msg string, keysAndValues ...interface{}) { - l.logger.Criticalw(msg, keysAndValues...) -} - -func (l *splunkLogger) Panicw(msg string, keysAndValues ...interface{}) { - l.logger.Panicw(msg, keysAndValues...) -} - -func (l *splunkLogger) Fatalw(msg string, keysAndValues ...interface{}) { - l.logger.Fatalw(msg, keysAndValues...) -} - -func (l *splunkLogger) ErrorIf(err error, msg string) { - if err != nil { - l.logger.Errorw(msg, "err", err) - } -} - -func (l *splunkLogger) ErrorIfClosing(c io.Closer, name string) { - if err := c.Close(); err != nil { - l.logger.Errorw(fmt.Sprintf("Error closing %s", name), "err", err) - } -} - -func (l *splunkLogger) Sync() error { - return l.logger.Sync() -} - -func (l *splunkLogger) Helper(add int) Logger { - return &splunkLogger{ - logger: l.logger.Helper(add), - splunkToken: l.splunkToken, - splunkURL: l.splunkURL, - environmentName: l.environmentName, - hostname: l.hostname, - localIP: getLocalIP(), - } -} - -func (l *splunkLogger) Recover(panicErr interface{}) { - l.logger.Recover(panicErr) -} From a7e9c9ef923bd174e81650caa4ff8d7878b77ca0 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Thu, 30 Jun 2022 11:04:39 -0700 Subject: [PATCH 11/92] WIP - rework audit logger to top level Application struct --- core/logger/{ => audit}/audit_logger.go | 13 ++++++------- core/logger/logger.go | 4 ---- core/services/chainlink/application.go | 1 + 3 files changed, 7 insertions(+), 11 deletions(-) rename core/logger/{ => audit}/audit_logger.go (97%) diff --git a/core/logger/audit_logger.go b/core/logger/audit/audit_logger.go similarity index 97% rename from core/logger/audit_logger.go rename to core/logger/audit/audit_logger.go index b2b88630b24..4a11b2ed931 100644 --- a/core/logger/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -1,4 +1,4 @@ -package logger +package audit import ( "bytes" @@ -13,13 +13,12 @@ import ( "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/core/logger/audit" - + "github.com/smartcontractkit/chainlink/core/logger" "go.uber.org/zap/zapcore" ) -type auditLogger struct { - logger Logger +type AuditLogger struct { + logger logger.Logger AuditLoggerConfig } @@ -72,8 +71,8 @@ func NewAuditLoggerConfig(serviceURL string, headersEncoded, jsonWrapperKey, hos // .Auditf function implementations should continue the pattern. Audit logs here must be emitted // regardless of log level, hence the separate 'Audit' log level // Audit log events are posted up to with an HTTP forwarder -func newAuditLogger(auditLoggerCfg AuditLoggerConfig, logger Logger) Logger { - sLogger := auditLogger{ +func newAuditLogger(auditLoggerCfg AuditLoggerConfig, logger Logger) AuditLogger { + sLogger := AuditLogger{ logger: logger.Helper(1), } sLogger.AuditLoggerConfig = auditLoggerCfg diff --git a/core/logger/logger.go b/core/logger/logger.go index 4ddcdd205c7..84c6726b679 100644 --- a/core/logger/logger.go +++ b/core/logger/logger.go @@ -11,7 +11,6 @@ import ( "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink/core/config/envvar" - "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/static" "github.com/smartcontractkit/chainlink/core/utils" ) @@ -79,9 +78,6 @@ type Logger interface { // exit uncleanly Fatal(args ...interface{}) - // Audit logs an audit event, regardless of level. - Audit(eventID audit.EventID, data map[string]interface{}) - Tracef(format string, values ...interface{}) Debugf(format string, values ...interface{}) Infof(format string, values ...interface{}) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 8dfb734a823..0151a796fdc 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -126,6 +126,7 @@ type ChainlinkApplication struct { HealthChecker services.Checker Nurse *services.Nurse logger logger.Logger + auditLogger audit.AuditLogger closeLogger func() error sqlxDB *sqlx.DB From dbda04bcd1038c6a08a0aae320c0e7b5e88987e4 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Thu, 30 Jun 2022 23:59:28 -0700 Subject: [PATCH 12/92] Rework audit logger system as standalone interface and struct instead of nesting and working into the current Logger system. This is a better implementation because it does not polite the logger interface (adding .Audit that only this implementation uses), and does not require special wrapping Previously the audit logger would just forward through all other function calls. Now the auditLogger is a sibling field adjacent to the logger field in the Application struct. It is parameterized and passed through the call stack. Update call sites to pass context for threaded calls and move auditLogger to its own package --- core/logger/audit/audit_logger.go | 251 +++++------------- core/logger/logger.go | 35 --- core/logger/logger_mock_test.go | 7 - core/logger/null_logger.go | 18 +- core/logger/prometheus.go | 6 - core/logger/sentry.go | 5 - core/logger/zap.go | 3 - core/services/chainlink/application.go | 15 +- core/sessions/orm.go | 16 +- core/web/bridge_types_controller.go | 7 +- core/web/chains_controller.go | 10 +- core/web/config_controller.go | 4 +- core/web/csa_keys_controller.go | 6 +- core/web/eth_keys_controller.go | 10 +- core/web/evm_chains_controller.go | 2 +- core/web/evm_forwarders_controller.go | 4 +- core/web/evm_nodes_controller.go | 1 + core/web/evm_transfer_controller.go | 2 +- core/web/external_initiators_controller.go | 4 +- core/web/jobs_controller.go | 4 +- core/web/keys_controller.go | 20 +- core/web/log_controller.go | 6 +- core/web/nodes_controller.go | 7 +- core/web/ocr2_keys_controller.go | 8 +- core/web/ocr_keys_controller.go | 8 +- core/web/p2p_keys_controller.go | 8 +- .../pipeline_job_spec_errors_controller.go | 2 +- core/web/pipeline_runs_controller.go | 2 +- core/web/resolver/mutation.go | 80 +++--- core/web/sessions_controller.go | 2 +- core/web/solana_chains_controller.go | 2 +- core/web/solana_keys_controller.go | 2 +- core/web/solana_nodes_controller.go | 1 + core/web/solana_transfer_controller.go | 2 +- core/web/terra_chains_controller.go | 2 +- core/web/terra_keys_controller.go | 2 +- core/web/terra_nodes_controller.go | 1 + core/web/terra_transfer_controller.go | 2 +- core/web/user_controller.go | 12 +- core/web/vrf_keys_controller.go | 8 +- core/web/webauthn_controller.go | 2 +- 41 files changed, 216 insertions(+), 373 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 4a11b2ed931..91ba0da11a3 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -2,27 +2,27 @@ package audit import ( "bytes" + "context" "encoding/json" - "fmt" - "io" "io/ioutil" "net" "net/http" + "os" "strings" "time" - "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/core/logger" - "go.uber.org/zap/zapcore" + + "github.com/pkg/errors" ) -type AuditLogger struct { - logger logger.Logger - AuditLoggerConfig +type AuditLogger interface { + Audit(ctx context.Context, eventID EventID, data map[string]interface{}) } -type AuditLoggerConfig struct { +type auditLogger struct { + logger logger.Logger + enabled bool serviceURL string serviceHeaders []serviceHeader jsonWrapperKey string @@ -37,18 +37,40 @@ type serviceHeader struct { value string } -// NewAuditLoggerConfig parses and validatesthe passed AUDIT_LOGS_* environment values and populates fields -func NewAuditLoggerConfig(serviceURL string, headersEncoded, jsonWrapperKey, hostname, environment string) (AuditLoggerConfig, error) { - newConfig := AuditLoggerConfig{} +// NewAuditLogger returns a buffer push system that ingests audit log events and +// asynchronously pushes them up to an HTTP log service. +// 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) (AuditLogger, error) { + + // Start parsing environment variables for audit logger + auditLogsURL := os.Getenv("AUDIT_LOGS_FORWARDER_URL") + if auditLogsURL == "" { + // Unset, return a disabled audit logger + logger.Info("No AUDIT_LOGS_FORWARDER_URL environment set, audit log events will not be captured") + + return &auditLogger{}, nil + } + + env := "production" + if os.Getenv("CHAINLINK_DEV") == "true" { + env = "develop" + } + hostname, err := os.Hostname() + if err != nil { + return &auditLogger{}, errors.Errorf("Audit Log initialization error - unable to get hostname", "err", err) + } // Split and prepare optional service client headers from env variable headers := []serviceHeader{} + headersEncoded := os.Getenv("AUDIT_LOGS_FORWARDER_HEADERS") if headersEncoded != "" { headerLines := strings.Split(headersEncoded, "\\") for _, header := range headerLines { keyValue := strings.Split(header, "||") if len(keyValue) != 2 { - return AuditLoggerConfig{}, errors.Errorf("Invalid AUDIT_LOGS_FORWARDER_HEADERS value, single pair split on || required, got: %s", keyValue) + return &auditLogger{}, errors.Errorf("Invalid AUDIT_LOGS_FORWARDER_HEADERS value, single pair split on || required, got: %s", keyValue) } headers = append(headers, serviceHeader{ header: keyValue[0], @@ -57,51 +79,28 @@ func NewAuditLoggerConfig(serviceURL string, headersEncoded, jsonWrapperKey, hos } } - newConfig.serviceURL = serviceURL - newConfig.serviceHeaders = headers - newConfig.jsonWrapperKey = jsonWrapperKey - newConfig.environmentName = environment - newConfig.hostname = hostname - newConfig.localIP = getLocalIP() - - return newConfig, nil -} - -// The .Audit function in the logger interface is exclusively used by this AuditLogger implementation. -// .Auditf function implementations should continue the pattern. Audit logs here must be emitted -// regardless of log level, hence the separate 'Audit' log level -// Audit log events are posted up to with an HTTP forwarder -func newAuditLogger(auditLoggerCfg AuditLoggerConfig, logger Logger) AuditLogger { - sLogger := AuditLogger{ - logger: logger.Helper(1), + // Finally, create new auditLogger with parameters + auditLogger := auditLogger{ + logger: logger.Helper(1), + enabled: true, + serviceURL: auditLogsURL, + serviceHeaders: headers, + jsonWrapperKey: os.Getenv("AUDIT_LOGS_FORWARDER_JSON_WRAPPER_KEY"), + environmentName: env, + hostname: hostname, + localIP: getLocalIP(), } - sLogger.AuditLoggerConfig = auditLoggerCfg - return &sLogger + return &auditLogger, nil } -func (l *auditLogger) Audit(eventID audit.EventID, data map[string]interface{}) { - l.postLogToLogService(eventID, data) - l.logger.Audit(eventID, data) -} - -// getLocalIP returns the first non-loopback local IP of the host -func getLocalIP() string { - addrs, err := net.InterfaceAddrs() - if err != nil { - return "" - } - for _, address := range addrs { - // filter and return address types for first non loopback address - if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil { - return ipnet.IP.String() - } - } +func (l *auditLogger) Audit(ctx context.Context, eventID EventID, data map[string]interface{}) { + if !l.enabled { + return } - return "" + l.postLogToLogService(eventID, data) } -func (l *auditLogger) postLogToLogService(eventID audit.EventID, data map[string]interface{}) { +func (l *auditLogger) postLogToLogService(eventID EventID, data map[string]interface{}) { // Audit log JSON data logItem := map[string]interface{}{ "eventID": eventID, @@ -144,143 +143,19 @@ func (l *auditLogger) postLogToLogService(eventID audit.EventID, data map[string } } -func (l *auditLogger) With(args ...interface{}) Logger { - return &auditLogger{ - logger: l.logger.With(args...), - AuditLoggerConfig: l.AuditLoggerConfig, - } -} - -func (l *auditLogger) Named(name string) Logger { - return &auditLogger{ - logger: l.logger.Named(name), - AuditLoggerConfig: l.AuditLoggerConfig, - } -} - -func (l *auditLogger) SetLogLevel(level zapcore.Level) { - l.logger.SetLogLevel(level) -} - -func (l *auditLogger) Trace(args ...interface{}) { - l.logger.Trace(args...) -} - -func (l *auditLogger) Debug(args ...interface{}) { - l.logger.Debug(args...) -} - -func (l *auditLogger) Info(args ...interface{}) { - l.logger.Info(args...) -} - -func (l *auditLogger) Warn(args ...interface{}) { - l.logger.Warn(args...) -} - -func (l *auditLogger) Error(args ...interface{}) { - l.logger.Error(args...) -} - -func (l *auditLogger) Critical(args ...interface{}) { - l.logger.Critical(args...) -} - -func (l *auditLogger) Panic(args ...interface{}) { - l.logger.Panic(args...) -} - -func (l *auditLogger) Fatal(args ...interface{}) { - l.logger.Fatal(args...) -} - -func (l *auditLogger) Tracef(format string, values ...interface{}) { - l.logger.Tracef(format, values...) -} - -func (l *auditLogger) Debugf(format string, values ...interface{}) { - l.logger.Debugf(format, values...) -} - -func (l *auditLogger) Infof(format string, values ...interface{}) { - l.logger.Infof(format, values...) -} - -func (l *auditLogger) Warnf(format string, values ...interface{}) { - l.logger.Warnf(format, values...) -} - -func (l *auditLogger) Errorf(format string, values ...interface{}) { - l.logger.Errorf(format, values...) -} - -func (l *auditLogger) Criticalf(format string, values ...interface{}) { - l.logger.Criticalf(format, values...) -} - -func (l *auditLogger) Panicf(format string, values ...interface{}) { - l.logger.Panicf(format, values...) -} - -func (l *auditLogger) Fatalf(format string, values ...interface{}) { - l.logger.Fatalf(format, values...) -} - -func (l *auditLogger) Tracew(msg string, keysAndValues ...interface{}) { - l.logger.Tracew(msg, keysAndValues...) -} - -func (l *auditLogger) Debugw(msg string, keysAndValues ...interface{}) { - l.logger.Debugw(msg, keysAndValues...) -} - -func (l *auditLogger) Infow(msg string, keysAndValues ...interface{}) { - l.logger.Infow(msg, keysAndValues...) -} - -func (l *auditLogger) Warnw(msg string, keysAndValues ...interface{}) { - l.logger.Warnw(msg, keysAndValues...) -} - -func (l *auditLogger) Errorw(msg string, keysAndValues ...interface{}) { - l.logger.Errorw(msg, keysAndValues...) -} - -func (l *auditLogger) Criticalw(msg string, keysAndValues ...interface{}) { - l.logger.Criticalw(msg, keysAndValues...) -} - -func (l *auditLogger) Panicw(msg string, keysAndValues ...interface{}) { - l.logger.Panicw(msg, keysAndValues...) -} - -func (l *auditLogger) Fatalw(msg string, keysAndValues ...interface{}) { - l.logger.Fatalw(msg, keysAndValues...) -} - -func (l *auditLogger) ErrorIf(err error, msg string) { +// getLocalIP returns the first non-loopback local IP of the host +func getLocalIP() string { + addrs, err := net.InterfaceAddrs() if err != nil { - l.logger.Errorw(msg, "err", err) - } -} - -func (l *auditLogger) ErrorIfClosing(c io.Closer, name string) { - if err := c.Close(); err != nil { - l.logger.Errorw(fmt.Sprintf("Error closing %s", name), "err", err) + return "" } -} - -func (l *auditLogger) Sync() error { - return l.logger.Sync() -} - -func (l *auditLogger) Helper(add int) Logger { - return &auditLogger{ - logger: l.logger.Helper(add), - AuditLoggerConfig: l.AuditLoggerConfig, + for _, address := range addrs { + // filter and return address types for first non loopback address + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + return ipnet.IP.String() + } + } } -} - -func (l *auditLogger) Recover(panicErr interface{}) { - l.logger.Recover(panicErr) + return "" } diff --git a/core/logger/logger.go b/core/logger/logger.go index 84c6726b679..34e55515b60 100644 --- a/core/logger/logger.go +++ b/core/logger/logger.go @@ -203,34 +203,6 @@ func NewLogger() (Logger, func() error) { } } - // Set optional Audit Logger values in Config if any defined in env - // Disabled on empty config - auditLogsURL := os.Getenv("AUDIT_LOGS_FORWARDER_URL") - if auditLogsURL != "" { - // Audit logger environment variables set, enable by initializing and storing config - env := "production" - if os.Getenv("CHAINLINK_DEV") == "true" { - env = "develop" - } - hostname, err := os.Hostname() - if err != nil { - errString := fmt.Sprintf("Error get hostname for Logger config: %s", err) - parseErrs = append(parseErrs, errString) - } - auditCfg, err := NewAuditLoggerConfig( - auditLogsURL, - os.Getenv("AUDIT_LOGS_FORWARDER_HEADERS"), - os.Getenv("AUDIT_LOGS_FORWARDER_JSON_WRAPPER_KEY"), - hostname, - env, - ) - if err != nil { - errString := fmt.Sprintf("Error creating Audit Logger: %s.", err) - parseErrs = append(parseErrs, errString) - } - c.AuditConfig = auditCfg - } - c.UnixTS, invalid = envvar.LogUnixTS.Parse() if invalid != "" { parseErrs = append(parseErrs, invalid) @@ -256,7 +228,6 @@ type Config struct { FileMaxBackups int // files Hostname string ChainlinkDev bool - AuditConfig AuditLoggerConfig } // New returns a new Logger with pretty printing to stdout, prometheus counters, and sentry forwarding. @@ -275,12 +246,6 @@ func (c *Config) New() (Logger, func() error) { l = newSentryLogger(l) l = newPrometheusLogger(l) - - // If Audit Logging HTTP forwarder is enabled if config is populated with a URL - // Extend/wrap the logger with an auditLogger instance - if c.AuditConfig.serviceURL != "" { - l = newAuditLogger(c.AuditConfig, l) - } return l, close } diff --git a/core/logger/logger_mock_test.go b/core/logger/logger_mock_test.go index 38ed377b414..dec717ce1a4 100644 --- a/core/logger/logger_mock_test.go +++ b/core/logger/logger_mock_test.go @@ -5,8 +5,6 @@ package logger import ( io "io" - audit "github.com/smartcontractkit/chainlink/core/logger/audit" - mock "github.com/stretchr/testify/mock" zapcore "go.uber.org/zap/zapcore" @@ -17,11 +15,6 @@ type MockLogger struct { mock.Mock } -// Audit provides a mock function with given fields: eventID, data -func (_m *MockLogger) Audit(eventID audit.EventID, data map[string]interface{}) { - _m.Called(eventID, data) -} - // Critical provides a mock function with given fields: args func (_m *MockLogger) Critical(args ...interface{}) { var _ca []interface{} diff --git a/core/logger/null_logger.go b/core/logger/null_logger.go index e8b096b96db..54ce0df54fd 100644 --- a/core/logger/null_logger.go +++ b/core/logger/null_logger.go @@ -3,7 +3,6 @@ package logger import ( "io" - "github.com/smartcontractkit/chainlink/core/logger/audit" "go.uber.org/zap/zapcore" ) @@ -16,15 +15,14 @@ func (l *nullLogger) With(args ...interface{}) Logger { return l } func (l *nullLogger) Named(name string) Logger { return l } func (l *nullLogger) SetLogLevel(_ zapcore.Level) {} -func (l *nullLogger) Audit(eventID audit.EventID, data map[string]interface{}) {} -func (l *nullLogger) Trace(args ...interface{}) {} -func (l *nullLogger) Debug(args ...interface{}) {} -func (l *nullLogger) Info(args ...interface{}) {} -func (l *nullLogger) Warn(args ...interface{}) {} -func (l *nullLogger) Error(args ...interface{}) {} -func (l *nullLogger) Critical(args ...interface{}) {} -func (l *nullLogger) Panic(args ...interface{}) {} -func (l *nullLogger) Fatal(args ...interface{}) {} +func (l *nullLogger) Trace(args ...interface{}) {} +func (l *nullLogger) Debug(args ...interface{}) {} +func (l *nullLogger) Info(args ...interface{}) {} +func (l *nullLogger) Warn(args ...interface{}) {} +func (l *nullLogger) Error(args ...interface{}) {} +func (l *nullLogger) Critical(args ...interface{}) {} +func (l *nullLogger) Panic(args ...interface{}) {} +func (l *nullLogger) Fatal(args ...interface{}) {} func (l *nullLogger) Tracef(format string, values ...interface{}) {} func (l *nullLogger) Debugf(format string, values ...interface{}) {} diff --git a/core/logger/prometheus.go b/core/logger/prometheus.go index da234d2cdfe..8ddf6776c6a 100644 --- a/core/logger/prometheus.go +++ b/core/logger/prometheus.go @@ -4,8 +4,6 @@ import ( "fmt" "io" - "github.com/smartcontractkit/chainlink/core/logger/audit" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/zap/zapcore" @@ -88,10 +86,6 @@ func (s *prometheusLogger) SetLogLevel(level zapcore.Level) { s.h.SetLogLevel(level) } -func (s *prometheusLogger) Audit(eventID audit.EventID, data map[string]interface{}) { - s.h.Audit(eventID, data) -} - func (s *prometheusLogger) Trace(args ...interface{}) { s.h.Trace(args...) } diff --git a/core/logger/sentry.go b/core/logger/sentry.go index 4abc1ea05ab..d968bd88764 100644 --- a/core/logger/sentry.go +++ b/core/logger/sentry.go @@ -10,7 +10,6 @@ import ( "github.com/getsentry/sentry-go" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/static" ) @@ -88,10 +87,6 @@ func (s *sentryLogger) SetLogLevel(level zapcore.Level) { s.h.SetLogLevel(level) } -func (s *sentryLogger) Audit(eventID audit.EventID, data map[string]interface{}) { - s.h.Audit(eventID, data) -} - func (s *sentryLogger) Trace(args ...interface{}) { s.h.Trace(args...) } diff --git a/core/logger/zap.go b/core/logger/zap.go index 320e04b2d0c..36ec831ec81 100644 --- a/core/logger/zap.go +++ b/core/logger/zap.go @@ -6,7 +6,6 @@ import ( "os" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/core/logger/audit" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) @@ -44,8 +43,6 @@ func (l *zapLogger) With(args ...interface{}) Logger { return &newLogger } -func (l *zapLogger) Audit(eventID audit.EventID, data map[string]interface{}) { /* STUB */ } - // copyFields returns a copy of fields with add appended. func copyFields(fields []interface{}, add ...interface{}) []interface{} { f := make([]interface{}, 0, len(fields)+len(add)) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 0151a796fdc..88d6439227b 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -29,6 +29,7 @@ import ( "github.com/smartcontractkit/chainlink/core/chains/terra" "github.com/smartcontractkit/chainlink/core/config" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services" "github.com/smartcontractkit/chainlink/core/services/blockhashstore" "github.com/smartcontractkit/chainlink/core/services/cron" @@ -63,6 +64,7 @@ type Application interface { Start(ctx context.Context) error Stop() error GetLogger() logger.Logger + GetAuditLogger() audit.AuditLogger GetHealthChecker() services.Checker GetSqlxDB() *sqlx.DB GetConfig() config.GeneralConfig @@ -185,6 +187,12 @@ func NewApplication(opts ApplicationOpts) (Application, error) { restrictedHTTPClient := opts.RestrictedHTTPClient unrestrictedHTTPClient := opts.UnrestrictedHTTPClient + // Configure and optionally start the audit log forwarder service + auditLogger, err := audit.NewAuditLogger(globalLogger) + if err != nil { + return nil, errors.Errorf("Unable to initialize audit logger", "err", err) + } + var nurse *services.Nurse if cfg.AutoPprofEnabled() { globalLogger.Info("Nurse service (automatic pprof profiling) is enabled") @@ -248,7 +256,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { var ( pipelineORM = pipeline.NewORM(db, globalLogger, cfg) bridgeORM = bridges.NewORM(db, globalLogger, cfg) - sessionORM = sessions.NewORM(db, cfg.SessionTimeout().Duration(), globalLogger) + sessionORM = sessions.NewORM(db, cfg.SessionTimeout().Duration(), globalLogger, auditLogger) pipelineRunner = pipeline.NewRunner(pipelineORM, cfg, chains.EVM, keyStore.Eth(), keyStore.VRF(), globalLogger, restrictedHTTPClient, unrestrictedHTTPClient) jobORM = job.NewORM(db, chains.EVM, pipelineORM, keyStore, globalLogger, cfg) txmORM = txmgr.NewORM(db, globalLogger, cfg) @@ -429,6 +437,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { HealthChecker: healthChecker, Nurse: nurse, logger: globalLogger, + auditLogger: auditLogger, closeLogger: opts.CloseLogger, sqlxDB: opts.SqlxDB, @@ -562,6 +571,10 @@ func (app *ChainlinkApplication) GetLogger() logger.Logger { return app.logger } +func (app *ChainlinkApplication) GetAuditLogger() audit.AuditLogger { + return app.auditLogger +} + func (app *ChainlinkApplication) GetHealthChecker() services.Checker { return app.HealthChecker } diff --git a/core/sessions/orm.go b/core/sessions/orm.go index 4a85ec75792..2b61dbcbd4d 100644 --- a/core/sessions/orm.go +++ b/core/sessions/orm.go @@ -1,6 +1,7 @@ package sessions import ( + "context" "crypto/subtle" "database/sql" "encoding/json" @@ -45,12 +46,13 @@ type orm struct { db *sqlx.DB sessionDuration time.Duration lggr logger.Logger + auditLogger audit.AuditLogger } var _ ORM = (*orm)(nil) -func NewORM(db *sqlx.DB, sessionDuration time.Duration, lggr logger.Logger) ORM { - return &orm{db, sessionDuration, lggr.Named("SessionsORM")} +func NewORM(db *sqlx.DB, sessionDuration time.Duration, lggr logger.Logger, auditLogger audit.AuditLogger) ORM { + return &orm{db, sessionDuration, lggr.Named("SessionsORM"), auditLogger} } // FindUser will return the one API user, or an error. @@ -134,12 +136,12 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { // Do email and password check first to prevent extra database look up // for MFA tokens leaking if an account has MFA tokens or not. if !constantTimeEmailCompare(sr.Email, user.Email) { - o.lggr.Audit(audit.AuthLoginFailedEmail, map[string]interface{}{"email": sr.Email}) + o.auditLogger.Audit(context.Background(), audit.AuthLoginFailedEmail, map[string]interface{}{"email": sr.Email}) return "", errors.New("Invalid email") } if !utils.CheckPasswordHash(sr.Password, user.HashedPassword) { - o.lggr.Audit(audit.AuthLoginFailedPassword, map[string]interface{}{"email": sr.Email}) + o.auditLogger.Audit(context.Background(), audit.AuthLoginFailedPassword, map[string]interface{}{"email": sr.Email}) return "", errors.New("Invalid password") } @@ -156,7 +158,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { lggr.Infof("No MFA for user. Creating Session") session := NewSession() _, err = o.db.Exec("INSERT INTO sessions (id, last_used, created_at) VALUES ($1, now(), now())", session.ID) - o.lggr.Audit(audit.AuthLoginSuccessNo2FA, map[string]interface{}{"email": sr.Email}) + o.auditLogger.Audit(context.Background(), audit.AuthLoginSuccessNo2FA, map[string]interface{}{"email": sr.Email}) return session.ID, err } @@ -187,7 +189,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { if err != nil { // The user does have WebAuthn enabled but failed the check - o.lggr.Audit(audit.AuthLoginFailed2FA, map[string]interface{}{"email": sr.Email, "error": err}) + o.auditLogger.Audit(context.Background(), audit.AuthLoginFailed2FA, map[string]interface{}{"email": sr.Email, "error": err}) lggr.Errorf("User sent an invalid attestation: %v", err) return "", errors.New("MFA Error") } @@ -206,7 +208,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { lggr.Errorf("error in Marshal credentials: %s", err) return "", err } - o.lggr.Audit(audit.AuthLoginSuccessWith2FA, map[string]interface{}{"email": sr.Email, "credential": string(uwasj)}) + o.auditLogger.Audit(context.Background(), audit.AuthLoginSuccessWith2FA, map[string]interface{}{"email": sr.Email, "credential": string(uwasj)}) return session.ID, nil } diff --git a/core/web/bridge_types_controller.go b/core/web/bridge_types_controller.go index 266c7f143a5..08e41e720a8 100644 --- a/core/web/bridge_types_controller.go +++ b/core/web/bridge_types_controller.go @@ -1,6 +1,7 @@ package web import ( + "context" "database/sql" "fmt" "net/http" @@ -97,7 +98,7 @@ func (btc *BridgeTypesController) Create(c *gin.Context) { resource := presenters.NewBridgeResource(*bt) resource.IncomingToken = bta.IncomingToken - btc.App.GetLogger().Audit(audit.BridgeCreated, map[string]interface{}{ + btc.App.GetAuditLogger().Audit(context.Background(), audit.BridgeCreated, map[string]interface{}{ "bridgeName": bta.Name, "bridgeConfirmations": bta.Confirmations, "bridgeMinimumContractPayment": bta.MinimumContractPayment, @@ -178,7 +179,7 @@ func (btc *BridgeTypesController) Update(c *gin.Context) { return } - btc.App.GetLogger().Audit(audit.BridgeUpdated, map[string]interface{}{ + btc.App.GetAuditLogger().Audit(context.Background(), audit.BridgeUpdated, map[string]interface{}{ "bridgeName": bt.Name, "bridgeConfirmations": bt.Confirmations, "bridgeMinimumContractPayment": bt.MinimumContractPayment, @@ -222,7 +223,7 @@ func (btc *BridgeTypesController) Destroy(c *gin.Context) { return } - btc.App.GetLogger().Audit(audit.BridgeDeleted, map[string]interface{}{"name": name}) + btc.App.GetAuditLogger().Audit(context.Background(), audit.BridgeDeleted, map[string]interface{}{"name": name}) jsonAPIResponse(c, presenters.NewBridgeResource(bt), "bridge") } diff --git a/core/web/chains_controller.go b/core/web/chains_controller.go index e6240854aa9..271a310328e 100644 --- a/core/web/chains_controller.go +++ b/core/web/chains_controller.go @@ -34,10 +34,11 @@ type chainsController[I chains.ID, C chains.Config, R jsonapi.EntityNamer] struc parseChainID func(string) (I, error) newResource func(chains.DBChain[I, C]) R lggr logger.Logger + auditLogger audit.AuditLogger } func newChainsController[I chains.ID, C chains.Config, R jsonapi.EntityNamer](prefix string, chainSet chains.DBChainSet[I, C], errNotEnabled error, - parseChainID func(string) (I, error), newResource func(chains.DBChain[I, C]) R, lggr logger.Logger) *chainsController[I, C, R] { + parseChainID func(string) (I, error), newResource func(chains.DBChain[I, C]) R, lggr logger.Logger, auditLogger audit.AuditLogger) *chainsController[I, C, R] { return &chainsController[I, C, R]{ resourceName: prefix + "_chain", chainSet: chainSet, @@ -45,6 +46,7 @@ func newChainsController[I chains.ID, C chains.Config, R jsonapi.EntityNamer](pr parseChainID: parseChainID, newResource: newResource, lggr: lggr, + auditLogger: auditLogger, } } @@ -100,7 +102,7 @@ func (cc *chainsController[I, C, R]) Create(c *gin.Context) { if err != nil { cc.lggr.Errorf("Unable to marshal chain to json", "err", err) } - cc.lggr.Audit(audit.ChainAdded, map[string]interface{}{"chain": chainj}) + cc.auditLogger.Audit(c.Request.Context(), audit.ChainAdded, map[string]interface{}{"chain": chainj}) jsonAPIResponseWithStatus(c, cc.newResource(chain), cc.resourceName, http.StatusCreated) } @@ -160,7 +162,7 @@ func (cc *chainsController[I, C, R]) Update(c *gin.Context) { if err != nil { cc.lggr.Errorf("Unable to marshal chain to json", "err", err) } - cc.lggr.Audit(audit.ChainSpecUpdated, map[string]interface{}{"chain": chainj}) + cc.auditLogger.Audit(c.Request.Context(), audit.ChainSpecUpdated, map[string]interface{}{"chain": chainj}) jsonAPIResponse(c, cc.newResource(chain), cc.resourceName) } @@ -184,7 +186,7 @@ func (cc *chainsController[I, C, R]) Delete(c *gin.Context) { return } - cc.lggr.Audit(audit.ChainDeleted, map[string]interface{}{"id": id}) + cc.auditLogger.Audit(c.Request.Context(), audit.ChainDeleted, map[string]interface{}{"id": id}) jsonAPIResponseWithStatus(c, nil, cc.resourceName, http.StatusNoContent) } diff --git a/core/web/config_controller.go b/core/web/config_controller.go index e1a8c5cb3cd..8b787672f87 100644 --- a/core/web/config_controller.go +++ b/core/web/config_controller.go @@ -23,7 +23,7 @@ type ConfigController struct { func (cc *ConfigController) Show(c *gin.Context) { cw := config.NewConfigPrinter(cc.App.GetConfig()) - cc.App.GetLogger().Audit(audit.EnvNoncriticalEnvDumped, map[string]interface{}{}) + cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.EnvNoncriticalEnvDumped, map[string]interface{}{}) jsonAPIResponse(c, cw, "config") } @@ -88,6 +88,6 @@ func (cc *ConfigController) Patch(c *gin.Context) { }, EVMChainID: utils.NewBig(chain.ID()), } - cc.App.GetLogger().Audit(audit.ConfigUpdated, map[string]interface{}{"configResponse": response}) + cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ConfigUpdated, map[string]interface{}{"configResponse": response}) jsonAPIResponse(c, response, "config") } diff --git a/core/web/csa_keys_controller.go b/core/web/csa_keys_controller.go index 550229c6662..a7e2a524cc3 100644 --- a/core/web/csa_keys_controller.go +++ b/core/web/csa_keys_controller.go @@ -45,7 +45,7 @@ func (ctrl *CSAKeysController) Create(c *gin.Context) { return } - ctrl.App.GetLogger().Audit(audit.CSAKeyCreated, map[string]interface{}{ + ctrl.App.GetAuditLogger().Audit(c.Request.Context(), audit.CSAKeyCreated, map[string]interface{}{ "CSAPublicKey": key.PublicKey, "CSVersion": key.Version, }) @@ -69,7 +69,7 @@ func (ctrl *CSAKeysController) Import(c *gin.Context) { return } - ctrl.App.GetLogger().Audit(audit.CSAKeyImported, map[string]interface{}{ + ctrl.App.GetAuditLogger().Audit(c.Request.Context(), audit.CSAKeyImported, map[string]interface{}{ "CSAPublicKey": key.PublicKey, "CSVersion": key.Version, }) @@ -90,6 +90,6 @@ func (ctrl *CSAKeysController) Export(c *gin.Context) { return } - ctrl.App.GetLogger().Audit(audit.CSAKeyExported, map[string]interface{}{"keyID": keyID}) + ctrl.App.GetAuditLogger().Audit(c.Request.Context(), audit.CSAKeyExported, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/eth_keys_controller.go b/core/web/eth_keys_controller.go index 5f5218f66c4..3be8f182bab 100644 --- a/core/web/eth_keys_controller.go +++ b/core/web/eth_keys_controller.go @@ -132,7 +132,7 @@ func (ekc *ETHKeysController) Create(c *gin.Context) { return } - ekc.App.GetLogger().Audit(audit.ETHKeyCreated, map[string]interface{}{ + ekc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ETHKeyCreated, map[string]interface{}{ "ethPublicKey": key.Address, "ethID": key.ID(), }) @@ -187,7 +187,7 @@ func (ekc *ETHKeysController) Update(c *gin.Context) { return } - ekc.App.GetLogger().Audit(audit.ETHKeyUpdated, map[string]interface{}{ + ekc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ETHKeyUpdated, map[string]interface{}{ "ethPublicKey": key.Address, "ethID": key.ID(), }) @@ -243,7 +243,7 @@ func (ekc *ETHKeysController) Delete(c *gin.Context) { return } - ekc.App.GetLogger().Audit(audit.ETHKeyDeleted, map[string]interface{}{"id": keyID}) + ekc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ETHKeyDeleted, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, r, "account") } @@ -291,7 +291,7 @@ func (ekc *ETHKeysController) Import(c *gin.Context) { return } - ekc.App.GetLogger().Audit(audit.ETHKeyImported, map[string]interface{}{ + ekc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ETHKeyImported, map[string]interface{}{ "ethPublicKey": key.Address, "ethID": key.ID(), }) @@ -311,7 +311,7 @@ func (ekc *ETHKeysController) Export(c *gin.Context) { return } - ekc.App.GetLogger().Audit(audit.ETHKeyExported, map[string]interface{}{"address": address}) + ekc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ETHKeyExported, map[string]interface{}{"address": address}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/evm_chains_controller.go b/core/web/evm_chains_controller.go index 300efc05dea..c00839a0806 100644 --- a/core/web/evm_chains_controller.go +++ b/core/web/evm_chains_controller.go @@ -17,5 +17,5 @@ func NewEVMChainsController(app chainlink.Application) ChainsController { return } return newChainsController[utils.Big, *types.ChainCfg, presenters.EVMChainResource]( - "evm", app.GetChains().EVM, ErrEVMNotEnabled, parse, presenters.NewEVMChainResource, app.GetLogger()) + "evm", app.GetChains().EVM, ErrEVMNotEnabled, parse, presenters.NewEVMChainResource, app.GetLogger(), app.GetAuditLogger()) } diff --git a/core/web/evm_forwarders_controller.go b/core/web/evm_forwarders_controller.go index 6897a52cb85..66e5cbec0a4 100644 --- a/core/web/evm_forwarders_controller.go +++ b/core/web/evm_forwarders_controller.go @@ -59,7 +59,7 @@ func (cc *EVMForwardersController) Create(c *gin.Context) { return } - cc.App.GetLogger().Audit(audit.ForwarderCreated, map[string]interface{}{ + cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ForwarderCreated, map[string]interface{}{ "forwarderID": fwd.ID, "forwarderAddress": fwd.Address, "forwarderEVMChainID": fwd.EVMChainID, @@ -83,6 +83,6 @@ func (cc *EVMForwardersController) Delete(c *gin.Context) { return } - cc.App.GetLogger().Audit(audit.ForwarderDeleted, map[string]interface{}{"id": id}) + cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ForwarderDeleted, map[string]interface{}{"id": id}) jsonAPIResponseWithStatus(c, nil, "forwarder", http.StatusNoContent) } diff --git a/core/web/evm_nodes_controller.go b/core/web/evm_nodes_controller.go index 761baae3432..49ac8cd014c 100644 --- a/core/web/evm_nodes_controller.go +++ b/core/web/evm_nodes_controller.go @@ -30,5 +30,6 @@ func NewEVMNodesController(app chainlink.Application) NodesController { }, nil }, app.GetLogger(), + app.GetAuditLogger(), ) } diff --git a/core/web/evm_transfer_controller.go b/core/web/evm_transfer_controller.go index ba5c0ad57a0..4ff6727b0fb 100644 --- a/core/web/evm_transfer_controller.go +++ b/core/web/evm_transfer_controller.go @@ -64,7 +64,7 @@ func (tc *EVMTransfersController) Create(c *gin.Context) { return } - tc.App.GetLogger().Audit(audit.EthTransactionCreated, map[string]interface{}{ + tc.App.GetAuditLogger().Audit(c.Request.Context(), audit.EthTransactionCreated, map[string]interface{}{ "ethTX": etx, }) diff --git a/core/web/external_initiators_controller.go b/core/web/external_initiators_controller.go index 85c5484fb37..405ce5082d3 100644 --- a/core/web/external_initiators_controller.go +++ b/core/web/external_initiators_controller.go @@ -85,7 +85,7 @@ func (eic *ExternalInitiatorsController) Create(c *gin.Context) { return } - eic.App.GetLogger().Audit(audit.ExternalInitiatorCreated, map[string]interface{}{ + eic.App.GetAuditLogger().Audit(c.Request.Context(), audit.ExternalInitiatorCreated, map[string]interface{}{ "externalInitiatorID": ei.ID, "externalInitiatorName": ei.Name, "externalInitiatorURL": ei.URL, @@ -108,6 +108,6 @@ func (eic *ExternalInitiatorsController) Destroy(c *gin.Context) { return } - eic.App.GetLogger().Audit(audit.ExternalInitiatorDeleted, map[string]interface{}{"name": name}) + eic.App.GetAuditLogger().Audit(c.Request.Context(), audit.ExternalInitiatorDeleted, map[string]interface{}{"name": name}) jsonAPIResponseWithStatus(c, nil, "external initiator", http.StatusNoContent) } diff --git a/core/web/jobs_controller.go b/core/web/jobs_controller.go index dac86b1edf4..0a6916a767b 100644 --- a/core/web/jobs_controller.go +++ b/core/web/jobs_controller.go @@ -159,7 +159,7 @@ func (jc *JobsController) Create(c *gin.Context) { } jbj, _ := json.Marshal(jb) - jc.App.GetLogger().Audit(audit.JobCreated, map[string]interface{}{"job": string(jbj)}) + jc.App.GetAuditLogger().Audit(c.Request.Context(), audit.JobCreated, map[string]interface{}{"job": string(jbj)}) jsonAPIResponse(c, presenters.NewJobResource(jb), jb.Type.String()) } @@ -186,6 +186,6 @@ func (jc *JobsController) Delete(c *gin.Context) { return } - jc.App.GetLogger().Audit(audit.JobDeleted, map[string]interface{}{"id": j.ID}) + jc.App.GetAuditLogger().Audit(c.Request.Context(), audit.JobDeleted, map[string]interface{}{"id": j.ID}) jsonAPIResponseWithStatus(c, nil, "job", http.StatusNoContent) } diff --git a/core/web/keys_controller.go b/core/web/keys_controller.go index 8307eb427f7..3d803b48901 100644 --- a/core/web/keys_controller.go +++ b/core/web/keys_controller.go @@ -40,16 +40,18 @@ type KeysController interface { type keysController[K keystore.Key, R jsonapi.EntityNamer] struct { ks Keystore[K] lggr logger.Logger + auditLogger audit.AuditLogger resourceName string newResource func(K) *R newResources func([]K) []R } -func NewKeysController[K keystore.Key, R jsonapi.EntityNamer](ks Keystore[K], lggr logger.Logger, resourceName string, +func NewKeysController[K keystore.Key, R jsonapi.EntityNamer](ks Keystore[K], lggr logger.Logger, auditLogger audit.AuditLogger, resourceName string, newResource func(K) *R, newResources func([]K) []R) KeysController { return &keysController[K, R]{ ks: ks, lggr: lggr, + auditLogger: auditLogger, resourceName: resourceName, newResource: newResource, newResources: newResources, @@ -75,12 +77,12 @@ func (kc *keysController[K, R]) Create(c *gin.Context) { // Emit audit log, determine if Terra or Solana key switch unwrappedKey := any(key).(type) { case terrakey.Key: - kc.lggr.Audit(audit.TerraKeyCreated, map[string]interface{}{ + kc.auditLogger.Audit(c.Request.Context(), audit.TerraKeyCreated, map[string]interface{}{ "publicKey": unwrappedKey.PublicKey(), "id": unwrappedKey.ID(), }) case solkey.Key: - kc.lggr.Audit(audit.SolanaKeyCreated, map[string]interface{}{ + kc.auditLogger.Audit(c.Request.Context(), audit.SolanaKeyCreated, map[string]interface{}{ "publicKey": unwrappedKey.PublicKey(), "id": unwrappedKey.ID(), }) @@ -105,9 +107,9 @@ func (kc *keysController[K, R]) Delete(c *gin.Context) { // Emit audit log, determine if Terra or Solana key switch any(key).(type) { case terrakey.Key: - kc.lggr.Audit(audit.TerraKeyDeleted, map[string]interface{}{"id": keyID}) + kc.auditLogger.Audit(c.Request.Context(), audit.TerraKeyDeleted, map[string]interface{}{"id": keyID}) case solkey.Key: - kc.lggr.Audit(audit.SolanaKeyDeleted, map[string]interface{}{"id": keyID}) + kc.auditLogger.Audit(c.Request.Context(), audit.SolanaKeyDeleted, map[string]interface{}{"id": keyID}) } jsonAPIResponse(c, kc.newResource(key), kc.resourceName) @@ -131,12 +133,12 @@ func (kc *keysController[K, R]) Import(c *gin.Context) { // Emit audit log, determine if Terra or Solana key switch unwrappedKey := any(key).(type) { case terrakey.Key: - kc.lggr.Audit(audit.TerraKeyImported, map[string]interface{}{ + kc.auditLogger.Audit(c.Request.Context(), audit.TerraKeyImported, map[string]interface{}{ "publicKey": unwrappedKey.PublicKey(), "id": unwrappedKey.ID(), }) case solkey.Key: - kc.lggr.Audit(audit.SolanaKeyImported, map[string]interface{}{ + kc.auditLogger.Audit(c.Request.Context(), audit.SolanaKeyImported, map[string]interface{}{ "publicKey": unwrappedKey.PublicKey(), "id": unwrappedKey.ID(), }) @@ -157,9 +159,9 @@ func (kc *keysController[K, R]) Export(c *gin.Context) { } if strings.HasPrefix(c.Request.URL.Path, "/v2/keys/terra") { - kc.lggr.Audit(audit.TerraKeyExported, map[string]interface{}{"id": keyID}) + kc.auditLogger.Audit(c.Request.Context(), audit.TerraKeyExported, map[string]interface{}{"id": keyID}) } else if strings.HasPrefix(c.Request.URL.Path, "/v2/keys/solana") { - kc.lggr.Audit(audit.SolanaKeyExported, map[string]interface{}{"id": keyID}) + kc.auditLogger.Audit(c.Request.Context(), audit.SolanaKeyExported, map[string]interface{}{"id": keyID}) } c.Data(http.StatusOK, MediaType, bytes) diff --git a/core/web/log_controller.go b/core/web/log_controller.go index 4392dcde636..c1942ec9edc 100644 --- a/core/web/log_controller.go +++ b/core/web/log_controller.go @@ -91,13 +91,13 @@ func (cc *LogController) Patch(c *gin.Context) { LogLevel: lvls, } - cc.App.GetLogger().Audit(audit.GlobalLogLevelSet, map[string]interface{}{"logLevel": request.Level}) + cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.GlobalLogLevelSet, map[string]interface{}{"logLevel": request.Level}) if request.Level == "debug" { if request.SqlEnabled != nil && *request.SqlEnabled { - cc.App.GetLogger().Audit(audit.ConfigSqlLoggingEnabled, map[string]interface{}{}) + cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ConfigSqlLoggingEnabled, map[string]interface{}{}) } else { - cc.App.GetLogger().Audit(audit.ConfigSqlLoggingDisabled, map[string]interface{}{}) + cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ConfigSqlLoggingDisabled, map[string]interface{}{}) } } diff --git a/core/web/nodes_controller.go b/core/web/nodes_controller.go index 6086b6f056e..9bde8cd3a09 100644 --- a/core/web/nodes_controller.go +++ b/core/web/nodes_controller.go @@ -28,6 +28,7 @@ type nodesController[I chains.ID, N chains.Node, R jsonapi.EntityNamer] struct { newResource func(N) R createNode func(*gin.Context) (N, error) lggr logger.Logger + auditLogger audit.AuditLogger } func newNodesController[I chains.ID, N chains.Node, R jsonapi.EntityNamer]( @@ -37,6 +38,7 @@ func newNodesController[I chains.ID, N chains.Node, R jsonapi.EntityNamer]( newResource func(N) R, createNode func(*gin.Context) (N, error), lggr logger.Logger, + auditLogger audit.AuditLogger, ) NodesController { return &nodesController[I, N, R]{ nodeSet: nodeSet, @@ -45,6 +47,7 @@ func newNodesController[I chains.ID, N chains.Node, R jsonapi.EntityNamer]( newResource: newResource, createNode: createNode, lggr: lggr, + auditLogger: auditLogger, } } @@ -100,7 +103,7 @@ func (n *nodesController[I, N, R]) Create(c *gin.Context) { return } - n.lggr.Audit(audit.ChainRpcNodeAdded, map[string]interface{}{}) + n.auditLogger.Audit(c.Request.Context(), audit.ChainRpcNodeAdded, map[string]interface{}{}) jsonAPIResponse(c, n.newResource(node), "node") } @@ -124,7 +127,7 @@ func (n *nodesController[I, N, R]) Delete(c *gin.Context) { return } - n.lggr.Audit(audit.ChainDeleted, map[string]interface{}{"id": id}) + n.auditLogger.Audit(c.Request.Context(), audit.ChainDeleted, map[string]interface{}{"id": id}) jsonAPIResponseWithStatus(c, nil, "node", http.StatusNoContent) } diff --git a/core/web/ocr2_keys_controller.go b/core/web/ocr2_keys_controller.go index a1d88d0f5f1..8d6fa8d017a 100644 --- a/core/web/ocr2_keys_controller.go +++ b/core/web/ocr2_keys_controller.go @@ -44,7 +44,7 @@ func (ocr2kc *OCR2KeysController) Create(c *gin.Context) { return } - ocr2kc.App.GetLogger().Audit(audit.OCR2KeyBundleCreated, map[string]interface{}{ + ocr2kc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCR2KeyBundleCreated, map[string]interface{}{ "ocr2KeyID": key.ID(), "ocr2KeyChainType": key.ChainType(), "ocr2KeyConfigEncryptionPublicKey": key.ConfigEncryptionPublicKey(), @@ -71,7 +71,7 @@ func (ocr2kc *OCR2KeysController) Delete(c *gin.Context) { return } - ocr2kc.App.GetLogger().Audit(audit.OCR2KeyBundleDeleted, map[string]interface{}{"id": id}) + ocr2kc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCR2KeyBundleDeleted, map[string]interface{}{"id": id}) jsonAPIResponse(c, presenters.NewOCR2KeysBundleResource(key), "offChainReporting2KeyBundle") } @@ -93,7 +93,7 @@ func (ocr2kc *OCR2KeysController) Import(c *gin.Context) { return } - ocr2kc.App.GetLogger().Audit(audit.OCR2KeyBundleImported, map[string]interface{}{ + ocr2kc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCR2KeyBundleImported, map[string]interface{}{ "ocr2KeyID": keyBundle.ID(), "ocr2KeyChainType": keyBundle.ChainType(), "ocr2KeyConfigEncryptionPublicKey": keyBundle.ConfigEncryptionPublicKey(), @@ -119,6 +119,6 @@ func (ocr2kc *OCR2KeysController) Export(c *gin.Context) { return } - ocr2kc.App.GetLogger().Audit(audit.OCR2KeyBundleExported, map[string]interface{}{"keyID": stringID}) + ocr2kc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCR2KeyBundleExported, map[string]interface{}{"keyID": stringID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/ocr_keys_controller.go b/core/web/ocr_keys_controller.go index 15cb40b36d2..ba01da9f1fd 100644 --- a/core/web/ocr_keys_controller.go +++ b/core/web/ocr_keys_controller.go @@ -37,7 +37,7 @@ func (ocrkc *OCRKeysController) Create(c *gin.Context) { return } - ocrkc.App.GetLogger().Audit(audit.OCRKeyBundleCreated, map[string]interface{}{ + ocrkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCRKeyBundleCreated, map[string]interface{}{ "ocrKeyBundleID": key.ID(), "ocrKeyBundlePublicKeyAddressOnChain": key.PublicKeyAddressOnChain(), }) @@ -61,7 +61,7 @@ func (ocrkc *OCRKeysController) Delete(c *gin.Context) { return } - ocrkc.App.GetLogger().Audit(audit.OCRKeyBundleDeleted, map[string]interface{}{"id": id}) + ocrkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCRKeyBundleDeleted, map[string]interface{}{"id": id}) jsonAPIResponse(c, presenters.NewOCRKeysBundleResource(key), "offChainReportingKeyBundle") } @@ -83,7 +83,7 @@ func (ocrkc *OCRKeysController) Import(c *gin.Context) { return } - ocrkc.App.GetLogger().Audit(audit.OCRKeyBundleImported, map[string]interface{}{ + ocrkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCRKeyBundleImported, map[string]interface{}{ "OCRID": encryptedOCRKeyBundle.GetID(), "OCRPublicKeyAddressOnChain": encryptedOCRKeyBundle.PublicKeyAddressOnChain(), "OCRPublicKeyOffChain": encryptedOCRKeyBundle.PublicKeyOffChain(), @@ -106,6 +106,6 @@ func (ocrkc *OCRKeysController) Export(c *gin.Context) { return } - ocrkc.App.GetLogger().Audit(audit.OCRKeyBundleExported, map[string]interface{}{"keyID": stringID}) + ocrkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCRKeyBundleExported, map[string]interface{}{"keyID": stringID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/p2p_keys_controller.go b/core/web/p2p_keys_controller.go index b12babc6f2a..c81f61279fc 100644 --- a/core/web/p2p_keys_controller.go +++ b/core/web/p2p_keys_controller.go @@ -39,7 +39,7 @@ func (p2pkc *P2PKeysController) Create(c *gin.Context) { return } - p2pkc.App.GetLogger().Audit(audit.P2PKeyCreated, map[string]interface{}{ + p2pkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.P2PKeyCreated, map[string]interface{}{ "p2pPublicKey": key.PublicKeyHex(), "p2pID": key.ID(), "p2pPeerID": key.PeerID(), @@ -69,7 +69,7 @@ func (p2pkc *P2PKeysController) Delete(c *gin.Context) { return } - p2pkc.App.GetLogger().Audit(audit.P2PKeyDeleted, map[string]interface{}{"id": keyID}) + p2pkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.P2PKeyDeleted, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, presenters.NewP2PKeyResource(key), "p2pKey") } @@ -91,7 +91,7 @@ func (p2pkc *P2PKeysController) Import(c *gin.Context) { return } - p2pkc.App.GetLogger().Audit(audit.P2PKeyImported, map[string]interface{}{ + p2pkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.P2PKeyImported, map[string]interface{}{ "p2pPublicKey": key.PublicKeyHex(), "p2pID": key.ID(), "p2pPeerID": key.PeerID(), @@ -119,6 +119,6 @@ func (p2pkc *P2PKeysController) Export(c *gin.Context) { return } - p2pkc.App.GetLogger().Audit(audit.P2PKeyExported, map[string]interface{}{"keyID": keyID}) + p2pkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.P2PKeyExported, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/pipeline_job_spec_errors_controller.go b/core/web/pipeline_job_spec_errors_controller.go index d3d2423348c..fb1e6d476c8 100644 --- a/core/web/pipeline_job_spec_errors_controller.go +++ b/core/web/pipeline_job_spec_errors_controller.go @@ -38,6 +38,6 @@ func (psec *PipelineJobSpecErrorsController) Destroy(c *gin.Context) { return } - psec.App.GetLogger().Audit(audit.JobErrorDismissed, map[string]interface{}{"id": jobSpec.ID}) + psec.App.GetAuditLogger().Audit(c.Request.Context(), audit.JobErrorDismissed, map[string]interface{}{"id": jobSpec.ID}) jsonAPIResponseWithStatus(c, nil, "job", http.StatusNoContent) } diff --git a/core/web/pipeline_runs_controller.go b/core/web/pipeline_runs_controller.go index 1e7cf8339ad..8644463d8bf 100644 --- a/core/web/pipeline_runs_controller.go +++ b/core/web/pipeline_runs_controller.go @@ -180,6 +180,6 @@ func (prc *PipelineRunsController) Resume(c *gin.Context) { return } - prc.App.GetLogger().Audit(audit.UnauthedRunResumed, map[string]interface{}{"runID": c.Param("runID")}) + prc.App.GetAuditLogger().Audit(c.Request.Context(), audit.UnauthedRunResumed, map[string]interface{}{"runID": c.Param("runID")}) c.Status(http.StatusOK) } diff --git a/core/web/resolver/mutation.go b/core/web/resolver/mutation.go index c864eb29ad8..889195ffed0 100644 --- a/core/web/resolver/mutation.go +++ b/core/web/resolver/mutation.go @@ -96,7 +96,7 @@ func (r *Resolver) CreateBridge(ctx context.Context, args struct{ Input createBr return nil, err } - r.App.GetLogger().Audit(audit.BridgeCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(ctx, audit.BridgeCreated, map[string]interface{}{ "bridgeName": bta.Name, "bridgeConfirmations": bta.Confirmations, "bridgeMinimumContractPayment": bta.MinimumContractPayment, @@ -120,7 +120,7 @@ func (r *Resolver) CreateCSAKey(ctx context.Context) (*CreateCSAKeyPayloadResolv return nil, err } - r.App.GetLogger().Audit(audit.CSAKeyCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(ctx, audit.CSAKeyCreated, map[string]interface{}{ "CSAPublicKey": key.PublicKey, "CSVersion": key.Version, }) @@ -144,7 +144,7 @@ func (r *Resolver) DeleteCSAKey(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.CSAKeyDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(ctx, audit.CSAKeyDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteCSAKeyPayload(key, nil), nil } @@ -237,7 +237,7 @@ func (r *Resolver) CreateFeedsManagerChainConfig(ctx context.Context, args struc } fmj, _ := json.Marshal(ccfg) - r.App.GetLogger().Audit(audit.FeedsManChainConfigCreated, map[string]interface{}{"feedsManager": fmj}) + r.App.GetAuditLogger().Audit(ctx, audit.FeedsManChainConfigCreated, map[string]interface{}{"feedsManager": fmj}) return NewCreateFeedsManagerChainConfigPayload(ccfg, nil, nil), nil } @@ -273,7 +273,7 @@ func (r *Resolver) DeleteFeedsManagerChainConfig(ctx context.Context, args struc return nil, err } - r.App.GetLogger().Audit(audit.FeedsManChainConfigDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(ctx, audit.FeedsManChainConfigDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteFeedsManagerChainConfigPayload(ccfg, nil), nil } @@ -357,7 +357,7 @@ func (r *Resolver) UpdateFeedsManagerChainConfig(ctx context.Context, args struc } fmj, _ := json.Marshal(ccfg) - r.App.GetLogger().Audit(audit.FeedsManChainConfigUpdated, map[string]interface{}{"feedsManager": fmj}) + r.App.GetAuditLogger().Audit(ctx, audit.FeedsManChainConfigUpdated, map[string]interface{}{"feedsManager": fmj}) return NewUpdateFeedsManagerChainConfigPayload(ccfg, nil, nil), nil } @@ -408,7 +408,7 @@ func (r *Resolver) CreateFeedsManager(ctx context.Context, args struct { } mgrj, _ := json.Marshal(mgr) - r.App.GetLogger().Audit(audit.FeedsManCreated, map[string]interface{}{"mgrj": mgrj}) + r.App.GetAuditLogger().Audit(ctx, audit.FeedsManCreated, map[string]interface{}{"mgrj": mgrj}) return NewCreateFeedsManagerPayload(mgr, nil, nil), nil } @@ -472,7 +472,7 @@ func (r *Resolver) UpdateBridge(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.BridgeUpdated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(ctx, audit.BridgeUpdated, map[string]interface{}{ "bridgeName": bridge.Name, "bridgeConfirmations": bridge.Confirmations, "bridgeMinimumContractPayment": bridge.MinimumContractPayment, @@ -531,7 +531,7 @@ func (r *Resolver) UpdateFeedsManager(ctx context.Context, args struct { } mgrj, _ := json.Marshal(mgr) - r.App.GetLogger().Audit(audit.FeedsManUpdated, map[string]interface{}{"mgrj": mgrj}) + r.App.GetAuditLogger().Audit(ctx, audit.FeedsManUpdated, map[string]interface{}{"mgrj": mgrj}) return NewUpdateFeedsManagerPayload(mgr, nil, nil), nil } @@ -546,7 +546,7 @@ func (r *Resolver) CreateOCRKeyBundle(ctx context.Context) (*CreateOCRKeyBundleP return nil, err } - r.App.GetLogger().Audit(audit.OCRKeyBundleCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(ctx, audit.OCRKeyBundleCreated, map[string]interface{}{ "ocrKeyBundleID": key.ID(), "ocrKeyBundlePublicKeyAddressOnChain": key.PublicKeyAddressOnChain(), }) @@ -569,7 +569,7 @@ func (r *Resolver) DeleteOCRKeyBundle(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.OCRKeyBundleDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(ctx, audit.OCRKeyBundleDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteOCRKeyBundlePayloadResolver(deletedKey, nil), nil } @@ -593,7 +593,7 @@ func (r *Resolver) CreateNode(ctx context.Context, args struct { wsURL, _ := url.Parse(args.Input.WSURL.String) // Forward only RPC host to logs httpURL, _ := url.Parse(args.Input.HTTPURL.String) - r.App.GetLogger().Audit(audit.ChainRpcNodeAdded, map[string]interface{}{ + r.App.GetAuditLogger().Audit(ctx, audit.ChainRpcNodeAdded, map[string]interface{}{ "chainNodeName": args.Input.Name, "chainNodeEvmChainID": args.Input.EVMChainID, "chainNodeRPCWebSocketHost": wsURL.Host, @@ -637,7 +637,7 @@ func (r *Resolver) DeleteNode(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.ChainRpcNodeDeleted, map[string]interface{}{"id": id}) + r.App.GetAuditLogger().Audit(ctx, audit.ChainRpcNodeDeleted, map[string]interface{}{"id": id}) return NewDeleteNodePayloadResolver(&node, nil), nil } @@ -675,7 +675,7 @@ func (r *Resolver) DeleteBridge(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.BridgeDeleted, map[string]interface{}{"name": bt.Name}) + r.App.GetAuditLogger().Audit(ctx, audit.BridgeDeleted, map[string]interface{}{"name": bt.Name}) return NewDeleteBridgePayload(&bt, nil), nil } @@ -689,7 +689,7 @@ func (r *Resolver) CreateP2PKey(ctx context.Context) (*CreateP2PKeyPayloadResolv return nil, err } - r.App.GetLogger().Audit(audit.P2PKeyCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(ctx, audit.P2PKeyCreated, map[string]interface{}{ "p2pPublicKey": key.PublicKeyHex(), "p2pID": key.ID(), "p2pPeerID": key.PeerID(), @@ -719,7 +719,7 @@ func (r *Resolver) DeleteP2PKey(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.P2PKeyDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(ctx, audit.P2PKeyDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteP2PKeyPayload(key, nil), nil } @@ -733,7 +733,7 @@ func (r *Resolver) CreateVRFKey(ctx context.Context) (*CreateVRFKeyPayloadResolv return nil, err } - r.App.GetLogger().Audit(audit.VRFKeyCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(ctx, audit.VRFKeyCreated, map[string]interface{}{ "vrfPublicKey": key.PublicKey, "vrfID": key.ID(), "vrfPublicKeyAddress": key.PublicKey.Address(), @@ -757,7 +757,7 @@ func (r *Resolver) DeleteVRFKey(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.VRFKeyDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(ctx, audit.VRFKeyDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteVRFKeyPayloadResolver(key, nil), nil } @@ -796,7 +796,7 @@ func (r *Resolver) ApproveJobProposalSpec(ctx context.Context, args struct { } specj, _ := json.Marshal(spec) - r.App.GetLogger().Audit(audit.JobProposalSpecApproved, map[string]interface{}{"spec": specj}) + r.App.GetAuditLogger().Audit(ctx, audit.JobProposalSpecApproved, map[string]interface{}{"spec": specj}) return NewApproveJobProposalSpecPayload(spec, err), nil } @@ -831,7 +831,7 @@ func (r *Resolver) CancelJobProposalSpec(ctx context.Context, args struct { } specj, _ := json.Marshal(spec) - r.App.GetLogger().Audit(audit.JobProposalSpecCanceled, map[string]interface{}{"spec": specj}) + r.App.GetAuditLogger().Audit(ctx, audit.JobProposalSpecCanceled, map[string]interface{}{"spec": specj}) return NewCancelJobProposalSpecPayload(spec, err), nil } @@ -866,7 +866,7 @@ func (r *Resolver) RejectJobProposalSpec(ctx context.Context, args struct { } specj, _ := json.Marshal(spec) - r.App.GetLogger().Audit(audit.JobProposalSpecRejected, map[string]interface{}{"spec": specj}) + r.App.GetAuditLogger().Audit(ctx, audit.JobProposalSpecRejected, map[string]interface{}{"spec": specj}) return NewRejectJobProposalSpecPayload(spec, err), nil } @@ -904,7 +904,7 @@ func (r *Resolver) UpdateJobProposalSpecDefinition(ctx context.Context, args str } specj, _ := json.Marshal(spec) - r.App.GetLogger().Audit(audit.JobProposalSpecUpdated, map[string]interface{}{"spec": specj}) + r.App.GetAuditLogger().Audit(ctx, audit.JobProposalSpecUpdated, map[string]interface{}{"spec": specj}) return NewUpdateJobProposalSpecDefinitionPayload(spec, err), nil } @@ -927,7 +927,7 @@ func (r *Resolver) UpdateUserPassword(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.OldPassword, dbUser.HashedPassword) { - r.App.GetLogger().Audit(audit.PasswordResetAttemptFailedMismatch, map[string]interface{}{"user": dbUser.Email}) + r.App.GetAuditLogger().Audit(ctx, audit.PasswordResetAttemptFailedMismatch, map[string]interface{}{"user": dbUser.Email}) return NewUpdatePasswordPayload(nil, map[string]string{ "oldPassword": "old password does not match", @@ -943,7 +943,7 @@ func (r *Resolver) UpdateUserPassword(ctx context.Context, args struct { return nil, failedPasswordUpdateError{} } - r.App.GetLogger().Audit(audit.PasswordResetSuccess, map[string]interface{}{"user": dbUser.Email}) + r.App.GetAuditLogger().Audit(ctx, audit.PasswordResetSuccess, map[string]interface{}{"user": dbUser.Email}) return NewUpdatePasswordPayload(session.User, nil), nil } @@ -957,9 +957,9 @@ func (r *Resolver) SetSQLLogging(ctx context.Context, args struct { r.App.GetConfig().SetLogSQL(args.Input.Enabled) if args.Input.Enabled { - r.App.GetLogger().Audit(audit.ConfigSqlLoggingEnabled, map[string]interface{}{}) + r.App.GetAuditLogger().Audit(ctx, audit.ConfigSqlLoggingEnabled, map[string]interface{}{}) } else { - r.App.GetLogger().Audit(audit.ConfigSqlLoggingDisabled, map[string]interface{}{}) + r.App.GetAuditLogger().Audit(ctx, audit.ConfigSqlLoggingDisabled, map[string]interface{}{}) } return NewSetSQLLoggingPayload(args.Input.Enabled), nil @@ -978,7 +978,7 @@ func (r *Resolver) CreateAPIToken(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.Password, dbUser.HashedPassword) { - r.App.GetLogger().Audit(audit.APITokenCreateAttemptPasswordMismatch, map[string]interface{}{"user": dbUser.Email}) + r.App.GetAuditLogger().Audit(ctx, audit.APITokenCreateAttemptPasswordMismatch, map[string]interface{}{"user": dbUser.Email}) return NewCreateAPITokenPayload(nil, map[string]string{ "password": "incorrect password", @@ -990,7 +990,7 @@ func (r *Resolver) CreateAPIToken(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.APITokenCreated, map[string]interface{}{"user": dbUser.Email}) + r.App.GetAuditLogger().Audit(ctx, audit.APITokenCreated, map[string]interface{}{"user": dbUser.Email}) return NewCreateAPITokenPayload(newToken, nil), nil } @@ -1007,7 +1007,7 @@ func (r *Resolver) DeleteAPIToken(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.Password, dbUser.HashedPassword) { - r.App.GetLogger().Audit(audit.APITokenDeleteAttemptPasswordMismatch, map[string]interface{}{"user": dbUser.Email}) + r.App.GetAuditLogger().Audit(ctx, audit.APITokenDeleteAttemptPasswordMismatch, map[string]interface{}{"user": dbUser.Email}) return NewDeleteAPITokenPayload(nil, map[string]string{ "password": "incorrect password", @@ -1019,7 +1019,7 @@ func (r *Resolver) DeleteAPIToken(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.APITokenDeleted, map[string]interface{}{"user": dbUser.Email}) + r.App.GetAuditLogger().Audit(ctx, audit.APITokenDeleted, map[string]interface{}{"user": dbUser.Email}) return NewDeleteAPITokenPayload(&auth.Token{ AccessKey: dbUser.TokenKey.String, @@ -1074,7 +1074,7 @@ func (r *Resolver) CreateChain(ctx context.Context, args struct { if err != nil { r.App.GetLogger().Errorf("Unable to marshal chain to json", "err", err) } - r.App.GetLogger().Audit(audit.ChainAdded, map[string]interface{}{"chain": chainj}) + r.App.GetAuditLogger().Audit(ctx, audit.ChainAdded, map[string]interface{}{"chain": chainj}) return NewCreateChainPayload(&chain, nil), nil } @@ -1132,7 +1132,7 @@ func (r *Resolver) UpdateChain(ctx context.Context, args struct { if err != nil { r.App.GetLogger().Errorf("Unable to marshal chain to json", "err", err) } - r.App.GetLogger().Audit(audit.ChainSpecUpdated, map[string]interface{}{"chainj": chainj}) + r.App.GetAuditLogger().Audit(ctx, audit.ChainSpecUpdated, map[string]interface{}{"chainj": chainj}) return NewUpdateChainPayload(&chain, nil, nil), nil } @@ -1164,7 +1164,7 @@ func (r *Resolver) DeleteChain(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.ChainDeleted, map[string]interface{}{"id": id}) + r.App.GetAuditLogger().Audit(ctx, audit.ChainDeleted, map[string]interface{}{"id": id}) return NewDeleteChainPayload(&chain, nil), nil } @@ -1231,7 +1231,7 @@ func (r *Resolver) CreateJob(ctx context.Context, args struct { } jbj, _ := json.Marshal(jb) - r.App.GetLogger().Audit(audit.JobCreated, map[string]interface{}{"job": string(jbj)}) + r.App.GetAuditLogger().Audit(ctx, audit.JobCreated, map[string]interface{}{"job": string(jbj)}) return NewCreateJobPayload(r.App, &jb, nil), nil } @@ -1266,7 +1266,7 @@ func (r *Resolver) DeleteJob(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.JobDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(ctx, audit.JobDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteJobPayload(r.App, &j, nil), nil } @@ -1300,7 +1300,7 @@ func (r *Resolver) DismissJobError(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.JobErrorDismissed, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(ctx, audit.JobErrorDismissed, map[string]interface{}{"id": args.ID}) return NewDismissJobErrorPayload(&specErr, nil), nil } @@ -1330,7 +1330,7 @@ func (r *Resolver) RunJob(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.JobRunSet, map[string]interface{}{"jobID": args.ID, "jobRunID": jobRunID, "planRunID": plnRun}) + r.App.GetAuditLogger().Audit(ctx, audit.JobRunSet, map[string]interface{}{"jobID": args.ID, "jobRunID": jobRunID, "planRunID": plnRun}) return NewRunJobPayload(&plnRun, r.App, nil), nil } @@ -1355,7 +1355,7 @@ func (r *Resolver) SetGlobalLogLevel(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.GlobalLogLevelSet, map[string]interface{}{"logLevel": args.Level}) + r.App.GetAuditLogger().Audit(ctx, audit.GlobalLogLevelSet, map[string]interface{}{"logLevel": args.Level}) return NewSetGlobalLogLevelPayload(args.Level, nil), nil } @@ -1374,7 +1374,7 @@ func (r *Resolver) CreateOCR2KeyBundle(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.OCR2KeyBundleCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(ctx, audit.OCR2KeyBundleCreated, map[string]interface{}{ "ocrKeyID": key.ID(), "ocrKeyChainType": key.ChainType(), "ocrKeyConfigEncryptionPublicKey": key.ConfigEncryptionPublicKey(), @@ -1405,6 +1405,6 @@ func (r *Resolver) DeleteOCR2KeyBundle(ctx context.Context, args struct { return nil, err } - r.App.GetLogger().Audit(audit.OCR2KeyBundleDeleted, map[string]interface{}{"id": id}) + r.App.GetAuditLogger().Audit(ctx, audit.OCR2KeyBundleDeleted, map[string]interface{}{"id": id}) return NewDeleteOCR2KeyBundlePayloadResolver(&key, nil), nil } diff --git a/core/web/sessions_controller.go b/core/web/sessions_controller.go index 421b43985a4..31c6dac88c0 100644 --- a/core/web/sessions_controller.go +++ b/core/web/sessions_controller.go @@ -84,7 +84,7 @@ func (sc *SessionsController) Destroy(c *gin.Context) { return } - sc.App.GetLogger().Audit(audit.AuthSessionDeleted, map[string]interface{}{"sessionID": sessionID}) + sc.App.GetAuditLogger().Audit(c.Request.Context(), audit.AuthSessionDeleted, map[string]interface{}{"sessionID": sessionID}) jsonAPIResponse(c, Session{Authenticated: false}, "session") } diff --git a/core/web/solana_chains_controller.go b/core/web/solana_chains_controller.go index 5392102234b..958db68c5c5 100644 --- a/core/web/solana_chains_controller.go +++ b/core/web/solana_chains_controller.go @@ -9,5 +9,5 @@ import ( func NewSolanaChainsController(app chainlink.Application) ChainsController { return newChainsController[string, *db.ChainCfg]("solana", app.GetChains().Solana, ErrSolanaNotEnabled, - func(s string) (string, error) { return s, nil }, presenters.NewSolanaChainResource, app.GetLogger()) + func(s string) (string, error) { return s, nil }, presenters.NewSolanaChainResource, app.GetLogger(), app.GetAuditLogger()) } diff --git a/core/web/solana_keys_controller.go b/core/web/solana_keys_controller.go index 6f9bcce0419..6aecd36775c 100644 --- a/core/web/solana_keys_controller.go +++ b/core/web/solana_keys_controller.go @@ -7,6 +7,6 @@ import ( ) func NewSolanaKeysController(app chainlink.Application) KeysController { - return NewKeysController[solkey.Key, presenters.SolanaKeyResource](app.GetKeyStore().Solana(), app.GetLogger(), + return NewKeysController[solkey.Key, presenters.SolanaKeyResource](app.GetKeyStore().Solana(), app.GetLogger(), app.GetAuditLogger(), "solanaKey", presenters.NewSolanaKeyResource, presenters.NewSolanaKeyResources) } diff --git a/core/web/solana_nodes_controller.go b/core/web/solana_nodes_controller.go index 691cc309175..697b132b6e0 100644 --- a/core/web/solana_nodes_controller.go +++ b/core/web/solana_nodes_controller.go @@ -37,5 +37,6 @@ func NewSolanaNodesController(app chainlink.Application) NodesController { }, nil }, app.GetLogger(), + app.GetAuditLogger(), ) } diff --git a/core/web/solana_transfer_controller.go b/core/web/solana_transfer_controller.go index b9fa04ffcf4..e19419aaec7 100644 --- a/core/web/solana_transfer_controller.go +++ b/core/web/solana_transfer_controller.go @@ -111,7 +111,7 @@ func (tc *SolanaTransfersController) Create(c *gin.Context) { resource.From = tr.From.String() resource.To = tr.To.String() - tc.App.GetLogger().Audit(audit.SolanaTransactionCreated, map[string]interface{}{ + tc.App.GetAuditLogger().Audit(c.Request.Context(), audit.SolanaTransactionCreated, map[string]interface{}{ "solanaTransactionResource": resource, }) jsonAPIResponse(c, resource, "solana_tx") diff --git a/core/web/terra_chains_controller.go b/core/web/terra_chains_controller.go index ce5147895e6..430ca985031 100644 --- a/core/web/terra_chains_controller.go +++ b/core/web/terra_chains_controller.go @@ -10,5 +10,5 @@ import ( func NewTerraChainsController(app chainlink.Application) ChainsController { parse := func(s string) (string, error) { return s, nil } return newChainsController[string, *db.ChainCfg, presenters.TerraChainResource]( - "terra", app.GetChains().Terra, ErrTerraNotEnabled, parse, presenters.NewTerraChainResource, app.GetLogger()) + "terra", app.GetChains().Terra, ErrTerraNotEnabled, parse, presenters.NewTerraChainResource, app.GetLogger(), app.GetAuditLogger()) } diff --git a/core/web/terra_keys_controller.go b/core/web/terra_keys_controller.go index bc4ef38eefe..69e8a331c0b 100644 --- a/core/web/terra_keys_controller.go +++ b/core/web/terra_keys_controller.go @@ -7,6 +7,6 @@ import ( ) func NewTerraKeysController(app chainlink.Application) KeysController { - return NewKeysController[terrakey.Key, presenters.TerraKeyResource](app.GetKeyStore().Terra(), app.GetLogger(), + return NewKeysController[terrakey.Key, presenters.TerraKeyResource](app.GetKeyStore().Terra(), app.GetLogger(), app.GetAuditLogger(), "terraKey", presenters.NewTerraKeyResource, presenters.NewTerraKeyResources) } diff --git a/core/web/terra_nodes_controller.go b/core/web/terra_nodes_controller.go index 66eb6d3f0c7..2326379788d 100644 --- a/core/web/terra_nodes_controller.go +++ b/core/web/terra_nodes_controller.go @@ -39,5 +39,6 @@ func NewTerraNodesController(app chainlink.Application) NodesController { }, nil }, app.GetLogger(), + app.GetAuditLogger(), ) } diff --git a/core/web/terra_transfer_controller.go b/core/web/terra_transfer_controller.go index 0e458cfa30b..2d7d31dabe2 100644 --- a/core/web/terra_transfer_controller.go +++ b/core/web/terra_transfer_controller.go @@ -111,7 +111,7 @@ func (tc *TerraTransfersController) Create(c *gin.Context) { resource.TxHash = msg.TxHash resource.State = string(msg.State) - tc.App.GetLogger().Audit(audit.TerraTransactionCreated, map[string]interface{}{ + tc.App.GetAuditLogger().Audit(c.Request.Context(), audit.TerraTransactionCreated, map[string]interface{}{ "terraTransactionResource": resource, }) diff --git a/core/web/user_controller.go b/core/web/user_controller.go index 0d6db6b55a7..fc95dcabcfb 100644 --- a/core/web/user_controller.go +++ b/core/web/user_controller.go @@ -43,7 +43,7 @@ func (c *UserController) UpdatePassword(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.OldPassword, user.HashedPassword) { - c.App.GetLogger().Audit(audit.PasswordResetAttemptFailedMismatch, map[string]interface{}{"user": user.Email}) + c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.PasswordResetAttemptFailedMismatch, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusConflict, errors.New("old password does not match")) return } @@ -52,7 +52,7 @@ func (c *UserController) UpdatePassword(ctx *gin.Context) { return } - c.App.GetLogger().Audit(audit.PasswordResetSuccess, map[string]interface{}{"user": user.Email}) + c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.PasswordResetSuccess, map[string]interface{}{"user": user.Email}) jsonAPIResponse(ctx, presenters.NewUserResource(user), "user") } @@ -70,7 +70,7 @@ func (c *UserController) NewAPIToken(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.Password, user.HashedPassword) { - c.App.GetLogger().Audit(audit.APITokenCreateAttemptPasswordMismatch, map[string]interface{}{"user": user.Email}) + c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.APITokenCreateAttemptPasswordMismatch, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusUnauthorized, errors.New("incorrect password")) return } @@ -80,7 +80,7 @@ func (c *UserController) NewAPIToken(ctx *gin.Context) { return } - c.App.GetLogger().Audit(audit.APITokenCreated, map[string]interface{}{"user": user.Email}) + c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.APITokenCreated, map[string]interface{}{"user": user.Email}) jsonAPIResponseWithStatus(ctx, newToken, "auth_token", http.StatusCreated) } @@ -98,7 +98,7 @@ func (c *UserController) DeleteAPIToken(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.Password, user.HashedPassword) { - c.App.GetLogger().Audit(audit.APITokenDeleteAttemptPasswordMismatch, map[string]interface{}{"user": user.Email}) + c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.APITokenDeleteAttemptPasswordMismatch, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusUnauthorized, errors.New("incorrect password")) return } @@ -107,7 +107,7 @@ func (c *UserController) DeleteAPIToken(ctx *gin.Context) { return } { - c.App.GetLogger().Audit(audit.APITokenDeleted, map[string]interface{}{"user": user.Email}) + c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.APITokenDeleted, map[string]interface{}{"user": user.Email}) jsonAPIResponseWithStatus(ctx, nil, "auth_token", http.StatusNoContent) } } diff --git a/core/web/vrf_keys_controller.go b/core/web/vrf_keys_controller.go index 81625331066..543402ba9ee 100644 --- a/core/web/vrf_keys_controller.go +++ b/core/web/vrf_keys_controller.go @@ -38,7 +38,7 @@ func (vrfkc *VRFKeysController) Create(c *gin.Context) { return } - vrfkc.App.GetLogger().Audit(audit.VRFKeyCreated, map[string]interface{}{ + vrfkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.VRFKeyCreated, map[string]interface{}{ "vrfPublicKey": pk.PublicKey, "vrfID": pk.ID(), "vrfPublicKeyAddress": pk.PublicKey.Address(), @@ -63,7 +63,7 @@ func (vrfkc *VRFKeysController) Delete(c *gin.Context) { return } - vrfkc.App.GetLogger().Audit(audit.VRFKeyDeleted, map[string]interface{}{"id": keyID}) + vrfkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.VRFKeyDeleted, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, presenters.NewVRFKeyResource(key, vrfkc.App.GetLogger()), "vrfKey") } @@ -85,7 +85,7 @@ func (vrfkc *VRFKeysController) Import(c *gin.Context) { return } - vrfkc.App.GetLogger().Audit(audit.VRFKeyImported, map[string]interface{}{ + vrfkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.VRFKeyImported, map[string]interface{}{ "vrfID": key.ID(), "vrfPublicKey": key.PublicKey, }) @@ -107,6 +107,6 @@ func (vrfkc *VRFKeysController) Export(c *gin.Context) { return } - vrfkc.App.GetLogger().Audit(audit.VRFKeyExported, map[string]interface{}{"keyID": keyID}) + vrfkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.VRFKeyExported, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/webauthn_controller.go b/core/web/webauthn_controller.go index b02eb5d58e9..071f626054d 100644 --- a/core/web/webauthn_controller.go +++ b/core/web/webauthn_controller.go @@ -94,7 +94,7 @@ func (c *WebAuthnController) FinishRegistration(ctx *gin.Context) { jsonAPIError(ctx, http.StatusBadRequest, errors.New("registration was unsuccessful")) return } - c.App.GetLogger().Audit(audit.Auth2FAEnrolled, map[string]interface{}{"email": user.Email, "credential": string(credj)}) + c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.Auth2FAEnrolled, map[string]interface{}{"email": user.Email, "credential": string(credj)}) ctx.String(http.StatusOK, "{}") } From de09f4128ed95d55373803d9c13b320e6e835321 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Fri, 1 Jul 2022 06:11:14 -0700 Subject: [PATCH 13/92] Generate mocks and remove previous audit log test rework --- core/internal/mocks/application.go | 19 +++++++++++++++++++ core/logger/logger_mock_test.go | 1 - core/web/resolver/resolver_test.go | 4 ---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/core/internal/mocks/application.go b/core/internal/mocks/application.go index f79d858f377..e1717a6e729 100644 --- a/core/internal/mocks/application.go +++ b/core/internal/mocks/application.go @@ -5,7 +5,10 @@ package mocks import ( big "math/big" + audit "github.com/smartcontractkit/chainlink/core/logger/audit" + bridges "github.com/smartcontractkit/chainlink/core/bridges" + chainlink "github.com/smartcontractkit/chainlink/core/services/chainlink" config "github.com/smartcontractkit/chainlink/core/config" @@ -108,6 +111,22 @@ func (_m *Application) EVMORM() types.ORM { return r0 } +// GetAuditLogger provides a mock function with given fields: +func (_m *Application) GetAuditLogger() audit.AuditLogger { + ret := _m.Called() + + var r0 audit.AuditLogger + if rf, ok := ret.Get(0).(func() audit.AuditLogger); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(audit.AuditLogger) + } + } + + return r0 +} + // GetChains provides a mock function with given fields: func (_m *Application) GetChains() chainlink.Chains { ret := _m.Called() diff --git a/core/logger/logger_mock_test.go b/core/logger/logger_mock_test.go index dec717ce1a4..c8f0e06a2e7 100644 --- a/core/logger/logger_mock_test.go +++ b/core/logger/logger_mock_test.go @@ -6,7 +6,6 @@ import ( io "io" mock "github.com/stretchr/testify/mock" - zapcore "go.uber.org/zap/zapcore" ) diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go index a384db731b2..b1e56de5d59 100644 --- a/core/web/resolver/resolver_test.go +++ b/core/web/resolver/resolver_test.go @@ -17,7 +17,6 @@ import ( configMocks "github.com/smartcontractkit/chainlink/core/config/mocks" coremocks "github.com/smartcontractkit/chainlink/core/internal/mocks" "github.com/smartcontractkit/chainlink/core/internal/testutils/evmtest" - "github.com/smartcontractkit/chainlink/core/logger" feedsMocks "github.com/smartcontractkit/chainlink/core/services/feeds/mocks" jobORMMocks "github.com/smartcontractkit/chainlink/core/services/job/mocks" keystoreMocks "github.com/smartcontractkit/chainlink/core/services/keystore/mocks" @@ -85,9 +84,6 @@ func setupFramework(t *testing.T) *gqlTestFramework { ctx = loader.InjectDataloader(context.Background(), app) ) - l := logger.TestLogger(t) - app.On("GetLogger").Return(l).Maybe() - // Setup mocks // Note - If you add a new mock make sure you assert it's expectation below. m := &mocks{ From 19d0637d697859a6e6bac268a5e129fe4580d5c7 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Fri, 1 Jul 2022 10:17:58 -0700 Subject: [PATCH 14/92] Fix errors.Errorf call in application --- core/services/chainlink/application.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 88d6439227b..0d09c835069 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -190,7 +190,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { // Configure and optionally start the audit log forwarder service auditLogger, err := audit.NewAuditLogger(globalLogger) if err != nil { - return nil, errors.Errorf("Unable to initialize audit logger", "err", err) + return nil, errors.Errorf("Unable to initialize audit logger: %s", err) } var nurse *services.Nurse From 96a6eadf9d202bc31f6c28475554f9cd93760e86 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Fri, 1 Jul 2022 11:47:06 -0700 Subject: [PATCH 15/92] Add mocks and stubs to test harness calls that require new auditLogger field --- core/cmd/client_test.go | 9 +++++---- core/cmd/local_client_test.go | 9 +++++---- core/logger/audit/audit_logger.go | 14 +++++++------- core/sessions/orm_test.go | 5 +++-- core/sessions/reaper_test.go | 3 ++- core/web/resolver/resolver_test.go | 3 +++ 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/core/cmd/client_test.go b/core/cmd/client_test.go index db2ddb657f6..0529c7b8e9e 100644 --- a/core/cmd/client_test.go +++ b/core/cmd/client_test.go @@ -9,6 +9,7 @@ import ( "github.com/smartcontractkit/chainlink/core/internal/testutils" "github.com/smartcontractkit/chainlink/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/sessions" "github.com/stretchr/testify/assert" @@ -135,7 +136,7 @@ func TestTerminalAPIInitializer_InitializeWithoutAPIUser(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { db := pgtest.NewSqlxDB(t) - orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t)) + orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), &audit.AuditLoggerService{}) mock := &cltest.MockCountingPrompter{T: t, EnteredStrings: test.enteredStrings, NotTerminal: !test.isTerminal} tai := cmd.NewPromptingAPIInitializer(mock) @@ -165,7 +166,7 @@ func TestTerminalAPIInitializer_InitializeWithExistingAPIUser(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t)) + orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), &audit.AuditLoggerService{}) initialUser := cltest.MustRandomUser(t) require.NoError(t, orm.CreateUser(&initialUser)) @@ -194,7 +195,7 @@ func TestFileAPIInitializer_InitializeWithoutAPIUser(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { db := pgtest.NewSqlxDB(t) - orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t)) + orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), &audit.AuditLoggerService{}) // Clear out fixture user orm.DeleteUser() @@ -217,7 +218,7 @@ func TestFileAPIInitializer_InitializeWithExistingAPIUser(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t)) + orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), &audit.AuditLoggerService{}) tests := []struct { name string diff --git a/core/cmd/local_client_test.go b/core/cmd/local_client_test.go index 97ebd2f3b9e..43b960da45e 100644 --- a/core/cmd/local_client_test.go +++ b/core/cmd/local_client_test.go @@ -21,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/core/sessions" @@ -52,7 +53,7 @@ func TestClient_RunNodeShowsEnv(t *testing.T) { require.NoError(t, cfg.SetLogLevel(zapcore.DebugLevel)) db := pgtest.NewSqlxDB(t) - sessionORM := sessions.NewORM(db, time.Minute, lggr) + sessionORM := sessions.NewORM(db, time.Minute, lggr, &audit.AuditLoggerService{}) keyStore := cltest.NewKeyStore(t, db, cfg) _, err := keyStore.Eth().Create(&cltest.FixtureChainID) require.NoError(t, err) @@ -224,7 +225,7 @@ func TestClient_RunNodeWithPasswords(t *testing.T) { cfg := cltest.NewTestGeneralConfig(t) db := pgtest.NewSqlxDB(t) keyStore := cltest.NewKeyStore(t, db, cfg) - sessionORM := sessions.NewORM(db, time.Minute, logger.TestLogger(t)) + sessionORM := sessions.NewORM(db, time.Minute, logger.TestLogger(t), &audit.AuditLoggerService{}) // Clear out fixture err := sessionORM.DeleteUser() require.NoError(t, err) @@ -276,7 +277,7 @@ func TestClient_RunNode_CreateFundingKeyIfNotExists(t *testing.T) { lggr := logger.TestLogger(t) cfg := cltest.NewTestGeneralConfig(t) db := pgtest.NewSqlxDB(t) - sessionORM := sessions.NewORM(db, time.Minute, lggr) + sessionORM := sessions.NewORM(db, time.Minute, lggr, &audit.AuditLoggerService{}) keyStore := cltest.NewKeyStore(t, db, cfg) _, err := keyStore.Eth().Create(&cltest.FixtureChainID) require.NoError(t, err) @@ -338,7 +339,7 @@ func TestClient_RunNodeWithAPICredentialsFile(t *testing.T) { t.Run(test.name, func(t *testing.T) { cfg := cltest.NewTestGeneralConfig(t) db := pgtest.NewSqlxDB(t) - sessionORM := sessions.NewORM(db, time.Minute, logger.TestLogger(t)) + sessionORM := sessions.NewORM(db, time.Minute, logger.TestLogger(t), &audit.AuditLoggerService{}) // Clear out fixture err := sessionORM.DeleteUser() require.NoError(t, err) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 91ba0da11a3..97d8f1c4073 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -20,7 +20,7 @@ type AuditLogger interface { Audit(ctx context.Context, eventID EventID, data map[string]interface{}) } -type auditLogger struct { +type AuditLoggerService struct { logger logger.Logger enabled bool serviceURL string @@ -50,7 +50,7 @@ func NewAuditLogger(logger logger.Logger) (AuditLogger, error) { // Unset, return a disabled audit logger logger.Info("No AUDIT_LOGS_FORWARDER_URL environment set, audit log events will not be captured") - return &auditLogger{}, nil + return &AuditLoggerService{}, nil } env := "production" @@ -59,7 +59,7 @@ func NewAuditLogger(logger logger.Logger) (AuditLogger, error) { } hostname, err := os.Hostname() if err != nil { - return &auditLogger{}, errors.Errorf("Audit Log initialization error - unable to get hostname", "err", err) + return &AuditLoggerService{}, errors.Errorf("Audit Log initialization error - unable to get hostname", "err", err) } // Split and prepare optional service client headers from env variable @@ -70,7 +70,7 @@ func NewAuditLogger(logger logger.Logger) (AuditLogger, error) { for _, header := range headerLines { keyValue := strings.Split(header, "||") if len(keyValue) != 2 { - return &auditLogger{}, errors.Errorf("Invalid AUDIT_LOGS_FORWARDER_HEADERS value, single pair split on || required, got: %s", keyValue) + return &AuditLoggerService{}, errors.Errorf("Invalid AUDIT_LOGS_FORWARDER_HEADERS value, single pair split on || required, got: %s", keyValue) } headers = append(headers, serviceHeader{ header: keyValue[0], @@ -80,7 +80,7 @@ func NewAuditLogger(logger logger.Logger) (AuditLogger, error) { } // Finally, create new auditLogger with parameters - auditLogger := auditLogger{ + auditLogger := AuditLoggerService{ logger: logger.Helper(1), enabled: true, serviceURL: auditLogsURL, @@ -93,14 +93,14 @@ func NewAuditLogger(logger logger.Logger) (AuditLogger, error) { return &auditLogger, nil } -func (l *auditLogger) Audit(ctx context.Context, eventID EventID, data map[string]interface{}) { +func (l *AuditLoggerService) Audit(ctx context.Context, eventID EventID, data map[string]interface{}) { if !l.enabled { return } l.postLogToLogService(eventID, data) } -func (l *auditLogger) postLogToLogService(eventID EventID, data map[string]interface{}) { +func (l *AuditLoggerService) postLogToLogService(eventID EventID, data map[string]interface{}) { // Audit log JSON data logItem := map[string]interface{}{ "eventID": eventID, diff --git a/core/sessions/orm_test.go b/core/sessions/orm_test.go index d6a86f0aa8c..978e428f935 100644 --- a/core/sessions/orm_test.go +++ b/core/sessions/orm_test.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/core/internal/cltest" "github.com/smartcontractkit/chainlink/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/sessions" "github.com/smartcontractkit/chainlink/core/utils" ) @@ -24,7 +25,7 @@ func setupORM(t *testing.T) (*sqlx.DB, sessions.ORM) { t.Helper() db := pgtest.NewSqlxDB(t) - orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t)) + orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), &audit.AuditLoggerService{}) return db, orm } @@ -66,7 +67,7 @@ func TestORM_AuthorizedUserWithSession(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { db := pgtest.NewSqlxDB(t) - orm := sessions.NewORM(db, test.sessionDuration, logger.TestLogger(t)) + orm := sessions.NewORM(db, test.sessionDuration, logger.TestLogger(t), &audit.AuditLoggerService{}) user := cltest.MustNewUser(t, "have@email", "password") require.NoError(t, orm.CreateUser(&user)) diff --git a/core/sessions/reaper_test.go b/core/sessions/reaper_test.go index c4d83a84756..7853b478f37 100644 --- a/core/sessions/reaper_test.go +++ b/core/sessions/reaper_test.go @@ -7,6 +7,7 @@ import ( "github.com/smartcontractkit/chainlink/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/sessions" "github.com/smartcontractkit/chainlink/core/store/models" @@ -31,7 +32,7 @@ func TestSessionReaper_ReapSessions(t *testing.T) { db := pgtest.NewSqlxDB(t) config := sessionReaperConfig{} lggr := logger.TestLogger(t) - orm := sessions.NewORM(db, config.SessionTimeout().Duration(), lggr) + orm := sessions.NewORM(db, config.SessionTimeout().Duration(), lggr, &audit.AuditLoggerService{}) r := sessions.NewSessionReaper(db.DB, config, lggr) defer r.Stop() diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go index b1e56de5d59..61d1aa9f43a 100644 --- a/core/web/resolver/resolver_test.go +++ b/core/web/resolver/resolver_test.go @@ -17,6 +17,7 @@ import ( configMocks "github.com/smartcontractkit/chainlink/core/config/mocks" coremocks "github.com/smartcontractkit/chainlink/core/internal/mocks" "github.com/smartcontractkit/chainlink/core/internal/testutils/evmtest" + "github.com/smartcontractkit/chainlink/core/logger/audit" feedsMocks "github.com/smartcontractkit/chainlink/core/services/feeds/mocks" jobORMMocks "github.com/smartcontractkit/chainlink/core/services/job/mocks" keystoreMocks "github.com/smartcontractkit/chainlink/core/services/keystore/mocks" @@ -84,6 +85,8 @@ func setupFramework(t *testing.T) *gqlTestFramework { ctx = loader.InjectDataloader(context.Background(), app) ) + app.Mock.On("GetAuditLogger", mock.Anything, mock.Anything).Return(&audit.AuditLoggerService{}).Maybe() + // Setup mocks // Note - If you add a new mock make sure you assert it's expectation below. m := &mocks{ From cf792e30c6827529085bae9347177cc64a14861f Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Fri, 1 Jul 2022 13:30:02 -0700 Subject: [PATCH 16/92] audit_logger.go error handling fix --- core/logger/audit/audit_logger.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 97d8f1c4073..8ce3a275d3d 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -43,7 +43,6 @@ type serviceHeader struct { // 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) (AuditLogger, error) { - // Start parsing environment variables for audit logger auditLogsURL := os.Getenv("AUDIT_LOGS_FORWARDER_URL") if auditLogsURL == "" { @@ -59,7 +58,7 @@ func NewAuditLogger(logger logger.Logger) (AuditLogger, error) { } hostname, err := os.Hostname() if err != nil { - return &AuditLoggerService{}, errors.Errorf("Audit Log initialization error - unable to get hostname", "err", err) + return &AuditLoggerService{}, errors.Errorf("Audit Log initialization error - unable to get hostname: %s", err) } // Split and prepare optional service client headers from env variable @@ -97,7 +96,7 @@ func (l *AuditLoggerService) Audit(ctx context.Context, eventID EventID, data ma if !l.enabled { return } - l.postLogToLogService(eventID, data) + go l.postLogToLogService(eventID, data) } func (l *AuditLoggerService) postLogToLogService(eventID EventID, data map[string]interface{}) { From 22677fe50fbbf2b8bd2dd2b826590a6aba6181f2 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Tue, 13 Sep 2022 18:54:37 -0700 Subject: [PATCH 17/92] Fix issues after rebase --- core/web/dkgencrypt_keys_controller.go | 1 + core/web/dkgsign_keys_controller.go | 1 + core/web/starknet_chains_controller.go | 2 +- core/web/starknet_keys_controller.go | 2 +- core/web/starknet_nodes_controller.go | 2 +- core/web/webauthn_controller.go | 2 -- 6 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/web/dkgencrypt_keys_controller.go b/core/web/dkgencrypt_keys_controller.go index 93677fc074e..9d8069411da 100644 --- a/core/web/dkgencrypt_keys_controller.go +++ b/core/web/dkgencrypt_keys_controller.go @@ -10,6 +10,7 @@ func NewDKGEncryptKeysController(app chainlink.Application) KeysController { return NewKeysController[dkgencryptkey.Key, presenters.DKGEncryptKeyResource]( app.GetKeyStore().DKGEncrypt(), app.GetLogger(), + app.GetAuditLogger(), "dkgencryptKey", presenters.NewDKGEncryptKeyResource, presenters.NewDKGEncryptKeyResources) diff --git a/core/web/dkgsign_keys_controller.go b/core/web/dkgsign_keys_controller.go index 76c300adb9b..61c1f1b4fc9 100644 --- a/core/web/dkgsign_keys_controller.go +++ b/core/web/dkgsign_keys_controller.go @@ -10,6 +10,7 @@ func NewDKGSignKeysController(app chainlink.Application) KeysController { return NewKeysController[dkgsignkey.Key, presenters.DKGSignKeyResource]( app.GetKeyStore().DKGSign(), app.GetLogger(), + app.GetAuditLogger(), "dkgsignKey", presenters.NewDKGSignKeyResource, presenters.NewDKGSignKeyResources) diff --git a/core/web/starknet_chains_controller.go b/core/web/starknet_chains_controller.go index 6b0cf4707da..f653aaaab9e 100644 --- a/core/web/starknet_chains_controller.go +++ b/core/web/starknet_chains_controller.go @@ -9,5 +9,5 @@ import ( func NewStarkNetChainsController(app chainlink.Application) ChainsController { return newChainsController[string, *db.ChainCfg]("starknet", app.GetChains().StarkNet, ErrStarkNetNotEnabled, - func(s string) (string, error) { return s, nil }, presenters.NewStarkNetChainResource) + func(s string) (string, error) { return s, nil }, presenters.NewStarkNetChainResource, app.GetLogger(), app.GetAuditLogger()) } diff --git a/core/web/starknet_keys_controller.go b/core/web/starknet_keys_controller.go index 07ac71a76f9..93e32d38975 100644 --- a/core/web/starknet_keys_controller.go +++ b/core/web/starknet_keys_controller.go @@ -7,6 +7,6 @@ import ( ) func NewStarkNetKeysController(app chainlink.Application) KeysController { - return NewKeysController[starkkey.Key, presenters.StarkNetKeyResource](app.GetKeyStore().StarkNet(), app.GetLogger(), + return NewKeysController[starkkey.Key, presenters.StarkNetKeyResource](app.GetKeyStore().StarkNet(), app.GetLogger(), app.GetAuditLogger(), "starknetKey", presenters.NewStarkNetKeyResource, presenters.NewStarkNetKeyResources) } diff --git a/core/web/starknet_nodes_controller.go b/core/web/starknet_nodes_controller.go index 690ef797a58..2886a906c31 100644 --- a/core/web/starknet_nodes_controller.go +++ b/core/web/starknet_nodes_controller.go @@ -39,5 +39,5 @@ func NewStarkNetNodesController(app chainlink.Application) NodesController { ChainID: request.ChainID, URL: request.URL, }, nil - }) + }, app.GetLogger(), app.GetAuditLogger()) } diff --git a/core/web/webauthn_controller.go b/core/web/webauthn_controller.go index 9a4c334fb79..c6cd562ebc5 100644 --- a/core/web/webauthn_controller.go +++ b/core/web/webauthn_controller.go @@ -3,11 +3,9 @@ package web import ( "encoding/json" "errors" - "fmt" "net/http" "github.com/gin-gonic/gin" - "github.com/pkg/errors" "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/logger" From deb43a2252367eaef902297721561ada4c6b9602 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Tue, 13 Sep 2022 20:07:43 -0700 Subject: [PATCH 18/92] Remove a line accidentally added by rebase --- core/cmd/client_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/cmd/client_test.go b/core/cmd/client_test.go index 67ec773bc7d..448e5036135 100644 --- a/core/cmd/client_test.go +++ b/core/cmd/client_test.go @@ -136,7 +136,6 @@ func TestTerminalAPIInitializer_InitializeWithoutAPIUser(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { db := pgtest.NewSqlxDB(t) - orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t)) orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cltest.NewTestGeneralConfig(t), &audit.AuditLoggerService{}) mock := &cltest.MockCountingPrompter{T: t, EnteredStrings: test.enteredStrings, NotTerminal: !test.isTerminal} From 01fba6ef0eb4a53b0e0446943deb682bef82d26a Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Wed, 14 Sep 2022 14:43:34 -0700 Subject: [PATCH 19/92] Move to a single green thread instead of many --- core/logger/audit/audit_logger.go | 56 ++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 8ce3a275d3d..20d1e2116e5 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -16,7 +16,10 @@ import ( "github.com/pkg/errors" ) +const AUDIT_LOGS_CAPACITY = 2048 + type AuditLogger interface { + //Audit(ctx context.Context, eventID EventID, data map[string]interface{}) Audit(ctx context.Context, eventID EventID, data map[string]interface{}) } @@ -29,6 +32,7 @@ type AuditLoggerService struct { environmentName string hostname string localIP string + loggingChannel chan WrappedAuditLog } // Configurable headers to include in POST to log service @@ -37,6 +41,11 @@ type serviceHeader struct { value string } +type WrappedAuditLog struct { + eventID EventID + data map[string]interface{} +} + // NewAuditLogger returns a buffer push system that ingests audit log events and // asynchronously pushes them up to an HTTP log service. // Parses and validates the AUDIT_LOGS_* environment values and returns an enabled @@ -78,6 +87,8 @@ func NewAuditLogger(logger logger.Logger) (AuditLogger, error) { } } + loggingChannel := make(chan WrappedAuditLog, AUDIT_LOGS_CAPACITY) + // Finally, create new auditLogger with parameters auditLogger := AuditLoggerService{ logger: logger.Helper(1), @@ -88,17 +99,60 @@ func NewAuditLogger(logger logger.Logger) (AuditLogger, error) { environmentName: env, hostname: hostname, localIP: getLocalIP(), + loggingChannel: loggingChannel, } + + // Start our go routine that will receive logs and send them out to the + // configured service. + go auditLogger.auditLoggerRoutine() + return &auditLogger, nil } +// / Entrypoint for new audit logs. This buffers all logs that come in they will +// / sent out by the goroutine that was started when the AuditLoggerService was +// / created. If this service was not enabled, this immeidately returns. +// / +// / This function never blocks. func (l *AuditLoggerService) Audit(ctx context.Context, eventID EventID, data map[string]interface{}) { if !l.enabled { return } - go l.postLogToLogService(eventID, data) + + wrappedLog := WrappedAuditLog{ + eventID: eventID, + data: data, + } + + select { + case l.loggingChannel <- wrappedLog: + default: + if len(l.loggingChannel) == AUDIT_LOGS_CAPACITY { + l.logger.Errorw("Audit log buffer is full. Dropping log with eventID: %s", eventID) + } else { + l.logger.Errorw("Could not send log to audit subsystem even though queue has %d space", AUDIT_LOGS_CAPACITY-len(l.loggingChannel)) + } + } +} + +// / Entrypoint for our log handling goroutine. This waits on the channel and sends out +// / logs as they come in. +// / +// / This function calls postLogToLogService which blocks. +func (l *AuditLoggerService) auditLoggerRoutine() { + for event := range l.loggingChannel { + l.postLogToLogService(event.eventID, event.data) + } + + l.logger.Errorw("Audit logger is shut down. Will not send requested audit log") } +// / Takes an EventID and associated data and sends it to the configured logging +// / endpoint. This function blocks on the send by timesout after a period of +// / several seconds. This helps us prevent getting stuck on a single log +// / due to transient network errors. +// / +// / This function blocks when called. func (l *AuditLoggerService) postLogToLogService(eventID EventID, data map[string]interface{}) { // Audit log JSON data logItem := map[string]interface{}{ From ab09a614aa294f03b9a810671413e097f5bd89c2 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Thu, 15 Sep 2022 09:59:01 -0700 Subject: [PATCH 20/92] Update changelog --- docs/CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 80d8d4586ea..4413cded406 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -19,6 +19,9 @@ as an estimated gas limit (up to `ETH_GAS_LIMIT_MAX`, with `1,000,000,000` defau - manually set the nonce for a key See [this PR](https://github.com/smartcontractkit/chainlink/pull/7406) for a screenshot example. +- New optional external logger added + - To opt in to receiving audit log events over HEC of actions performed within the node, set the `AUDIT_LOGS_FORWARDER_URL` and `AUDIT_LOGS_FORWARDER_HEADERS` environment variables accordingly. + ## 1.8.0 - 2022-09-01 ### Added @@ -143,8 +146,6 @@ For backward compatibility all insecure passwords will continue to work, however - `$(jobRun.blockReceiptsRoot)` : the root of the receipts trie of the block (hash) - `$(jobRun.blockTransactionsRoot)` : the root of the transaction trie of the block (hash) - `$(jobRun.blockStateRoot)` : the root of the final state trie of the block (hash) -- New optional Splunk logger added - - To opt in to receiving audit log events over HEC of actions performed within the node, set the `SPLUNK_URL` and `SPLUNK_TOKEN` environment variables accordingly. `SPLUNK_URL` is in the format of 'https://xxxxx.splunkcloud.com/services/collector' - `ethtx` tasks can now be configured to error if the transaction reverts on-chain. You must set `failOnRevert=true` on the task to enable this behavior, like so: `foo [type=ethtx failOnRevert=true ...]` From 6d7d8a6fdcc1a300de4970809941c5c922d14723 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Thu, 15 Sep 2022 10:00:07 -0700 Subject: [PATCH 21/92] Remove comment --- core/logger/audit/audit_logger.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 20d1e2116e5..5617b3e00c3 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -19,7 +19,6 @@ import ( const AUDIT_LOGS_CAPACITY = 2048 type AuditLogger interface { - //Audit(ctx context.Context, eventID EventID, data map[string]interface{}) Audit(ctx context.Context, eventID EventID, data map[string]interface{}) } From 714cb7aab7d11ce36174d1ed0b072b83e40b42b5 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Thu, 15 Sep 2022 10:22:45 -0700 Subject: [PATCH 22/92] Fix webauthn formatting --- core/web/webauthn_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/web/webauthn_controller.go b/core/web/webauthn_controller.go index c6cd562ebc5..e7a49c6a5b6 100644 --- a/core/web/webauthn_controller.go +++ b/core/web/webauthn_controller.go @@ -7,8 +7,8 @@ import ( "github.com/gin-gonic/gin" - "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/sessions" "github.com/smartcontractkit/chainlink/core/web/auth" From e8748c15d9f25151b9df60546c315a3ad915a274 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Thu, 15 Sep 2022 10:28:30 -0700 Subject: [PATCH 23/92] Type alias --- core/logger/audit/audit_logger.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 5617b3e00c3..d1d83fe9208 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -18,8 +18,10 @@ import ( const AUDIT_LOGS_CAPACITY = 2048 +type Data = map[string]any + type AuditLogger interface { - Audit(ctx context.Context, eventID EventID, data map[string]interface{}) + Audit(ctx context.Context, eventID EventID, data Data) } type AuditLoggerService struct { @@ -42,7 +44,7 @@ type serviceHeader struct { type WrappedAuditLog struct { eventID EventID - data map[string]interface{} + data Data } // NewAuditLogger returns a buffer push system that ingests audit log events and @@ -113,7 +115,7 @@ func NewAuditLogger(logger logger.Logger) (AuditLogger, error) { // / created. If this service was not enabled, this immeidately returns. // / // / This function never blocks. -func (l *AuditLoggerService) Audit(ctx context.Context, eventID EventID, data map[string]interface{}) { +func (l *AuditLoggerService) Audit(ctx context.Context, eventID EventID, data Data) { if !l.enabled { return } @@ -152,7 +154,7 @@ func (l *AuditLoggerService) auditLoggerRoutine() { // / due to transient network errors. // / // / This function blocks when called. -func (l *AuditLoggerService) postLogToLogService(eventID EventID, data map[string]interface{}) { +func (l *AuditLoggerService) postLogToLogService(eventID EventID, data Data) { // Audit log JSON data logItem := map[string]interface{}{ "eventID": eventID, From cbc31fdc2b33667faf1dcfe48dafd676227b4c76 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Thu, 15 Sep 2022 15:07:28 -0700 Subject: [PATCH 24/92] Change const from SCREAMING_SNAKE_CASE to lowerCamelCase --- core/logger/audit/audit_logger.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index d1d83fe9208..9d7be173bc3 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -16,7 +16,7 @@ import ( "github.com/pkg/errors" ) -const AUDIT_LOGS_CAPACITY = 2048 +const bufferCapacity = 2048 type Data = map[string]any @@ -88,7 +88,7 @@ func NewAuditLogger(logger logger.Logger) (AuditLogger, error) { } } - loggingChannel := make(chan WrappedAuditLog, AUDIT_LOGS_CAPACITY) + loggingChannel := make(chan WrappedAuditLog, bufferCapacity) // Finally, create new auditLogger with parameters auditLogger := AuditLoggerService{ @@ -128,10 +128,10 @@ func (l *AuditLoggerService) Audit(ctx context.Context, eventID EventID, data Da select { case l.loggingChannel <- wrappedLog: default: - if len(l.loggingChannel) == AUDIT_LOGS_CAPACITY { + if len(l.loggingChannel) == bufferCapacity { l.logger.Errorw("Audit log buffer is full. Dropping log with eventID: %s", eventID) } else { - l.logger.Errorw("Could not send log to audit subsystem even though queue has %d space", AUDIT_LOGS_CAPACITY-len(l.loggingChannel)) + l.logger.Errorw("Could not send log to audit subsystem even though queue has %d space", bufferCapacity-len(l.loggingChannel)) } } } From c8e9e76ef186c857fe89a205a3ad0903c1546b13 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Thu, 15 Sep 2022 17:13:10 -0700 Subject: [PATCH 25/92] Make configuration not require environment variables in the AuditLogger constructor --- core/config/envvar/schema.go | 5 ++ core/config/general_config.go | 28 ++++++++ core/config/v2/types.go | 2 + core/logger/audit/audit_logger.go | 80 ++++++++++++++--------- core/services/chainlink/application.go | 2 +- core/services/chainlink/config_general.go | 5 ++ docs/CHANGELOG.md | 38 ++++++++++- 7 files changed, 126 insertions(+), 34 deletions(-) diff --git a/core/config/envvar/schema.go b/core/config/envvar/schema.go index c23e76fbe0b..77d4c3dd60a 100644 --- a/core/config/envvar/schema.go +++ b/core/config/envvar/schema.go @@ -60,6 +60,11 @@ type ConfigSchema struct { TelemetryIngressUseBatchSend bool `env:"TELEMETRY_INGRESS_USE_BATCH_SEND" default:"true"` ShutdownGracePeriod time.Duration `env:"SHUTDOWN_GRACE_PERIOD" default:"5s"` + // Audit Logger + AuditLoggerForwardToUrl string `env:"AUDIT_LOGGER_FORWARD_TO_URL" default:""` + AuditLoggerHeaders string `env:"AUDIT_LOGGER_HEADERS" default:""` + AuditLoggerJsonWrapperKey string `env:"AUDIT_LOGGER_JSON_WRAPPER_KEY" default:""` + // Database DatabaseListenerMaxReconnectDuration time.Duration `env:"DATABASE_LISTENER_MAX_RECONNECT_DURATION" default:"10m"` //nodoc DatabaseListenerMinReconnectInterval time.Duration `env:"DATABASE_LISTENER_MIN_RECONNECT_INTERVAL" default:"1m"` //nodoc diff --git a/core/config/general_config.go b/core/config/general_config.go index e3d9f24d4e0..b16cf0e3d1e 100644 --- a/core/config/general_config.go +++ b/core/config/general_config.go @@ -24,6 +24,7 @@ import ( "github.com/smartcontractkit/chainlink/core/config/envvar" "github.com/smartcontractkit/chainlink/core/config/parse" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/static" "github.com/smartcontractkit/chainlink/core/store/dialects" "github.com/smartcontractkit/chainlink/core/store/models" @@ -74,6 +75,7 @@ type GeneralOnlyConfig interface { AdvisoryLockID() int64 AllowOrigins() string AppID() uuid.UUID + AuditLogger() *audit.AuditLoggerConfig AuthenticatedRateLimit() int64 AuthenticatedRateLimitPeriod() models.Duration AutoPprofBlockProfileRate() int @@ -483,6 +485,32 @@ func (c *generalConfig) AppID() uuid.UUID { return c.appID } +// Create the audit logger configuration to send events to an +// external service +func (c *generalConfig) AuditLogger() *audit.AuditLoggerConfig { + forwardToUrl := c.viper.GetString(envvar.Name("AuditLoggerForwardToUrl")) + // We have this check here to determine if we should enable the audit logger + // at all. If this is not set, then we don't need to output the error below + // when configuration fails. + if forwardToUrl == "" { + return nil + } + + auditLogger, err := audit.NewAuditLoggerConfig( + forwardToUrl, + c.viper.GetBool(envvar.Name("Dev")), + c.viper.GetString(envvar.Name("AuditLoggerJsonWrapperKey")), + c.viper.GetString(envvar.Name("AuditLoggerHeaders")), + ) + + if err != nil { + c.lggr.Errorf("Audit logger configuration failed with: %s", err) + return nil + } + + return &auditLogger +} + // AuthenticatedRateLimit defines the threshold to which authenticated requests // get limited. More than this many requests per AuthenticatedRateLimitPeriod will be rejected. func (c *generalConfig) AuthenticatedRateLimit() int64 { diff --git a/core/config/v2/types.go b/core/config/v2/types.go index f14ba1b9d0c..d332b862081 100644 --- a/core/config/v2/types.go +++ b/core/config/v2/types.go @@ -10,6 +10,7 @@ import ( "go.uber.org/multierr" "github.com/smartcontractkit/chainlink/core/config" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/core/store/models" @@ -19,6 +20,7 @@ import ( // Core holds the core configuration. See chainlink.Config for more information. type Core struct { // General/misc + AuditLogger *audit.AuditLoggerConfig ExplorerURL *models.URL InsecureFastScrypt *bool RootDir *string diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 9d7be173bc3..6b428b10763 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -24,6 +24,47 @@ type AuditLogger interface { Audit(ctx context.Context, eventID EventID, data Data) } +type AuditLoggerConfig struct { + forwardToUrl string + environment string + jsonWrapperKey string + headers []serviceHeader +} + +func NewAuditLoggerConfig(forwardToUrl string, isDev bool, jsonWrapperKey string, encodedHeaders string) (AuditLoggerConfig, error) { + environment := "production" + if isDev { + environment = "develop" + } + + if forwardToUrl == "" { + return AuditLoggerConfig{}, errors.Errorf("No forwardToURL provided") + } + + // Split and prepare optional service client headers from env variable + headers := []serviceHeader{} + if encodedHeaders != "" { + headerLines := strings.Split(encodedHeaders, "\\") + for _, header := range headerLines { + keyValue := strings.Split(header, "||") + if len(keyValue) != 2 { + return AuditLoggerConfig{}, errors.Errorf("Invalid headers provided for the audit logger. Value, single pair split on || required, got: %s", keyValue) + } + headers = append(headers, serviceHeader{ + header: keyValue[0], + value: keyValue[1], + }) + } + } + + return AuditLoggerConfig{ + forwardToUrl: forwardToUrl, + environment: environment, + jsonWrapperKey: jsonWrapperKey, + headers: headers, + }, nil +} + type AuditLoggerService struct { logger logger.Logger enabled bool @@ -52,52 +93,27 @@ type WrappedAuditLog struct { // 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) (AuditLogger, error) { - // Start parsing environment variables for audit logger - auditLogsURL := os.Getenv("AUDIT_LOGS_FORWARDER_URL") - if auditLogsURL == "" { - // Unset, return a disabled audit logger - logger.Info("No AUDIT_LOGS_FORWARDER_URL environment set, audit log events will not be captured") - +func NewAuditLogger(logger logger.Logger, config *AuditLoggerConfig) (AuditLogger, error) { + if config == nil { + logger.Info("Audit logger configuration is nil. Cannot start audit logger subsystem and audit events will not be captured.") return &AuditLoggerService{}, nil } - env := "production" - if os.Getenv("CHAINLINK_DEV") == "true" { - env = "develop" - } hostname, err := os.Hostname() if err != nil { return &AuditLoggerService{}, errors.Errorf("Audit Log initialization error - unable to get hostname: %s", err) } - // Split and prepare optional service client headers from env variable - headers := []serviceHeader{} - headersEncoded := os.Getenv("AUDIT_LOGS_FORWARDER_HEADERS") - if headersEncoded != "" { - headerLines := strings.Split(headersEncoded, "\\") - for _, header := range headerLines { - keyValue := strings.Split(header, "||") - if len(keyValue) != 2 { - return &AuditLoggerService{}, errors.Errorf("Invalid AUDIT_LOGS_FORWARDER_HEADERS value, single pair split on || required, got: %s", keyValue) - } - headers = append(headers, serviceHeader{ - header: keyValue[0], - value: keyValue[1], - }) - } - } - loggingChannel := make(chan WrappedAuditLog, bufferCapacity) // Finally, create new auditLogger with parameters auditLogger := AuditLoggerService{ logger: logger.Helper(1), enabled: true, - serviceURL: auditLogsURL, - serviceHeaders: headers, - jsonWrapperKey: os.Getenv("AUDIT_LOGS_FORWARDER_JSON_WRAPPER_KEY"), - environmentName: env, + serviceURL: config.forwardToUrl, + serviceHeaders: config.headers, + jsonWrapperKey: config.jsonWrapperKey, + environmentName: config.environment, hostname: hostname, localIP: getLocalIP(), loggingChannel: loggingChannel, diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 7eb7c3bfe60..e9bc45eafca 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -202,7 +202,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { unrestrictedHTTPClient := opts.UnrestrictedHTTPClient // Configure and optionally start the audit log forwarder service - auditLogger, err := audit.NewAuditLogger(globalLogger) + auditLogger, err := audit.NewAuditLogger(globalLogger, cfg.AuditLogger()) if err != nil { return nil, errors.Errorf("Unable to initialize audit logger: %s", err) } diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 5464be13b45..3cf9db35d62 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -21,6 +21,7 @@ import ( coreconfig "github.com/smartcontractkit/chainlink/core/config" v2 "github.com/smartcontractkit/chainlink/core/config/v2" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/core/store/dialects" @@ -166,6 +167,10 @@ func (g *generalConfig) AllowOrigins() string { return *g.c.WebServer.AllowOrigins } +func (g *generalConfig) AuditLogger() *audit.AuditLoggerConfig { + return g.c.AuditLogger +} + func (g *generalConfig) AuthenticatedRateLimit() int64 { return *g.c.WebServer.RateLimit.Authenticated } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 4413cded406..def125b86d5 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -20,7 +20,43 @@ as an estimated gas limit (up to `ETH_GAS_LIMIT_MAX`, with `1,000,000,000` defau See [this PR](https://github.com/smartcontractkit/chainlink/pull/7406) for a screenshot example. - New optional external logger added - - To opt in to receiving audit log events over HEC of actions performed within the node, set the `AUDIT_LOGS_FORWARDER_URL` and `AUDIT_LOGS_FORWARDER_HEADERS` environment variables accordingly. + ### AUDIT_LOGGER_FORWARD_TO_URL + + - Default: _none_ + + When set, this environment variable configures and enables an optional HTTP logger which is used specifcally to send audit log events. Audit logs events are emitted when specific actions are performed by any of the users through the node's API. The value of this variable should be a full URL. Log items will be sent via POST + + There are audit log implemented for the following events: + - Auth & Sessions (new session, login success, login failed, 2FA enrolled, 2FA failed, password reset, password reset failed, etc.) + - CRUD actions for all resources (add/create/delete resources such as bridges, nodes, keys) + - Sensitive actions (keys exported/imported, config changed, log level changed, environment dumped) + + A full list of audit log enum types can be found in the source within the `audit` package (`audit_types.go`). + + The following `AUDIT_LOGGER_*` environment variables below configure this optional audit log HTTP forwarder. + + ### AUDIT_LOGGER_HEADERS + + - Default: _none_ + + An optional list of HTTP headers to be added for every optional audit log event. If the above `AUDIT_LOGGER_FORWARD_TO_URL` is set, audit log events will be POSTed to that URL, and will include headers specified in this environment variable. One example use case is auth for example: ```AUDIT_LOGGER_HEADERS="Authorization||{{token}}"```. + + Header keys and values are delimited on ||, and multiple headers can be added with a forward slash delimiter ('\\'). An example of multiple key value pairs: + ```AUDIT_LOGGER_HEADERS="Authorization||{{token}}\Some-Other-Header||{{token2}}"``` + + ### AUDIT_LOGGER_JSON_WRAPPER_KEY + + - Default: _none_ + + When the audit log HTTP forwarder is enabled, if there is a value set for this optional environment variable then the POST body will be wrapped in a dictionary in a field specified by the value of set variable. This is to help enable specific logging service integrations that may require the event JSON in a special shape. For example: `AUDIT_LOGGER_JSON_WRAPPER_KEY=event` will create the POST body: + ``` + { + "event": { + "eventID": EVENT_ID_ENUM, + "data": ... + } + } + ``` ## 1.8.0 - 2022-09-01 From 069c57c96cca99b8564897530a26e7c567847871 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Thu, 15 Sep 2022 17:38:10 -0700 Subject: [PATCH 26/92] Improve error handling --- core/logger/audit/audit_logger.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 6b428b10763..afc17ccf826 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -146,8 +146,10 @@ func (l *AuditLoggerService) Audit(ctx context.Context, eventID EventID, data Da default: if len(l.loggingChannel) == bufferCapacity { l.logger.Errorw("Audit log buffer is full. Dropping log with eventID: %s", eventID) + } else if l.loggingChannel == nil { + l.logger.Errorw("Could not send log to audit subsystem because it has gone away!") } else { - l.logger.Errorw("Could not send log to audit subsystem even though queue has %d space", bufferCapacity-len(l.loggingChannel)) + l.logger.Errorw("An unknown error has occured in the audit logging subsystem and the audit log was dropped") } } } From f66ab6242e08abef9abe63071d5dc5db4aa5e73f Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Thu, 15 Sep 2022 17:50:26 -0700 Subject: [PATCH 27/92] Fix spelling mistake --- core/logger/audit/audit_logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index afc17ccf826..cc4b8fa417b 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -149,7 +149,7 @@ func (l *AuditLoggerService) Audit(ctx context.Context, eventID EventID, data Da } else if l.loggingChannel == nil { l.logger.Errorw("Could not send log to audit subsystem because it has gone away!") } else { - l.logger.Errorw("An unknown error has occured in the audit logging subsystem and the audit log was dropped") + l.logger.Errorw("An unknown error has occurred in the audit logging subsystem and the audit log was dropped") } } } From 6c97ac480deb124abcb2cdd537b59a963794b4f4 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 00:49:50 -0700 Subject: [PATCH 28/92] Regenerate mocks --- core/config/mocks/general_config.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/core/config/mocks/general_config.go b/core/config/mocks/general_config.go index e169ae0dae3..5705c7430c4 100644 --- a/core/config/mocks/general_config.go +++ b/core/config/mocks/general_config.go @@ -3,9 +3,10 @@ package mocks import ( - big "math/big" - assets "github.com/smartcontractkit/chainlink/core/assets" + audit "github.com/smartcontractkit/chainlink/core/logger/audit" + + big "math/big" commontypes "github.com/smartcontractkit/libocr/commontypes" @@ -101,6 +102,22 @@ func (_m *GeneralConfig) AppID() uuid.UUID { return r0 } +// AuditLogger provides a mock function with given fields: +func (_m *GeneralConfig) AuditLogger() *audit.AuditLoggerConfig { + ret := _m.Called() + + var r0 *audit.AuditLoggerConfig + if rf, ok := ret.Get(0).(func() *audit.AuditLoggerConfig); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*audit.AuditLoggerConfig) + } + } + + return r0 +} + // AuthenticatedRateLimit provides a mock function with given fields: func (_m *GeneralConfig) AuthenticatedRateLimit() int64 { ret := _m.Called() From dad89d9726ee8bf55c0104f16b7c7f3f8d2f6f9d Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 00:56:59 -0700 Subject: [PATCH 29/92] Address racy len --- .../evm/config/mocks/chain_scoped_config.go | 21 +++++++++++++++++-- core/logger/audit/audit_logger.go | 6 ++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/core/chains/evm/config/mocks/chain_scoped_config.go b/core/chains/evm/config/mocks/chain_scoped_config.go index b49ce82ea11..ad44412a8d7 100644 --- a/core/chains/evm/config/mocks/chain_scoped_config.go +++ b/core/chains/evm/config/mocks/chain_scoped_config.go @@ -3,9 +3,10 @@ package mocks import ( - big "math/big" - assets "github.com/smartcontractkit/chainlink/core/assets" + audit "github.com/smartcontractkit/chainlink/core/logger/audit" + + big "math/big" common "github.com/ethereum/go-ethereum/common" @@ -105,6 +106,22 @@ func (_m *ChainScopedConfig) AppID() uuid.UUID { return r0 } +// AuditLogger provides a mock function with given fields: +func (_m *ChainScopedConfig) AuditLogger() *audit.AuditLoggerConfig { + ret := _m.Called() + + var r0 *audit.AuditLoggerConfig + if rf, ok := ret.Get(0).(func() *audit.AuditLoggerConfig); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*audit.AuditLoggerConfig) + } + } + + return r0 +} + // AuthenticatedRateLimit provides a mock function with given fields: func (_m *ChainScopedConfig) AuthenticatedRateLimit() int64 { ret := _m.Called() diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index cc4b8fa417b..42782a6faf5 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -144,12 +144,10 @@ func (l *AuditLoggerService) Audit(ctx context.Context, eventID EventID, data Da select { case l.loggingChannel <- wrappedLog: default: - if len(l.loggingChannel) == bufferCapacity { - l.logger.Errorw("Audit log buffer is full. Dropping log with eventID: %s", eventID) - } else if l.loggingChannel == nil { + if l.loggingChannel == nil { l.logger.Errorw("Could not send log to audit subsystem because it has gone away!") } else { - l.logger.Errorw("An unknown error has occurred in the audit logging subsystem and the audit log was dropped") + l.logger.Errorw("Audit log buffer is full. Dropping log with eventID: %s", eventID) } } } From 1451531cea601bcdf12963dd4201862d2549e1bf Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 01:47:59 -0700 Subject: [PATCH 30/92] Add variables to test --- core/config/envvar/schema_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/config/envvar/schema_test.go b/core/config/envvar/schema_test.go index e9e525bad8f..8ee2d5033ad 100644 --- a/core/config/envvar/schema_test.go +++ b/core/config/envvar/schema_test.go @@ -13,6 +13,9 @@ func TestConfigSchema(t *testing.T) { items := map[string]string{ "AdvisoryLockCheckInterval": "ADVISORY_LOCK_CHECK_INTERVAL", "AdvisoryLockID": "ADVISORY_LOCK_ID", + "AuditLoggerForwardToUrl": "AUDIT_LOGGER_FORWARD_TO_URL", + "AuditLoggerHeaders": "AUDIT_LOGGER_HEADERS", + "AuditLoggerJsonWrapperKey": "AUDIT_LOGGER_JSON_WRAPPER_KEY", "AllowOrigins": "ALLOW_ORIGINS", "AuthenticatedRateLimit": "AUTHENTICATED_RATE_LIMIT", "AuthenticatedRateLimitPeriod": "AUTHENTICATED_RATE_LIMIT_PERIOD", From bfcddbc571316b182d582aa416ec7d4577376e33 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 09:52:06 -0700 Subject: [PATCH 31/92] Remove old logger changes --- core/logger/logger.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/logger/logger.go b/core/logger/logger.go index 051b8a0e922..448b53b11c0 100644 --- a/core/logger/logger.go +++ b/core/logger/logger.go @@ -215,8 +215,6 @@ type Config struct { FileMaxSizeMB int FileMaxAgeDays int FileMaxBackups int // files - Hostname string - ChainlinkDev bool } // New returns a new Logger with pretty printing to stdout, prometheus counters, and sentry forwarding. From 708d2b3c51fbb68b5c58f5f3c8db70297067f91b Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 10:01:37 -0700 Subject: [PATCH 32/92] Rename the audit config structure --- core/chains/evm/config/mocks/chain_scoped_config.go | 4 ++-- core/config/general_config.go | 8 ++++---- core/config/mocks/general_config.go | 4 ++-- core/services/chainlink/application.go | 2 +- core/services/chainlink/config_general.go | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/chains/evm/config/mocks/chain_scoped_config.go b/core/chains/evm/config/mocks/chain_scoped_config.go index ad44412a8d7..ffc493515df 100644 --- a/core/chains/evm/config/mocks/chain_scoped_config.go +++ b/core/chains/evm/config/mocks/chain_scoped_config.go @@ -106,8 +106,8 @@ func (_m *ChainScopedConfig) AppID() uuid.UUID { return r0 } -// AuditLogger provides a mock function with given fields: -func (_m *ChainScopedConfig) AuditLogger() *audit.AuditLoggerConfig { +// AuditLoggerConfig provides a mock function with given fields: +func (_m *ChainScopedConfig) AuditLoggerConfig() *audit.AuditLoggerConfig { ret := _m.Called() var r0 *audit.AuditLoggerConfig diff --git a/core/config/general_config.go b/core/config/general_config.go index b16cf0e3d1e..28c30b6d38a 100644 --- a/core/config/general_config.go +++ b/core/config/general_config.go @@ -75,7 +75,7 @@ type GeneralOnlyConfig interface { AdvisoryLockID() int64 AllowOrigins() string AppID() uuid.UUID - AuditLogger() *audit.AuditLoggerConfig + AuditLoggerConfig() *audit.AuditLoggerConfig AuthenticatedRateLimit() int64 AuthenticatedRateLimitPeriod() models.Duration AutoPprofBlockProfileRate() int @@ -487,7 +487,7 @@ func (c *generalConfig) AppID() uuid.UUID { // Create the audit logger configuration to send events to an // external service -func (c *generalConfig) AuditLogger() *audit.AuditLoggerConfig { +func (c *generalConfig) AuditLoggerConfig() *audit.AuditLoggerConfig { forwardToUrl := c.viper.GetString(envvar.Name("AuditLoggerForwardToUrl")) // We have this check here to determine if we should enable the audit logger // at all. If this is not set, then we don't need to output the error below @@ -496,7 +496,7 @@ func (c *generalConfig) AuditLogger() *audit.AuditLoggerConfig { return nil } - auditLogger, err := audit.NewAuditLoggerConfig( + auditLoggerConfig, err := audit.NewAuditLoggerConfig( forwardToUrl, c.viper.GetBool(envvar.Name("Dev")), c.viper.GetString(envvar.Name("AuditLoggerJsonWrapperKey")), @@ -508,7 +508,7 @@ func (c *generalConfig) AuditLogger() *audit.AuditLoggerConfig { return nil } - return &auditLogger + return &auditLoggerConfig } // AuthenticatedRateLimit defines the threshold to which authenticated requests diff --git a/core/config/mocks/general_config.go b/core/config/mocks/general_config.go index 5705c7430c4..164496b77bc 100644 --- a/core/config/mocks/general_config.go +++ b/core/config/mocks/general_config.go @@ -102,8 +102,8 @@ func (_m *GeneralConfig) AppID() uuid.UUID { return r0 } -// AuditLogger provides a mock function with given fields: -func (_m *GeneralConfig) AuditLogger() *audit.AuditLoggerConfig { +// AuditLoggerConfig provides a mock function with given fields: +func (_m *GeneralConfig) AuditLoggerConfig() *audit.AuditLoggerConfig { ret := _m.Called() var r0 *audit.AuditLoggerConfig diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index e9bc45eafca..31b6d2ece79 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -202,7 +202,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { unrestrictedHTTPClient := opts.UnrestrictedHTTPClient // Configure and optionally start the audit log forwarder service - auditLogger, err := audit.NewAuditLogger(globalLogger, cfg.AuditLogger()) + auditLogger, err := audit.NewAuditLogger(globalLogger, cfg.AuditLoggerConfig()) if err != nil { return nil, errors.Errorf("Unable to initialize audit logger: %s", err) } diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 3cf9db35d62..c1b22c34d0d 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -167,7 +167,7 @@ func (g *generalConfig) AllowOrigins() string { return *g.c.WebServer.AllowOrigins } -func (g *generalConfig) AuditLogger() *audit.AuditLoggerConfig { +func (g *generalConfig) AuditLoggerConfig() *audit.AuditLoggerConfig { return g.c.AuditLogger } From 76febb314c2483be0690e0a06f3c6ffecbaa166b Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 10:29:40 -0700 Subject: [PATCH 33/92] Renaming, mocks, try to fix config --- core/config/v2/types.go | 2 +- core/services/chainlink/config_general.go | 2 +- core/services/chainlink/testdata/config-full.toml | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/config/v2/types.go b/core/config/v2/types.go index d332b862081..c2aef7a50d2 100644 --- a/core/config/v2/types.go +++ b/core/config/v2/types.go @@ -20,7 +20,7 @@ import ( // Core holds the core configuration. See chainlink.Config for more information. type Core struct { // General/misc - AuditLogger *audit.AuditLoggerConfig + AuditLoggerConfig *audit.AuditLoggerConfig ExplorerURL *models.URL InsecureFastScrypt *bool RootDir *string diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index c1b22c34d0d..1643f451bab 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -168,7 +168,7 @@ func (g *generalConfig) AllowOrigins() string { } func (g *generalConfig) AuditLoggerConfig() *audit.AuditLoggerConfig { - return g.c.AuditLogger + return g.c.AuditLoggerConfig } func (g *generalConfig) AuthenticatedRateLimit() int64 { diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 01a568a8c2e..d0c69fa5ac8 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -3,6 +3,11 @@ InsecureFastScrypt = true RootDir = 'test/root/dir' ShutdownGracePeriod = '10s' +[AuditLoggerConfig] +forwardToUrl = "http://localhost:9898" +headers = [] +environment = "develop" + [Feature] FeedsManager = true LogPoller = true From 1f230ecb9cf24fa0090f99e91fdbae511c69c714 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 10:59:20 -0700 Subject: [PATCH 34/92] Remove context from audit calls --- core/logger/audit/audit_logger.go | 5 +- core/sessions/orm.go | 11 ++- core/web/bridge_types_controller.go | 7 +- core/web/chains_controller.go | 6 +- core/web/config_controller.go | 7 +- core/web/csa_keys_controller.go | 6 +- core/web/eth_keys_controller.go | 16 ++-- core/web/evm_forwarders_controller.go | 4 +- core/web/evm_transfer_controller.go | 2 +- core/web/external_initiators_controller.go | 4 +- core/web/jobs_controller.go | 4 +- core/web/keys_controller.go | 16 ++-- core/web/log_controller.go | 6 +- core/web/nodes_controller.go | 4 +- core/web/ocr2_keys_controller.go | 8 +- core/web/ocr_keys_controller.go | 8 +- core/web/p2p_keys_controller.go | 8 +- .../pipeline_job_spec_errors_controller.go | 2 +- core/web/pipeline_runs_controller.go | 2 +- core/web/resolver/mutation.go | 80 +++++++++---------- core/web/sessions_controller.go | 2 +- core/web/solana_transfer_controller.go | 2 +- core/web/terra_transfer_controller.go | 2 +- core/web/user_controller.go | 12 +-- core/web/vrf_keys_controller.go | 8 +- core/web/webauthn_controller.go | 2 +- 26 files changed, 117 insertions(+), 117 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 42782a6faf5..28487ce9065 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -2,7 +2,6 @@ package audit import ( "bytes" - "context" "encoding/json" "io/ioutil" "net" @@ -21,7 +20,7 @@ const bufferCapacity = 2048 type Data = map[string]any type AuditLogger interface { - Audit(ctx context.Context, eventID EventID, data Data) + Audit(eventID EventID, data Data) } type AuditLoggerConfig struct { @@ -131,7 +130,7 @@ func NewAuditLogger(logger logger.Logger, config *AuditLoggerConfig) (AuditLogge // / created. If this service was not enabled, this immeidately returns. // / // / This function never blocks. -func (l *AuditLoggerService) Audit(ctx context.Context, eventID EventID, data Data) { +func (l *AuditLoggerService) Audit(eventID EventID, data Data) { if !l.enabled { return } diff --git a/core/sessions/orm.go b/core/sessions/orm.go index 15683e3888d..6f871d6397a 100644 --- a/core/sessions/orm.go +++ b/core/sessions/orm.go @@ -1,7 +1,6 @@ package sessions import ( - "context" "crypto/subtle" "encoding/json" "strings" @@ -176,12 +175,12 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { // Do email and password check first to prevent extra database look up // for MFA tokens leaking if an account has MFA tokens or not. if !constantTimeEmailCompare(strings.ToLower(sr.Email), strings.ToLower(user.Email)) { - o.auditLogger.Audit(context.Background(), audit.AuthLoginFailedEmail, map[string]interface{}{"email": sr.Email}) + o.auditLogger.Audit(audit.AuthLoginFailedEmail, map[string]interface{}{"email": sr.Email}) return "", errors.New("Invalid email") } if !utils.CheckPasswordHash(sr.Password, user.HashedPassword) { - o.auditLogger.Audit(context.Background(), audit.AuthLoginFailedPassword, map[string]interface{}{"email": sr.Email}) + o.auditLogger.Audit(audit.AuthLoginFailedPassword, map[string]interface{}{"email": sr.Email}) return "", errors.New("Invalid password") } @@ -198,7 +197,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { lggr.Infof("No MFA for user. Creating Session") session := NewSession() _, err = o.q.Exec("INSERT INTO sessions (id, email, last_used, created_at) VALUES ($1, $2, now(), now())", session.ID, user.Email) - o.auditLogger.Audit(context.Background(), audit.AuthLoginSuccessNo2FA, map[string]interface{}{"email": sr.Email}) + o.auditLogger.Audit(audit.AuthLoginSuccessNo2FA, map[string]interface{}{"email": sr.Email}) return session.ID, err } @@ -229,7 +228,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { if err != nil { // The user does have WebAuthn enabled but failed the check - o.auditLogger.Audit(context.Background(), audit.AuthLoginFailed2FA, map[string]interface{}{"email": sr.Email, "error": err}) + o.auditLogger.Audit(audit.AuthLoginFailed2FA, map[string]interface{}{"email": sr.Email, "error": err}) lggr.Errorf("User sent an invalid attestation: %v", err) return "", errors.New("MFA Error") } @@ -248,7 +247,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { lggr.Errorf("error in Marshal credentials: %s", err) return "", err } - o.auditLogger.Audit(context.Background(), audit.AuthLoginSuccessWith2FA, map[string]interface{}{"email": sr.Email, "credential": string(uwasj)}) + o.auditLogger.Audit(audit.AuthLoginSuccessWith2FA, map[string]interface{}{"email": sr.Email, "credential": string(uwasj)}) return session.ID, nil } diff --git a/core/web/bridge_types_controller.go b/core/web/bridge_types_controller.go index 08e41e720a8..e2623fb365a 100644 --- a/core/web/bridge_types_controller.go +++ b/core/web/bridge_types_controller.go @@ -1,7 +1,6 @@ package web import ( - "context" "database/sql" "fmt" "net/http" @@ -98,7 +97,7 @@ func (btc *BridgeTypesController) Create(c *gin.Context) { resource := presenters.NewBridgeResource(*bt) resource.IncomingToken = bta.IncomingToken - btc.App.GetAuditLogger().Audit(context.Background(), audit.BridgeCreated, map[string]interface{}{ + btc.App.GetAuditLogger().Audit(audit.BridgeCreated, map[string]interface{}{ "bridgeName": bta.Name, "bridgeConfirmations": bta.Confirmations, "bridgeMinimumContractPayment": bta.MinimumContractPayment, @@ -179,7 +178,7 @@ func (btc *BridgeTypesController) Update(c *gin.Context) { return } - btc.App.GetAuditLogger().Audit(context.Background(), audit.BridgeUpdated, map[string]interface{}{ + btc.App.GetAuditLogger().Audit(audit.BridgeUpdated, map[string]interface{}{ "bridgeName": bt.Name, "bridgeConfirmations": bt.Confirmations, "bridgeMinimumContractPayment": bt.MinimumContractPayment, @@ -223,7 +222,7 @@ func (btc *BridgeTypesController) Destroy(c *gin.Context) { return } - btc.App.GetAuditLogger().Audit(context.Background(), audit.BridgeDeleted, map[string]interface{}{"name": name}) + btc.App.GetAuditLogger().Audit(audit.BridgeDeleted, map[string]interface{}{"name": name}) jsonAPIResponse(c, presenters.NewBridgeResource(bt), "bridge") } diff --git a/core/web/chains_controller.go b/core/web/chains_controller.go index edc0bde1d61..9feec8ed21d 100644 --- a/core/web/chains_controller.go +++ b/core/web/chains_controller.go @@ -112,7 +112,7 @@ func (cc *chainsController[I, C, R]) Create(c *gin.Context) { if err != nil { cc.lggr.Errorf("Unable to marshal chain to json", "err", err) } - cc.auditLogger.Audit(c.Request.Context(), audit.ChainAdded, map[string]interface{}{"chain": chainj}) + cc.auditLogger.Audit(audit.ChainAdded, map[string]interface{}{"chain": chainj}) jsonAPIResponseWithStatus(c, cc.newResource(chain), cc.resourceName, http.StatusCreated) } @@ -172,7 +172,7 @@ func (cc *chainsController[I, C, R]) Update(c *gin.Context) { if err != nil { cc.lggr.Errorf("Unable to marshal chain to json", "err", err) } - cc.auditLogger.Audit(c.Request.Context(), audit.ChainSpecUpdated, map[string]interface{}{"chain": chainj}) + cc.auditLogger.Audit(audit.ChainSpecUpdated, map[string]interface{}{"chain": chainj}) jsonAPIResponse(c, cc.newResource(chain), cc.resourceName) } @@ -196,7 +196,7 @@ func (cc *chainsController[I, C, R]) Delete(c *gin.Context) { return } - cc.auditLogger.Audit(c.Request.Context(), audit.ChainDeleted, map[string]interface{}{"id": id}) + cc.auditLogger.Audit(audit.ChainDeleted, map[string]interface{}{"id": id}) jsonAPIResponseWithStatus(c, nil, cc.resourceName, http.StatusNoContent) } diff --git a/core/web/config_controller.go b/core/web/config_controller.go index 477ec60a772..2b10f270fd6 100644 --- a/core/web/config_controller.go +++ b/core/web/config_controller.go @@ -19,11 +19,12 @@ type ConfigController struct { // Show returns the whitelist of config variables // Example: -// "/config" +// +// "/config" func (cc *ConfigController) Show(c *gin.Context) { cw := config.NewConfigPrinter(cc.App.GetConfig()) - cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.EnvNoncriticalEnvDumped, map[string]interface{}{}) + cc.App.GetAuditLogger().Audit(audit.EnvNoncriticalEnvDumped, map[string]interface{}{}) jsonAPIResponse(c, cw, "config") } @@ -110,6 +111,6 @@ func (cc *ConfigController) Patch(c *gin.Context) { }, EVMChainID: utils.NewBig(chain.ID()), } - cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ConfigUpdated, map[string]interface{}{"configResponse": response}) + cc.App.GetAuditLogger().Audit(audit.ConfigUpdated, map[string]interface{}{"configResponse": response}) jsonAPIResponse(c, response, "config") } diff --git a/core/web/csa_keys_controller.go b/core/web/csa_keys_controller.go index a7e2a524cc3..8cfa990f2c3 100644 --- a/core/web/csa_keys_controller.go +++ b/core/web/csa_keys_controller.go @@ -45,7 +45,7 @@ func (ctrl *CSAKeysController) Create(c *gin.Context) { return } - ctrl.App.GetAuditLogger().Audit(c.Request.Context(), audit.CSAKeyCreated, map[string]interface{}{ + ctrl.App.GetAuditLogger().Audit(audit.CSAKeyCreated, map[string]interface{}{ "CSAPublicKey": key.PublicKey, "CSVersion": key.Version, }) @@ -69,7 +69,7 @@ func (ctrl *CSAKeysController) Import(c *gin.Context) { return } - ctrl.App.GetAuditLogger().Audit(c.Request.Context(), audit.CSAKeyImported, map[string]interface{}{ + ctrl.App.GetAuditLogger().Audit(audit.CSAKeyImported, map[string]interface{}{ "CSAPublicKey": key.PublicKey, "CSVersion": key.Version, }) @@ -90,6 +90,6 @@ func (ctrl *CSAKeysController) Export(c *gin.Context) { return } - ctrl.App.GetAuditLogger().Audit(c.Request.Context(), audit.CSAKeyExported, map[string]interface{}{"keyID": keyID}) + ctrl.App.GetAuditLogger().Audit(audit.CSAKeyExported, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/eth_keys_controller.go b/core/web/eth_keys_controller.go index dcfb9da1eb1..504a53de9e9 100644 --- a/core/web/eth_keys_controller.go +++ b/core/web/eth_keys_controller.go @@ -30,7 +30,8 @@ type ETHKeysController struct { // Index returns the node's Ethereum keys and the account balances of ETH & LINK. // Example: -// "/keys/eth" +// +// "/keys/eth" func (ekc *ETHKeysController) Index(c *gin.Context) { ethKeyStore := ekc.App.GetKeyStore().Eth() var keys []ethkey.KeyV2 @@ -76,7 +77,8 @@ func (ekc *ETHKeysController) Index(c *gin.Context) { // Create adds a new account // Example: -// "/keys/eth" +// +// "/keys/eth" func (ekc *ETHKeysController) Create(c *gin.Context) { ethKeyStore := ekc.App.GetKeyStore().Eth() @@ -130,7 +132,7 @@ func (ekc *ETHKeysController) Create(c *gin.Context) { return } - ekc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ETHKeyCreated, map[string]interface{}{ + ekc.App.GetAuditLogger().Audit(audit.ETHKeyCreated, map[string]interface{}{ "ethPublicKey": key.Address, "ethID": key.ID(), }) @@ -194,7 +196,7 @@ func (ekc *ETHKeysController) Update(c *gin.Context) { return } - ekc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ETHKeyUpdated, map[string]interface{}{ + ekc.App.GetAuditLogger().Audit(audit.ETHKeyUpdated, map[string]interface{}{ "ethPublicKey": key.Address, "ethID": key.ID(), }) @@ -260,7 +262,7 @@ func (ekc *ETHKeysController) Delete(c *gin.Context) { return } - ekc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ETHKeyDeleted, map[string]interface{}{"id": keyID}) + ekc.App.GetAuditLogger().Audit(audit.ETHKeyDeleted, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, r, "account") } @@ -308,7 +310,7 @@ func (ekc *ETHKeysController) Import(c *gin.Context) { return } - ekc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ETHKeyImported, map[string]interface{}{ + ekc.App.GetAuditLogger().Audit(audit.ETHKeyImported, map[string]interface{}{ "ethPublicKey": key.Address, "ethID": key.ID(), }) @@ -328,7 +330,7 @@ func (ekc *ETHKeysController) Export(c *gin.Context) { return } - ekc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ETHKeyExported, map[string]interface{}{"address": address}) + ekc.App.GetAuditLogger().Audit(audit.ETHKeyExported, map[string]interface{}{"address": address}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/evm_forwarders_controller.go b/core/web/evm_forwarders_controller.go index f62b916f51f..80e240913ea 100644 --- a/core/web/evm_forwarders_controller.go +++ b/core/web/evm_forwarders_controller.go @@ -59,7 +59,7 @@ func (cc *EVMForwardersController) Track(c *gin.Context) { return } - cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ForwarderCreated, map[string]interface{}{ + cc.App.GetAuditLogger().Audit(audit.ForwarderCreated, map[string]interface{}{ "forwarderID": fwd.ID, "forwarderAddress": fwd.Address, "forwarderEVMChainID": fwd.EVMChainID, @@ -83,6 +83,6 @@ func (cc *EVMForwardersController) Delete(c *gin.Context) { return } - cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ForwarderDeleted, map[string]interface{}{"id": id}) + cc.App.GetAuditLogger().Audit(audit.ForwarderDeleted, map[string]interface{}{"id": id}) jsonAPIResponseWithStatus(c, nil, "forwarder", http.StatusNoContent) } diff --git a/core/web/evm_transfer_controller.go b/core/web/evm_transfer_controller.go index 4ff6727b0fb..25c4d8b9128 100644 --- a/core/web/evm_transfer_controller.go +++ b/core/web/evm_transfer_controller.go @@ -64,7 +64,7 @@ func (tc *EVMTransfersController) Create(c *gin.Context) { return } - tc.App.GetAuditLogger().Audit(c.Request.Context(), audit.EthTransactionCreated, map[string]interface{}{ + tc.App.GetAuditLogger().Audit(audit.EthTransactionCreated, map[string]interface{}{ "ethTX": etx, }) diff --git a/core/web/external_initiators_controller.go b/core/web/external_initiators_controller.go index 405ce5082d3..2269ba5e73b 100644 --- a/core/web/external_initiators_controller.go +++ b/core/web/external_initiators_controller.go @@ -85,7 +85,7 @@ func (eic *ExternalInitiatorsController) Create(c *gin.Context) { return } - eic.App.GetAuditLogger().Audit(c.Request.Context(), audit.ExternalInitiatorCreated, map[string]interface{}{ + eic.App.GetAuditLogger().Audit(audit.ExternalInitiatorCreated, map[string]interface{}{ "externalInitiatorID": ei.ID, "externalInitiatorName": ei.Name, "externalInitiatorURL": ei.URL, @@ -108,6 +108,6 @@ func (eic *ExternalInitiatorsController) Destroy(c *gin.Context) { return } - eic.App.GetAuditLogger().Audit(c.Request.Context(), audit.ExternalInitiatorDeleted, map[string]interface{}{"name": name}) + eic.App.GetAuditLogger().Audit(audit.ExternalInitiatorDeleted, map[string]interface{}{"name": name}) jsonAPIResponseWithStatus(c, nil, "external initiator", http.StatusNoContent) } diff --git a/core/web/jobs_controller.go b/core/web/jobs_controller.go index 0a6916a767b..77d2c742a82 100644 --- a/core/web/jobs_controller.go +++ b/core/web/jobs_controller.go @@ -159,7 +159,7 @@ func (jc *JobsController) Create(c *gin.Context) { } jbj, _ := json.Marshal(jb) - jc.App.GetAuditLogger().Audit(c.Request.Context(), audit.JobCreated, map[string]interface{}{"job": string(jbj)}) + jc.App.GetAuditLogger().Audit(audit.JobCreated, map[string]interface{}{"job": string(jbj)}) jsonAPIResponse(c, presenters.NewJobResource(jb), jb.Type.String()) } @@ -186,6 +186,6 @@ func (jc *JobsController) Delete(c *gin.Context) { return } - jc.App.GetAuditLogger().Audit(c.Request.Context(), audit.JobDeleted, map[string]interface{}{"id": j.ID}) + jc.App.GetAuditLogger().Audit(audit.JobDeleted, map[string]interface{}{"id": j.ID}) jsonAPIResponseWithStatus(c, nil, "job", http.StatusNoContent) } diff --git a/core/web/keys_controller.go b/core/web/keys_controller.go index 3d803b48901..2455dd5cf32 100644 --- a/core/web/keys_controller.go +++ b/core/web/keys_controller.go @@ -77,12 +77,12 @@ func (kc *keysController[K, R]) Create(c *gin.Context) { // Emit audit log, determine if Terra or Solana key switch unwrappedKey := any(key).(type) { case terrakey.Key: - kc.auditLogger.Audit(c.Request.Context(), audit.TerraKeyCreated, map[string]interface{}{ + kc.auditLogger.Audit(audit.TerraKeyCreated, map[string]interface{}{ "publicKey": unwrappedKey.PublicKey(), "id": unwrappedKey.ID(), }) case solkey.Key: - kc.auditLogger.Audit(c.Request.Context(), audit.SolanaKeyCreated, map[string]interface{}{ + kc.auditLogger.Audit(audit.SolanaKeyCreated, map[string]interface{}{ "publicKey": unwrappedKey.PublicKey(), "id": unwrappedKey.ID(), }) @@ -107,9 +107,9 @@ func (kc *keysController[K, R]) Delete(c *gin.Context) { // Emit audit log, determine if Terra or Solana key switch any(key).(type) { case terrakey.Key: - kc.auditLogger.Audit(c.Request.Context(), audit.TerraKeyDeleted, map[string]interface{}{"id": keyID}) + kc.auditLogger.Audit(audit.TerraKeyDeleted, map[string]interface{}{"id": keyID}) case solkey.Key: - kc.auditLogger.Audit(c.Request.Context(), audit.SolanaKeyDeleted, map[string]interface{}{"id": keyID}) + kc.auditLogger.Audit(audit.SolanaKeyDeleted, map[string]interface{}{"id": keyID}) } jsonAPIResponse(c, kc.newResource(key), kc.resourceName) @@ -133,12 +133,12 @@ func (kc *keysController[K, R]) Import(c *gin.Context) { // Emit audit log, determine if Terra or Solana key switch unwrappedKey := any(key).(type) { case terrakey.Key: - kc.auditLogger.Audit(c.Request.Context(), audit.TerraKeyImported, map[string]interface{}{ + kc.auditLogger.Audit(audit.TerraKeyImported, map[string]interface{}{ "publicKey": unwrappedKey.PublicKey(), "id": unwrappedKey.ID(), }) case solkey.Key: - kc.auditLogger.Audit(c.Request.Context(), audit.SolanaKeyImported, map[string]interface{}{ + kc.auditLogger.Audit(audit.SolanaKeyImported, map[string]interface{}{ "publicKey": unwrappedKey.PublicKey(), "id": unwrappedKey.ID(), }) @@ -159,9 +159,9 @@ func (kc *keysController[K, R]) Export(c *gin.Context) { } if strings.HasPrefix(c.Request.URL.Path, "/v2/keys/terra") { - kc.auditLogger.Audit(c.Request.Context(), audit.TerraKeyExported, map[string]interface{}{"id": keyID}) + kc.auditLogger.Audit(audit.TerraKeyExported, map[string]interface{}{"id": keyID}) } else if strings.HasPrefix(c.Request.URL.Path, "/v2/keys/solana") { - kc.auditLogger.Audit(c.Request.Context(), audit.SolanaKeyExported, map[string]interface{}{"id": keyID}) + kc.auditLogger.Audit(audit.SolanaKeyExported, map[string]interface{}{"id": keyID}) } c.Data(http.StatusOK, MediaType, bytes) diff --git a/core/web/log_controller.go b/core/web/log_controller.go index c1942ec9edc..395d03d50a4 100644 --- a/core/web/log_controller.go +++ b/core/web/log_controller.go @@ -91,13 +91,13 @@ func (cc *LogController) Patch(c *gin.Context) { LogLevel: lvls, } - cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.GlobalLogLevelSet, map[string]interface{}{"logLevel": request.Level}) + cc.App.GetAuditLogger().Audit(audit.GlobalLogLevelSet, map[string]interface{}{"logLevel": request.Level}) if request.Level == "debug" { if request.SqlEnabled != nil && *request.SqlEnabled { - cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ConfigSqlLoggingEnabled, map[string]interface{}{}) + cc.App.GetAuditLogger().Audit(audit.ConfigSqlLoggingEnabled, map[string]interface{}{}) } else { - cc.App.GetAuditLogger().Audit(c.Request.Context(), audit.ConfigSqlLoggingDisabled, map[string]interface{}{}) + cc.App.GetAuditLogger().Audit(audit.ConfigSqlLoggingDisabled, map[string]interface{}{}) } } diff --git a/core/web/nodes_controller.go b/core/web/nodes_controller.go index 13849ab63ae..8a96a78ca05 100644 --- a/core/web/nodes_controller.go +++ b/core/web/nodes_controller.go @@ -102,7 +102,7 @@ func (n *nodesController[I, N, R]) Create(c *gin.Context) { return } - n.auditLogger.Audit(c.Request.Context(), audit.ChainRpcNodeAdded, map[string]interface{}{}) + n.auditLogger.Audit(audit.ChainRpcNodeAdded, map[string]interface{}{}) jsonAPIResponse(c, n.newResource(node), "node") } @@ -126,7 +126,7 @@ func (n *nodesController[I, N, R]) Delete(c *gin.Context) { return } - n.auditLogger.Audit(c.Request.Context(), audit.ChainDeleted, map[string]interface{}{"id": id}) + n.auditLogger.Audit(audit.ChainDeleted, map[string]interface{}{"id": id}) jsonAPIResponseWithStatus(c, nil, "node", http.StatusNoContent) } diff --git a/core/web/ocr2_keys_controller.go b/core/web/ocr2_keys_controller.go index 8d6fa8d017a..783fc8d6554 100644 --- a/core/web/ocr2_keys_controller.go +++ b/core/web/ocr2_keys_controller.go @@ -44,7 +44,7 @@ func (ocr2kc *OCR2KeysController) Create(c *gin.Context) { return } - ocr2kc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCR2KeyBundleCreated, map[string]interface{}{ + ocr2kc.App.GetAuditLogger().Audit(audit.OCR2KeyBundleCreated, map[string]interface{}{ "ocr2KeyID": key.ID(), "ocr2KeyChainType": key.ChainType(), "ocr2KeyConfigEncryptionPublicKey": key.ConfigEncryptionPublicKey(), @@ -71,7 +71,7 @@ func (ocr2kc *OCR2KeysController) Delete(c *gin.Context) { return } - ocr2kc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCR2KeyBundleDeleted, map[string]interface{}{"id": id}) + ocr2kc.App.GetAuditLogger().Audit(audit.OCR2KeyBundleDeleted, map[string]interface{}{"id": id}) jsonAPIResponse(c, presenters.NewOCR2KeysBundleResource(key), "offChainReporting2KeyBundle") } @@ -93,7 +93,7 @@ func (ocr2kc *OCR2KeysController) Import(c *gin.Context) { return } - ocr2kc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCR2KeyBundleImported, map[string]interface{}{ + ocr2kc.App.GetAuditLogger().Audit(audit.OCR2KeyBundleImported, map[string]interface{}{ "ocr2KeyID": keyBundle.ID(), "ocr2KeyChainType": keyBundle.ChainType(), "ocr2KeyConfigEncryptionPublicKey": keyBundle.ConfigEncryptionPublicKey(), @@ -119,6 +119,6 @@ func (ocr2kc *OCR2KeysController) Export(c *gin.Context) { return } - ocr2kc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCR2KeyBundleExported, map[string]interface{}{"keyID": stringID}) + ocr2kc.App.GetAuditLogger().Audit(audit.OCR2KeyBundleExported, map[string]interface{}{"keyID": stringID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/ocr_keys_controller.go b/core/web/ocr_keys_controller.go index ba01da9f1fd..771e19d17aa 100644 --- a/core/web/ocr_keys_controller.go +++ b/core/web/ocr_keys_controller.go @@ -37,7 +37,7 @@ func (ocrkc *OCRKeysController) Create(c *gin.Context) { return } - ocrkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCRKeyBundleCreated, map[string]interface{}{ + ocrkc.App.GetAuditLogger().Audit(audit.OCRKeyBundleCreated, map[string]interface{}{ "ocrKeyBundleID": key.ID(), "ocrKeyBundlePublicKeyAddressOnChain": key.PublicKeyAddressOnChain(), }) @@ -61,7 +61,7 @@ func (ocrkc *OCRKeysController) Delete(c *gin.Context) { return } - ocrkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCRKeyBundleDeleted, map[string]interface{}{"id": id}) + ocrkc.App.GetAuditLogger().Audit(audit.OCRKeyBundleDeleted, map[string]interface{}{"id": id}) jsonAPIResponse(c, presenters.NewOCRKeysBundleResource(key), "offChainReportingKeyBundle") } @@ -83,7 +83,7 @@ func (ocrkc *OCRKeysController) Import(c *gin.Context) { return } - ocrkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCRKeyBundleImported, map[string]interface{}{ + ocrkc.App.GetAuditLogger().Audit(audit.OCRKeyBundleImported, map[string]interface{}{ "OCRID": encryptedOCRKeyBundle.GetID(), "OCRPublicKeyAddressOnChain": encryptedOCRKeyBundle.PublicKeyAddressOnChain(), "OCRPublicKeyOffChain": encryptedOCRKeyBundle.PublicKeyOffChain(), @@ -106,6 +106,6 @@ func (ocrkc *OCRKeysController) Export(c *gin.Context) { return } - ocrkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.OCRKeyBundleExported, map[string]interface{}{"keyID": stringID}) + ocrkc.App.GetAuditLogger().Audit(audit.OCRKeyBundleExported, map[string]interface{}{"keyID": stringID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/p2p_keys_controller.go b/core/web/p2p_keys_controller.go index c81f61279fc..59df2752852 100644 --- a/core/web/p2p_keys_controller.go +++ b/core/web/p2p_keys_controller.go @@ -39,7 +39,7 @@ func (p2pkc *P2PKeysController) Create(c *gin.Context) { return } - p2pkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.P2PKeyCreated, map[string]interface{}{ + p2pkc.App.GetAuditLogger().Audit(audit.P2PKeyCreated, map[string]interface{}{ "p2pPublicKey": key.PublicKeyHex(), "p2pID": key.ID(), "p2pPeerID": key.PeerID(), @@ -69,7 +69,7 @@ func (p2pkc *P2PKeysController) Delete(c *gin.Context) { return } - p2pkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.P2PKeyDeleted, map[string]interface{}{"id": keyID}) + p2pkc.App.GetAuditLogger().Audit(audit.P2PKeyDeleted, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, presenters.NewP2PKeyResource(key), "p2pKey") } @@ -91,7 +91,7 @@ func (p2pkc *P2PKeysController) Import(c *gin.Context) { return } - p2pkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.P2PKeyImported, map[string]interface{}{ + p2pkc.App.GetAuditLogger().Audit(audit.P2PKeyImported, map[string]interface{}{ "p2pPublicKey": key.PublicKeyHex(), "p2pID": key.ID(), "p2pPeerID": key.PeerID(), @@ -119,6 +119,6 @@ func (p2pkc *P2PKeysController) Export(c *gin.Context) { return } - p2pkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.P2PKeyExported, map[string]interface{}{"keyID": keyID}) + p2pkc.App.GetAuditLogger().Audit(audit.P2PKeyExported, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/pipeline_job_spec_errors_controller.go b/core/web/pipeline_job_spec_errors_controller.go index f9d855cc145..25822102ebf 100644 --- a/core/web/pipeline_job_spec_errors_controller.go +++ b/core/web/pipeline_job_spec_errors_controller.go @@ -37,6 +37,6 @@ func (psec *PipelineJobSpecErrorsController) Destroy(c *gin.Context) { return } - psec.App.GetAuditLogger().Audit(c.Request.Context(), audit.JobErrorDismissed, map[string]interface{}{"id": jobSpec.ID}) + psec.App.GetAuditLogger().Audit(audit.JobErrorDismissed, map[string]interface{}{"id": jobSpec.ID}) jsonAPIResponseWithStatus(c, nil, "job", http.StatusNoContent) } diff --git a/core/web/pipeline_runs_controller.go b/core/web/pipeline_runs_controller.go index 57afefcd4b1..ca153e1da80 100644 --- a/core/web/pipeline_runs_controller.go +++ b/core/web/pipeline_runs_controller.go @@ -179,6 +179,6 @@ func (prc *PipelineRunsController) Resume(c *gin.Context) { return } - prc.App.GetAuditLogger().Audit(c.Request.Context(), audit.UnauthedRunResumed, map[string]interface{}{"runID": c.Param("runID")}) + prc.App.GetAuditLogger().Audit(audit.UnauthedRunResumed, map[string]interface{}{"runID": c.Param("runID")}) c.Status(http.StatusOK) } diff --git a/core/web/resolver/mutation.go b/core/web/resolver/mutation.go index 1bcb9c84757..071d1817822 100644 --- a/core/web/resolver/mutation.go +++ b/core/web/resolver/mutation.go @@ -96,7 +96,7 @@ func (r *Resolver) CreateBridge(ctx context.Context, args struct{ Input createBr return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.BridgeCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(audit.BridgeCreated, map[string]interface{}{ "bridgeName": bta.Name, "bridgeConfirmations": bta.Confirmations, "bridgeMinimumContractPayment": bta.MinimumContractPayment, @@ -120,7 +120,7 @@ func (r *Resolver) CreateCSAKey(ctx context.Context) (*CreateCSAKeyPayloadResolv return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.CSAKeyCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(audit.CSAKeyCreated, map[string]interface{}{ "CSAPublicKey": key.PublicKey, "CSVersion": key.Version, }) @@ -144,7 +144,7 @@ func (r *Resolver) DeleteCSAKey(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.CSAKeyDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(audit.CSAKeyDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteCSAKeyPayload(key, nil), nil } @@ -237,7 +237,7 @@ func (r *Resolver) CreateFeedsManagerChainConfig(ctx context.Context, args struc } fmj, _ := json.Marshal(ccfg) - r.App.GetAuditLogger().Audit(ctx, audit.FeedsManChainConfigCreated, map[string]interface{}{"feedsManager": fmj}) + r.App.GetAuditLogger().Audit(audit.FeedsManChainConfigCreated, map[string]interface{}{"feedsManager": fmj}) return NewCreateFeedsManagerChainConfigPayload(ccfg, nil, nil), nil } @@ -273,7 +273,7 @@ func (r *Resolver) DeleteFeedsManagerChainConfig(ctx context.Context, args struc return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.FeedsManChainConfigDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(audit.FeedsManChainConfigDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteFeedsManagerChainConfigPayload(ccfg, nil), nil } @@ -357,7 +357,7 @@ func (r *Resolver) UpdateFeedsManagerChainConfig(ctx context.Context, args struc } fmj, _ := json.Marshal(ccfg) - r.App.GetAuditLogger().Audit(ctx, audit.FeedsManChainConfigUpdated, map[string]interface{}{"feedsManager": fmj}) + r.App.GetAuditLogger().Audit(audit.FeedsManChainConfigUpdated, map[string]interface{}{"feedsManager": fmj}) return NewUpdateFeedsManagerChainConfigPayload(ccfg, nil, nil), nil } @@ -408,7 +408,7 @@ func (r *Resolver) CreateFeedsManager(ctx context.Context, args struct { } mgrj, _ := json.Marshal(mgr) - r.App.GetAuditLogger().Audit(ctx, audit.FeedsManCreated, map[string]interface{}{"mgrj": mgrj}) + r.App.GetAuditLogger().Audit(audit.FeedsManCreated, map[string]interface{}{"mgrj": mgrj}) return NewCreateFeedsManagerPayload(mgr, nil, nil), nil } @@ -472,7 +472,7 @@ func (r *Resolver) UpdateBridge(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.BridgeUpdated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(audit.BridgeUpdated, map[string]interface{}{ "bridgeName": bridge.Name, "bridgeConfirmations": bridge.Confirmations, "bridgeMinimumContractPayment": bridge.MinimumContractPayment, @@ -531,7 +531,7 @@ func (r *Resolver) UpdateFeedsManager(ctx context.Context, args struct { } mgrj, _ := json.Marshal(mgr) - r.App.GetAuditLogger().Audit(ctx, audit.FeedsManUpdated, map[string]interface{}{"mgrj": mgrj}) + r.App.GetAuditLogger().Audit(audit.FeedsManUpdated, map[string]interface{}{"mgrj": mgrj}) return NewUpdateFeedsManagerPayload(mgr, nil, nil), nil } @@ -546,7 +546,7 @@ func (r *Resolver) CreateOCRKeyBundle(ctx context.Context) (*CreateOCRKeyBundleP return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.OCRKeyBundleCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(audit.OCRKeyBundleCreated, map[string]interface{}{ "ocrKeyBundleID": key.ID(), "ocrKeyBundlePublicKeyAddressOnChain": key.PublicKeyAddressOnChain(), }) @@ -569,7 +569,7 @@ func (r *Resolver) DeleteOCRKeyBundle(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.OCRKeyBundleDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(audit.OCRKeyBundleDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteOCRKeyBundlePayloadResolver(deletedKey, nil), nil } @@ -593,7 +593,7 @@ func (r *Resolver) CreateNode(ctx context.Context, args struct { wsURL, _ := url.Parse(args.Input.WSURL.String) // Forward only RPC host to logs httpURL, _ := url.Parse(args.Input.HTTPURL.String) - r.App.GetAuditLogger().Audit(ctx, audit.ChainRpcNodeAdded, map[string]interface{}{ + r.App.GetAuditLogger().Audit(audit.ChainRpcNodeAdded, map[string]interface{}{ "chainNodeName": args.Input.Name, "chainNodeEvmChainID": args.Input.EVMChainID, "chainNodeRPCWebSocketHost": wsURL.Host, @@ -637,7 +637,7 @@ func (r *Resolver) DeleteNode(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.ChainRpcNodeDeleted, map[string]interface{}{"id": id}) + r.App.GetAuditLogger().Audit(audit.ChainRpcNodeDeleted, map[string]interface{}{"id": id}) return NewDeleteNodePayloadResolver(&node, nil), nil } @@ -675,7 +675,7 @@ func (r *Resolver) DeleteBridge(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.BridgeDeleted, map[string]interface{}{"name": bt.Name}) + r.App.GetAuditLogger().Audit(audit.BridgeDeleted, map[string]interface{}{"name": bt.Name}) return NewDeleteBridgePayload(&bt, nil), nil } @@ -689,7 +689,7 @@ func (r *Resolver) CreateP2PKey(ctx context.Context) (*CreateP2PKeyPayloadResolv return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.P2PKeyCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(audit.P2PKeyCreated, map[string]interface{}{ "p2pPublicKey": key.PublicKeyHex(), "p2pID": key.ID(), "p2pPeerID": key.PeerID(), @@ -719,7 +719,7 @@ func (r *Resolver) DeleteP2PKey(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.P2PKeyDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(audit.P2PKeyDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteP2PKeyPayload(key, nil), nil } @@ -733,7 +733,7 @@ func (r *Resolver) CreateVRFKey(ctx context.Context) (*CreateVRFKeyPayloadResolv return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.VRFKeyCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(audit.VRFKeyCreated, map[string]interface{}{ "vrfPublicKey": key.PublicKey, "vrfID": key.ID(), "vrfPublicKeyAddress": key.PublicKey.Address(), @@ -757,7 +757,7 @@ func (r *Resolver) DeleteVRFKey(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.VRFKeyDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(audit.VRFKeyDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteVRFKeyPayloadResolver(key, nil), nil } @@ -796,7 +796,7 @@ func (r *Resolver) ApproveJobProposalSpec(ctx context.Context, args struct { } specj, _ := json.Marshal(spec) - r.App.GetAuditLogger().Audit(ctx, audit.JobProposalSpecApproved, map[string]interface{}{"spec": specj}) + r.App.GetAuditLogger().Audit(audit.JobProposalSpecApproved, map[string]interface{}{"spec": specj}) return NewApproveJobProposalSpecPayload(spec, err), nil } @@ -831,7 +831,7 @@ func (r *Resolver) CancelJobProposalSpec(ctx context.Context, args struct { } specj, _ := json.Marshal(spec) - r.App.GetAuditLogger().Audit(ctx, audit.JobProposalSpecCanceled, map[string]interface{}{"spec": specj}) + r.App.GetAuditLogger().Audit(audit.JobProposalSpecCanceled, map[string]interface{}{"spec": specj}) return NewCancelJobProposalSpecPayload(spec, err), nil } @@ -866,7 +866,7 @@ func (r *Resolver) RejectJobProposalSpec(ctx context.Context, args struct { } specj, _ := json.Marshal(spec) - r.App.GetAuditLogger().Audit(ctx, audit.JobProposalSpecRejected, map[string]interface{}{"spec": specj}) + r.App.GetAuditLogger().Audit(audit.JobProposalSpecRejected, map[string]interface{}{"spec": specj}) return NewRejectJobProposalSpecPayload(spec, err), nil } @@ -904,7 +904,7 @@ func (r *Resolver) UpdateJobProposalSpecDefinition(ctx context.Context, args str } specj, _ := json.Marshal(spec) - r.App.GetAuditLogger().Audit(ctx, audit.JobProposalSpecUpdated, map[string]interface{}{"spec": specj}) + r.App.GetAuditLogger().Audit(audit.JobProposalSpecUpdated, map[string]interface{}{"spec": specj}) return NewUpdateJobProposalSpecDefinitionPayload(spec, err), nil } @@ -927,7 +927,7 @@ func (r *Resolver) UpdateUserPassword(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.OldPassword, dbUser.HashedPassword) { - r.App.GetAuditLogger().Audit(ctx, audit.PasswordResetAttemptFailedMismatch, map[string]interface{}{"user": dbUser.Email}) + r.App.GetAuditLogger().Audit(audit.PasswordResetAttemptFailedMismatch, map[string]interface{}{"user": dbUser.Email}) return NewUpdatePasswordPayload(nil, map[string]string{ "oldPassword": "old password does not match", @@ -943,7 +943,7 @@ func (r *Resolver) UpdateUserPassword(ctx context.Context, args struct { return nil, failedPasswordUpdateError{} } - r.App.GetAuditLogger().Audit(ctx, audit.PasswordResetSuccess, map[string]interface{}{"user": dbUser.Email}) + r.App.GetAuditLogger().Audit(audit.PasswordResetSuccess, map[string]interface{}{"user": dbUser.Email}) return NewUpdatePasswordPayload(session.User, nil), nil } @@ -957,9 +957,9 @@ func (r *Resolver) SetSQLLogging(ctx context.Context, args struct { r.App.GetConfig().SetLogSQL(args.Input.Enabled) if args.Input.Enabled { - r.App.GetAuditLogger().Audit(ctx, audit.ConfigSqlLoggingEnabled, map[string]interface{}{}) + r.App.GetAuditLogger().Audit(audit.ConfigSqlLoggingEnabled, map[string]interface{}{}) } else { - r.App.GetAuditLogger().Audit(ctx, audit.ConfigSqlLoggingDisabled, map[string]interface{}{}) + r.App.GetAuditLogger().Audit(audit.ConfigSqlLoggingDisabled, map[string]interface{}{}) } return NewSetSQLLoggingPayload(args.Input.Enabled), nil @@ -982,7 +982,7 @@ func (r *Resolver) CreateAPIToken(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.Password, dbUser.HashedPassword) { - r.App.GetAuditLogger().Audit(ctx, audit.APITokenCreateAttemptPasswordMismatch, map[string]interface{}{"user": dbUser.Email}) + r.App.GetAuditLogger().Audit(audit.APITokenCreateAttemptPasswordMismatch, map[string]interface{}{"user": dbUser.Email}) return NewCreateAPITokenPayload(nil, map[string]string{ "password": "incorrect password", @@ -994,7 +994,7 @@ func (r *Resolver) CreateAPIToken(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.APITokenCreated, map[string]interface{}{"user": dbUser.Email}) + r.App.GetAuditLogger().Audit(audit.APITokenCreated, map[string]interface{}{"user": dbUser.Email}) return NewCreateAPITokenPayload(newToken, nil), nil } @@ -1015,7 +1015,7 @@ func (r *Resolver) DeleteAPIToken(ctx context.Context, args struct { } if !utils.CheckPasswordHash(args.Input.Password, dbUser.HashedPassword) { - r.App.GetAuditLogger().Audit(ctx, audit.APITokenDeleteAttemptPasswordMismatch, map[string]interface{}{"user": dbUser.Email}) + r.App.GetAuditLogger().Audit(audit.APITokenDeleteAttemptPasswordMismatch, map[string]interface{}{"user": dbUser.Email}) return NewDeleteAPITokenPayload(nil, map[string]string{ "password": "incorrect password", @@ -1027,7 +1027,7 @@ func (r *Resolver) DeleteAPIToken(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.APITokenDeleted, map[string]interface{}{"user": dbUser.Email}) + r.App.GetAuditLogger().Audit(audit.APITokenDeleted, map[string]interface{}{"user": dbUser.Email}) return NewDeleteAPITokenPayload(&auth.Token{ AccessKey: dbUser.TokenKey.String, @@ -1082,7 +1082,7 @@ func (r *Resolver) CreateChain(ctx context.Context, args struct { if err != nil { r.App.GetLogger().Errorf("Unable to marshal chain to json", "err", err) } - r.App.GetAuditLogger().Audit(ctx, audit.ChainAdded, map[string]interface{}{"chain": chainj}) + r.App.GetAuditLogger().Audit(audit.ChainAdded, map[string]interface{}{"chain": chainj}) return NewCreateChainPayload(&chain, nil), nil } @@ -1140,7 +1140,7 @@ func (r *Resolver) UpdateChain(ctx context.Context, args struct { if err != nil { r.App.GetLogger().Errorf("Unable to marshal chain to json", "err", err) } - r.App.GetAuditLogger().Audit(ctx, audit.ChainSpecUpdated, map[string]interface{}{"chainj": chainj}) + r.App.GetAuditLogger().Audit(audit.ChainSpecUpdated, map[string]interface{}{"chainj": chainj}) return NewUpdateChainPayload(&chain, nil, nil), nil } @@ -1172,7 +1172,7 @@ func (r *Resolver) DeleteChain(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.ChainDeleted, map[string]interface{}{"id": id}) + r.App.GetAuditLogger().Audit(audit.ChainDeleted, map[string]interface{}{"id": id}) return NewDeleteChainPayload(&chain, nil), nil } @@ -1239,7 +1239,7 @@ func (r *Resolver) CreateJob(ctx context.Context, args struct { } jbj, _ := json.Marshal(jb) - r.App.GetAuditLogger().Audit(ctx, audit.JobCreated, map[string]interface{}{"job": string(jbj)}) + r.App.GetAuditLogger().Audit(audit.JobCreated, map[string]interface{}{"job": string(jbj)}) return NewCreateJobPayload(r.App, &jb, nil), nil } @@ -1274,7 +1274,7 @@ func (r *Resolver) DeleteJob(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.JobDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(audit.JobDeleted, map[string]interface{}{"id": args.ID}) return NewDeleteJobPayload(r.App, &j, nil), nil } @@ -1308,7 +1308,7 @@ func (r *Resolver) DismissJobError(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.JobErrorDismissed, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(audit.JobErrorDismissed, map[string]interface{}{"id": args.ID}) return NewDismissJobErrorPayload(&specErr, nil), nil } @@ -1338,7 +1338,7 @@ func (r *Resolver) RunJob(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.JobRunSet, map[string]interface{}{"jobID": args.ID, "jobRunID": jobRunID, "planRunID": plnRun}) + r.App.GetAuditLogger().Audit(audit.JobRunSet, map[string]interface{}{"jobID": args.ID, "jobRunID": jobRunID, "planRunID": plnRun}) return NewRunJobPayload(&plnRun, r.App, nil), nil } @@ -1363,7 +1363,7 @@ func (r *Resolver) SetGlobalLogLevel(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.GlobalLogLevelSet, map[string]interface{}{"logLevel": args.Level}) + r.App.GetAuditLogger().Audit(audit.GlobalLogLevelSet, map[string]interface{}{"logLevel": args.Level}) return NewSetGlobalLogLevelPayload(args.Level, nil), nil } @@ -1382,7 +1382,7 @@ func (r *Resolver) CreateOCR2KeyBundle(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.OCR2KeyBundleCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(audit.OCR2KeyBundleCreated, map[string]interface{}{ "ocrKeyID": key.ID(), "ocrKeyChainType": key.ChainType(), "ocrKeyConfigEncryptionPublicKey": key.ConfigEncryptionPublicKey(), @@ -1413,6 +1413,6 @@ func (r *Resolver) DeleteOCR2KeyBundle(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(ctx, audit.OCR2KeyBundleDeleted, map[string]interface{}{"id": id}) + r.App.GetAuditLogger().Audit(audit.OCR2KeyBundleDeleted, map[string]interface{}{"id": id}) return NewDeleteOCR2KeyBundlePayloadResolver(&key, nil), nil } diff --git a/core/web/sessions_controller.go b/core/web/sessions_controller.go index 5fddfaff3ad..2cab7bfafea 100644 --- a/core/web/sessions_controller.go +++ b/core/web/sessions_controller.go @@ -84,7 +84,7 @@ func (sc *SessionsController) Destroy(c *gin.Context) { return } - sc.App.GetAuditLogger().Audit(c.Request.Context(), audit.AuthSessionDeleted, map[string]interface{}{"sessionID": sessionID}) + sc.App.GetAuditLogger().Audit(audit.AuthSessionDeleted, map[string]interface{}{"sessionID": sessionID}) jsonAPIResponse(c, Session{Authenticated: false}, "session") } diff --git a/core/web/solana_transfer_controller.go b/core/web/solana_transfer_controller.go index aa4c6c74459..730e2427e7d 100644 --- a/core/web/solana_transfer_controller.go +++ b/core/web/solana_transfer_controller.go @@ -111,7 +111,7 @@ func (tc *SolanaTransfersController) Create(c *gin.Context) { resource.From = tr.From.String() resource.To = tr.To.String() - tc.App.GetAuditLogger().Audit(c.Request.Context(), audit.SolanaTransactionCreated, map[string]interface{}{ + tc.App.GetAuditLogger().Audit(audit.SolanaTransactionCreated, map[string]interface{}{ "solanaTransactionResource": resource, }) jsonAPIResponse(c, resource, "solana_tx") diff --git a/core/web/terra_transfer_controller.go b/core/web/terra_transfer_controller.go index 07f9138ee4b..007ca3c0cc7 100644 --- a/core/web/terra_transfer_controller.go +++ b/core/web/terra_transfer_controller.go @@ -111,7 +111,7 @@ func (tc *TerraTransfersController) Create(c *gin.Context) { resource.TxHash = msg.TxHash resource.State = string(msg.State) - tc.App.GetAuditLogger().Audit(c.Request.Context(), audit.TerraTransactionCreated, map[string]interface{}{ + tc.App.GetAuditLogger().Audit(audit.TerraTransactionCreated, map[string]interface{}{ "terraTransactionResource": resource, }) diff --git a/core/web/user_controller.go b/core/web/user_controller.go index 228920adf93..e91418525c0 100644 --- a/core/web/user_controller.go +++ b/core/web/user_controller.go @@ -177,7 +177,7 @@ func (c *UserController) UpdatePassword(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.OldPassword, user.HashedPassword) { - c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.PasswordResetAttemptFailedMismatch, map[string]interface{}{"user": user.Email}) + c.App.GetAuditLogger().Audit(audit.PasswordResetAttemptFailedMismatch, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusConflict, errors.New("old password does not match")) return } @@ -190,7 +190,7 @@ func (c *UserController) UpdatePassword(ctx *gin.Context) { return } - c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.PasswordResetSuccess, map[string]interface{}{"user": user.Email}) + c.App.GetAuditLogger().Audit(audit.PasswordResetSuccess, map[string]interface{}{"user": user.Email}) jsonAPIResponse(ctx, presenters.NewUserResource(user), "user") } @@ -214,7 +214,7 @@ func (c *UserController) NewAPIToken(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.Password, user.HashedPassword) { - c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.APITokenCreateAttemptPasswordMismatch, map[string]interface{}{"user": user.Email}) + c.App.GetAuditLogger().Audit(audit.APITokenCreateAttemptPasswordMismatch, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusUnauthorized, errors.New("incorrect password")) return } @@ -224,7 +224,7 @@ func (c *UserController) NewAPIToken(ctx *gin.Context) { return } - c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.APITokenCreated, map[string]interface{}{"user": user.Email}) + c.App.GetAuditLogger().Audit(audit.APITokenCreated, map[string]interface{}{"user": user.Email}) jsonAPIResponseWithStatus(ctx, newToken, "auth_token", http.StatusCreated) } @@ -248,7 +248,7 @@ func (c *UserController) DeleteAPIToken(ctx *gin.Context) { return } if !utils.CheckPasswordHash(request.Password, user.HashedPassword) { - c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.APITokenDeleteAttemptPasswordMismatch, map[string]interface{}{"user": user.Email}) + c.App.GetAuditLogger().Audit(audit.APITokenDeleteAttemptPasswordMismatch, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusUnauthorized, errors.New("incorrect password")) return } @@ -257,7 +257,7 @@ func (c *UserController) DeleteAPIToken(ctx *gin.Context) { return } { - c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.APITokenDeleted, map[string]interface{}{"user": user.Email}) + c.App.GetAuditLogger().Audit(audit.APITokenDeleted, map[string]interface{}{"user": user.Email}) jsonAPIResponseWithStatus(ctx, nil, "auth_token", http.StatusNoContent) } } diff --git a/core/web/vrf_keys_controller.go b/core/web/vrf_keys_controller.go index 543402ba9ee..f97950399ba 100644 --- a/core/web/vrf_keys_controller.go +++ b/core/web/vrf_keys_controller.go @@ -38,7 +38,7 @@ func (vrfkc *VRFKeysController) Create(c *gin.Context) { return } - vrfkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.VRFKeyCreated, map[string]interface{}{ + vrfkc.App.GetAuditLogger().Audit(audit.VRFKeyCreated, map[string]interface{}{ "vrfPublicKey": pk.PublicKey, "vrfID": pk.ID(), "vrfPublicKeyAddress": pk.PublicKey.Address(), @@ -63,7 +63,7 @@ func (vrfkc *VRFKeysController) Delete(c *gin.Context) { return } - vrfkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.VRFKeyDeleted, map[string]interface{}{"id": keyID}) + vrfkc.App.GetAuditLogger().Audit(audit.VRFKeyDeleted, map[string]interface{}{"id": keyID}) jsonAPIResponse(c, presenters.NewVRFKeyResource(key, vrfkc.App.GetLogger()), "vrfKey") } @@ -85,7 +85,7 @@ func (vrfkc *VRFKeysController) Import(c *gin.Context) { return } - vrfkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.VRFKeyImported, map[string]interface{}{ + vrfkc.App.GetAuditLogger().Audit(audit.VRFKeyImported, map[string]interface{}{ "vrfID": key.ID(), "vrfPublicKey": key.PublicKey, }) @@ -107,6 +107,6 @@ func (vrfkc *VRFKeysController) Export(c *gin.Context) { return } - vrfkc.App.GetAuditLogger().Audit(c.Request.Context(), audit.VRFKeyExported, map[string]interface{}{"keyID": keyID}) + vrfkc.App.GetAuditLogger().Audit(audit.VRFKeyExported, map[string]interface{}{"keyID": keyID}) c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/webauthn_controller.go b/core/web/webauthn_controller.go index e7a49c6a5b6..73b7720f320 100644 --- a/core/web/webauthn_controller.go +++ b/core/web/webauthn_controller.go @@ -96,7 +96,7 @@ func (c *WebAuthnController) FinishRegistration(ctx *gin.Context) { jsonAPIError(ctx, http.StatusBadRequest, errors.New("registration was unsuccessful")) return } - c.App.GetAuditLogger().Audit(ctx.Request.Context(), audit.Auth2FAEnrolled, map[string]interface{}{"email": user.Email, "credential": string(credj)}) + c.App.GetAuditLogger().Audit(audit.Auth2FAEnrolled, map[string]interface{}{"email": user.Email, "credential": string(credj)}) ctx.String(http.StatusOK, "{}") } From caf8e6a18fa6b3f44474f516f3d97b0b0a08cfd1 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 11:55:31 -0700 Subject: [PATCH 35/92] Update configuration structures, follow service pattern --- core/logger/audit/audit_logger.go | 135 ++++++++++++++++--------- core/services/chainlink/application.go | 4 + 2 files changed, 91 insertions(+), 48 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 28487ce9065..445bd4297da 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -2,6 +2,7 @@ package audit import ( "bytes" + "context" "encoding/json" "io/ioutil" "net" @@ -11,35 +12,43 @@ import ( "time" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/store/models" "github.com/pkg/errors" ) const bufferCapacity = 2048 +const webRequestTimeout = 10 type Data = map[string]any type AuditLogger interface { Audit(eventID EventID, data Data) + Start() + Stop() } type AuditLoggerConfig struct { - forwardToUrl string - environment string - jsonWrapperKey string - headers []serviceHeader + ForwardToUrl *string + Environment *string + JsonWrapperKey *string + Headers []serviceHeader } func NewAuditLoggerConfig(forwardToUrl string, isDev bool, jsonWrapperKey string, encodedHeaders string) (AuditLoggerConfig, error) { + if forwardToUrl == "" { + return AuditLoggerConfig{}, errors.Errorf("No forwardToURL provided") + } + + if _, err := models.ParseURL(forwardToUrl); err != nil { + return AuditLoggerConfig{}, errors.Errorf("forwardToURL value is not a valid URL") + } + environment := "production" if isDev { environment = "develop" } - if forwardToUrl == "" { - return AuditLoggerConfig{}, errors.Errorf("No forwardToURL provided") - } - // Split and prepare optional service client headers from env variable headers := []serviceHeader{} if encodedHeaders != "" { @@ -57,23 +66,27 @@ func NewAuditLoggerConfig(forwardToUrl string, isDev bool, jsonWrapperKey string } return AuditLoggerConfig{ - forwardToUrl: forwardToUrl, - environment: environment, - jsonWrapperKey: jsonWrapperKey, - headers: headers, + ForwardToUrl: &forwardToUrl, + Environment: &environment, + JsonWrapperKey: &jsonWrapperKey, + Headers: headers, }, nil } type AuditLoggerService struct { - logger logger.Logger - enabled bool - serviceURL string - serviceHeaders []serviceHeader - jsonWrapperKey string - environmentName string - hostname string - localIP string - loggingChannel chan WrappedAuditLog + logger logger.Logger // The standard logger configured in the node + enabled bool // Whether the audit logger is enabled or not + forwardToUrl string // Location we are going to send logs to + headers []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 + localIP string // A non-loopback IP address as reported by the machine + + loggingChannel chan WrappedAuditLog + ctx context.Context + cancel context.CancelFunc + chDone chan struct{} } // Configurable headers to include in POST to log service @@ -103,29 +116,35 @@ func NewAuditLogger(logger logger.Logger, config *AuditLoggerConfig) (AuditLogge return &AuditLoggerService{}, errors.Errorf("Audit Log initialization error - unable to get hostname: %s", err) } + ctx, cancel := context.WithCancel(context.Background()) + loggingChannel := make(chan WrappedAuditLog, bufferCapacity) - // Finally, create new auditLogger with parameters + // Create new AuditLoggerService auditLogger := AuditLoggerService{ logger: logger.Helper(1), enabled: true, - serviceURL: config.forwardToUrl, - serviceHeaders: config.headers, - jsonWrapperKey: config.jsonWrapperKey, - environmentName: config.environment, + forwardToUrl: *config.ForwardToUrl, + headers: config.Headers, + jsonWrapperKey: *config.JsonWrapperKey, + environmentName: *config.Environment, hostname: hostname, localIP: getLocalIP(), - loggingChannel: loggingChannel, + + loggingChannel: loggingChannel, + ctx: ctx, + cancel: cancel, + chDone: make(chan struct{}), } // Start our go routine that will receive logs and send them out to the // configured service. - go auditLogger.auditLoggerRoutine() + // /go auditLogger.auditLoggerRoutine() return &auditLogger, nil } -// / Entrypoint for new audit logs. This buffers all logs that come in they will +// Entrypoint for new audit logs. This buffers all logs that come in they will // / sent out by the goroutine that was started when the AuditLoggerService was // / created. If this service was not enabled, this immeidately returns. // / @@ -151,24 +170,44 @@ func (l *AuditLoggerService) Audit(eventID EventID, data Data) { } } -// / Entrypoint for our log handling goroutine. This waits on the channel and sends out -// / logs as they come in. -// / -// / This function calls postLogToLogService which blocks. -func (l *AuditLoggerService) auditLoggerRoutine() { - for event := range l.loggingChannel { - l.postLogToLogService(event.eventID, event.data) - } +// Start is a comment which satisfies the linter +func (l *AuditLoggerService) Start() { + l.logger.Debugf("Enabled audit logger service") + go l.runLoop() +} - l.logger.Errorw("Audit logger is shut down. Will not send requested audit log") +// Stop is a comment which satisfies the linter +func (l *AuditLoggerService) Stop() { + l.logger.Warnf("Disabled the audit logger service") + l.cancel() + <-l.chDone } -// / Takes an EventID and associated data and sends it to the configured logging -// / endpoint. This function blocks on the send by timesout after a period of -// / several seconds. This helps us prevent getting stuck on a single log -// / due to transient network errors. -// / -// / This function blocks when called. +// Entrypoint for our log handling goroutine. This waits on the channel and sends out +// logs as they come in. +// +// This function calls postLogToLogService which blocks. +func (l *AuditLoggerService) runLoop() { + defer close(l.chDone) + + for { + select { + case <-l.ctx.Done(): + // I've made this an error since we expect it should never happen. + l.logger.Errorf("The audit logger has been requested to shut down!") + return + case event := <-l.loggingChannel: + l.postLogToLogService(event.eventID, event.data) + } + } +} + +// Takes an EventID and associated data and sends it to the configured logging +// endpoint. This function blocks on the send by timesout after a period of +// several seconds. This helps us prevent getting stuck on a single log +// due to transient network errors. +// +// This function blocks when called. func (l *AuditLoggerService) postLogToLogService(eventID EventID, data Data) { // Audit log JSON data logItem := map[string]interface{}{ @@ -190,10 +229,10 @@ func (l *AuditLoggerService) postLogToLogService(eventID EventID, data Data) { return } - // Send up to HEC log collector - httpClient := &http.Client{Timeout: time.Second * 10} - req, _ := http.NewRequest("POST", l.serviceURL, bytes.NewReader(serializedLog)) - for _, header := range l.serviceHeaders { + // Send to remote service + httpClient := &http.Client{Timeout: time.Second * webRequestTimeout} + req, _ := http.NewRequest("POST", l.forwardToUrl, bytes.NewReader(serializedLog)) + for _, header := range l.headers { req.Header.Add(header.header, header.value) } resp, err := httpClient.Do(req) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 31b6d2ece79..a1fd9433ece 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -207,6 +207,10 @@ func NewApplication(opts ApplicationOpts) (Application, error) { return nil, errors.Errorf("Unable to initialize audit logger: %s", err) } + if auditLogger != nil { + auditLogger.Start() + } + var profiler *pyroscope.Profiler if cfg.PyroscopeServerAddress() != "" { globalLogger.Debug("Pyroscope (automatic pprof profiling) is enabled") From 7fe93123eb5b7af87870d393b3b1d9aa49bf22d3 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 13:50:31 -0700 Subject: [PATCH 36/92] Servicify AuditLogger --- core/logger/audit/audit_logger.go | 56 +++++++++++++++++++------- core/services/chainlink/application.go | 4 +- 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 445bd4297da..db6f5424357 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -12,6 +12,7 @@ import ( "time" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/services" "github.com/smartcontractkit/chainlink/core/store/models" "github.com/pkg/errors" @@ -23,9 +24,9 @@ const webRequestTimeout = 10 type Data = map[string]any type AuditLogger interface { + services.ServiceCtx + Audit(eventID EventID, data Data) - Start() - Stop() } type AuditLoggerConfig struct { @@ -137,18 +138,14 @@ func NewAuditLogger(logger logger.Logger, config *AuditLoggerConfig) (AuditLogge chDone: make(chan struct{}), } - // Start our go routine that will receive logs and send them out to the - // configured service. - // /go auditLogger.auditLoggerRoutine() - return &auditLogger, nil } // Entrypoint for new audit logs. This buffers all logs that come in they will -// / sent out by the goroutine that was started when the AuditLoggerService was -// / created. If this service was not enabled, this immeidately returns. -// / -// / This function never blocks. +// sent out by the goroutine that was started when the AuditLoggerService was +// created. If this service was not enabled, this immeidately returns. +// +// This function never blocks. func (l *AuditLoggerService) Audit(eventID EventID, data Data) { if !l.enabled { return @@ -170,17 +167,48 @@ func (l *AuditLoggerService) Audit(eventID EventID, data Data) { } } -// Start is a comment which satisfies the linter -func (l *AuditLoggerService) Start() { +// Start the audit logger and begin processing logs on the channel +func (l *AuditLoggerService) Start(context.Context) error { + if !l.enabled { + return errors.Errorf("The audit logger is not enabled") + } l.logger.Debugf("Enabled audit logger service") go l.runLoop() + + return nil } -// Stop is a comment which satisfies the linter -func (l *AuditLoggerService) Stop() { +// Stops the logger and will close the channel. +func (l *AuditLoggerService) Close() error { + if !l.enabled { + return errors.Errorf("The audit logger is not enabled") + } + l.logger.Warnf("Disabled the audit logger service") l.cancel() <-l.chDone + + return nil +} + +func (l *AuditLoggerService) Healthy() error { + if !l.enabled { + return errors.Errorf("The audit logger is not enabled") + } + + if len(l.loggingChannel) == bufferCapacity { + return errors.Errorf("The audit log buffer is full") + } + + return nil +} + +func (l *AuditLoggerService) Ready() error { + if !l.enabled { + return errors.Errorf("The audit logger is not enabled") + } + + return nil } // Entrypoint for our log handling goroutine. This waits on the channel and sends out diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index a1fd9433ece..0e10a401d6f 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -207,9 +207,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { return nil, errors.Errorf("Unable to initialize audit logger: %s", err) } - if auditLogger != nil { - auditLogger.Start() - } + subservices = append(subservices, auditLogger) var profiler *pyroscope.Profiler if cfg.PyroscopeServerAddress() != "" { From dabe093152054da5b901e8486abec9f6e40ec85c Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 14:19:15 -0700 Subject: [PATCH 37/92] Address Krage feedback --- core/logger/audit/audit_logger.go | 33 +++++++++++++++++-------------- core/sessions/orm.go | 1 - core/web/jobs_controller.go | 8 ++++++-- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index db6f5424357..e62fecf0fc9 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -33,7 +33,7 @@ type AuditLoggerConfig struct { ForwardToUrl *string Environment *string JsonWrapperKey *string - Headers []serviceHeader + Headers []ServiceHeader } func NewAuditLoggerConfig(forwardToUrl string, isDev bool, jsonWrapperKey string, encodedHeaders string) (AuditLoggerConfig, error) { @@ -51,7 +51,7 @@ func NewAuditLoggerConfig(forwardToUrl string, isDev bool, jsonWrapperKey string } // Split and prepare optional service client headers from env variable - headers := []serviceHeader{} + headers := []ServiceHeader{} if encodedHeaders != "" { headerLines := strings.Split(encodedHeaders, "\\") for _, header := range headerLines { @@ -59,9 +59,9 @@ func NewAuditLoggerConfig(forwardToUrl string, isDev bool, jsonWrapperKey string if len(keyValue) != 2 { return AuditLoggerConfig{}, errors.Errorf("Invalid headers provided for the audit logger. Value, single pair split on || required, got: %s", keyValue) } - headers = append(headers, serviceHeader{ - header: keyValue[0], - value: keyValue[1], + headers = append(headers, ServiceHeader{ + Header: keyValue[0], + Value: keyValue[1], }) } } @@ -78,25 +78,25 @@ type AuditLoggerService struct { logger logger.Logger // The standard logger configured in the node enabled bool // Whether the audit logger is enabled or not forwardToUrl string // Location we are going to send logs to - headers []serviceHeader // Headers to be sent along with logs for identification/authentication + headers []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 localIP string // A non-loopback IP address as reported by the machine - loggingChannel chan WrappedAuditLog + loggingChannel chan wrappedAuditLog ctx context.Context cancel context.CancelFunc chDone chan struct{} } // Configurable headers to include in POST to log service -type serviceHeader struct { - header string - value string +type ServiceHeader struct { + Header string + Value string } -type WrappedAuditLog struct { +type wrappedAuditLog struct { eventID EventID data Data } @@ -119,7 +119,7 @@ func NewAuditLogger(logger logger.Logger, config *AuditLoggerConfig) (AuditLogge ctx, cancel := context.WithCancel(context.Background()) - loggingChannel := make(chan WrappedAuditLog, bufferCapacity) + loggingChannel := make(chan wrappedAuditLog, bufferCapacity) // Create new AuditLoggerService auditLogger := AuditLoggerService{ @@ -151,7 +151,7 @@ func (l *AuditLoggerService) Audit(eventID EventID, data Data) { return } - wrappedLog := WrappedAuditLog{ + wrappedLog := wrappedAuditLog{ eventID: eventID, data: data, } @@ -259,9 +259,12 @@ func (l *AuditLoggerService) postLogToLogService(eventID EventID, data Data) { // Send to remote service httpClient := &http.Client{Timeout: time.Second * webRequestTimeout} - req, _ := http.NewRequest("POST", l.forwardToUrl, bytes.NewReader(serializedLog)) + req, err := http.NewRequest("POST", l.forwardToUrl, bytes.NewReader(serializedLog)) + if err != nil { + l.logger.Errorf("Failed to create request to remote logging service!") + } for _, header := range l.headers { - req.Header.Add(header.header, header.value) + req.Header.Add(header.Header, header.Value) } resp, err := httpClient.Do(req) if err != nil { diff --git a/core/sessions/orm.go b/core/sessions/orm.go index 6f871d6397a..e8f6dfde88e 100644 --- a/core/sessions/orm.go +++ b/core/sessions/orm.go @@ -245,7 +245,6 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { uwasj, err := json.Marshal(uwas) if err != nil { lggr.Errorf("error in Marshal credentials: %s", err) - return "", err } o.auditLogger.Audit(audit.AuthLoginSuccessWith2FA, map[string]interface{}{"email": sr.Email, "credential": string(uwasj)}) diff --git a/core/web/jobs_controller.go b/core/web/jobs_controller.go index 77d2c742a82..04da7aa9e5e 100644 --- a/core/web/jobs_controller.go +++ b/core/web/jobs_controller.go @@ -158,8 +158,12 @@ func (jc *JobsController) Create(c *gin.Context) { return } - jbj, _ := json.Marshal(jb) - jc.App.GetAuditLogger().Audit(audit.JobCreated, map[string]interface{}{"job": string(jbj)}) + jbj, err := json.Marshal(jb) + if err == nil { + jc.App.GetAuditLogger().Audit(audit.JobCreated, map[string]interface{}{"job": string(jbj)}) + } else { + jc.App.GetLogger().Errorf("Could not send audit log for JobCreation", "err", err) + } jsonAPIResponse(c, presenters.NewJobResource(jb), jb.Type.String()) } From 5ebc8d638811ec8fbe0646bfe2591e3ef82785cc Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 14:56:17 -0700 Subject: [PATCH 38/92] Change config name --- core/config/v2/types.go | 2 +- core/services/chainlink/testdata/config-full.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/config/v2/types.go b/core/config/v2/types.go index c2aef7a50d2..d332b862081 100644 --- a/core/config/v2/types.go +++ b/core/config/v2/types.go @@ -20,7 +20,7 @@ import ( // Core holds the core configuration. See chainlink.Config for more information. type Core struct { // General/misc - AuditLoggerConfig *audit.AuditLoggerConfig + AuditLogger *audit.AuditLoggerConfig ExplorerURL *models.URL InsecureFastScrypt *bool RootDir *string diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index d0c69fa5ac8..977f9137ac6 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -3,7 +3,7 @@ InsecureFastScrypt = true RootDir = 'test/root/dir' ShutdownGracePeriod = '10s' -[AuditLoggerConfig] +[AuditLogger] forwardToUrl = "http://localhost:9898" headers = [] environment = "develop" From 78dfe22440f8b3b6b0c59e3f56d80f069f143086 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 15:09:53 -0700 Subject: [PATCH 39/92] Fix build --- core/services/chainlink/config_general.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 1643f451bab..c1b22c34d0d 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -168,7 +168,7 @@ func (g *generalConfig) AllowOrigins() string { } func (g *generalConfig) AuditLoggerConfig() *audit.AuditLoggerConfig { - return g.c.AuditLoggerConfig + return g.c.AuditLogger } func (g *generalConfig) AuthenticatedRateLimit() int64 { From b928d0b862117e6dde83906a14c15786e2a125da Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 15:13:57 -0700 Subject: [PATCH 40/92] Don't try to audit on marshal failure --- core/sessions/orm.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/sessions/orm.go b/core/sessions/orm.go index e8f6dfde88e..e1b3738b58f 100644 --- a/core/sessions/orm.go +++ b/core/sessions/orm.go @@ -245,8 +245,9 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { uwasj, err := json.Marshal(uwas) if err != nil { lggr.Errorf("error in Marshal credentials: %s", err) + } else { + o.auditLogger.Audit(audit.AuthLoginSuccessWith2FA, map[string]interface{}{"email": sr.Email, "credential": string(uwasj)}) } - o.auditLogger.Audit(audit.AuthLoginSuccessWith2FA, map[string]interface{}{"email": sr.Email, "credential": string(uwasj)}) return session.ID, nil } From fce3ed8b3c7a22e655f503fb90a5e557647df3e9 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 15:52:33 -0700 Subject: [PATCH 41/92] Fix docs --- core/services/chainlink/config_test.go | 9 +++++ .../chainlink/testdata/config-full.toml | 7 ++-- docs/CONFIG.md | 35 +++++++++++++++++++ internal/config/docs.toml | 11 ++++++ 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index c762adeddc1..9364db0a017 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -31,6 +31,7 @@ import ( legacy "github.com/smartcontractkit/chainlink/core/config" config "github.com/smartcontractkit/chainlink/core/config/v2" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink/cfgtest" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/p2pkey" @@ -204,6 +205,14 @@ func TestConfig_Marshal(t *testing.T) { } full := global + + full.AuditLogger = &audit.AuditLoggerConfig{ + ForwardToUrl: ptr("http://localhost:9898"), + Environment: ptr("develop"), + Headers: make([]audit.ServiceHeader, 0), + JsonWrapperKey: ptr(""), + } + full.Feature = &config.Feature{ FeedsManager: ptr(true), LogPoller: ptr(true), diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 977f9137ac6..895fb58a4ea 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -4,9 +4,10 @@ RootDir = 'test/root/dir' ShutdownGracePeriod = '10s' [AuditLogger] -forwardToUrl = "http://localhost:9898" -headers = [] -environment = "develop" +ForwardToUrl = 'http://localhost:9898' +Environment = 'develop' +JsonWrapperKey = '' +Headers = [] [Feature] FeedsManager = true diff --git a/docs/CONFIG.md b/docs/CONFIG.md index d77193f7b8e..e966c98458b 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -3,6 +3,7 @@ ## Table of contents - [Global](#Global) +- [AuditLogger](#AuditLogger) - [Feature](#Feature) - [Database](#Database) - [Backup](#Database-Backup) @@ -75,6 +76,40 @@ ShutdownGracePeriod = '5s' # Default ``` ShutdownGracePeriod is the maximum time allowed to shut down gracefully. If exceeded, the node will terminate immediately to avoid being SIGKILLed. +## AuditLogger +```toml +[AuditLogger] +ForwardToUrl = 'http://localhost:9898' # Example +Environment = 'develop' # Example +JsonWrapperKey = '' # Default +Headers = [] # Default +``` + + +### ForwardToUrl +```toml +ForwardToUrl = 'http://localhost:9898' # Example +``` +ForwardToUrl is where you want to forward logs to + +### Environment +```toml +Environment = 'develop' # Example +``` +Environment is an identifier for the host/environment you're on + +### JsonWrapperKey +```toml +JsonWrapperKey = '' # Default +``` +JsonWrapperKey if set wraps the map of data under another single key to make parsing easier + +### Headers +```toml +Headers = [] # Default +``` +Headers is the set of headers you wish to pass along with each request + ## Feature ```toml [Feature] diff --git a/internal/config/docs.toml b/internal/config/docs.toml index 71e9608150a..e30982ebf3f 100644 --- a/internal/config/docs.toml +++ b/internal/config/docs.toml @@ -8,6 +8,17 @@ RootDir = '~/.chainlink' # Default # ShutdownGracePeriod is the maximum time allowed to shut down gracefully. If exceeded, the node will terminate immediately to avoid being SIGKILLed. ShutdownGracePeriod = '5s' # Default +[AuditLogger] +# ForwardToUrl is where you want to forward logs to +ForwardToUrl = 'http://localhost:9898' # Example +# Environment is an identifier for the host/environment you're on +Environment = 'develop' # Example +# JsonWrapperKey if set wraps the map of data under another single key to make parsing easier +JsonWrapperKey = '' # Default +# Headers is the set of headers you wish to pass along with each request +Headers = [] # Default + + [Feature] # FeedsManager enables the experimental feeds manager service. FeedsManager = false # Default From 9969e8bb3fecda5baaa6331577c1e2291a8c88cc Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 16:31:59 -0700 Subject: [PATCH 42/92] Return nil instead --- core/config/v2/types.go | 4 ++-- core/logger/audit/audit_logger.go | 4 ++-- core/services/chainlink/application.go | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/core/config/v2/types.go b/core/config/v2/types.go index d332b862081..cbdd2b16fdb 100644 --- a/core/config/v2/types.go +++ b/core/config/v2/types.go @@ -20,7 +20,6 @@ import ( // Core holds the core configuration. See chainlink.Config for more information. type Core struct { // General/misc - AuditLogger *audit.AuditLoggerConfig ExplorerURL *models.URL InsecureFastScrypt *bool RootDir *string @@ -32,7 +31,8 @@ type Core struct { TelemetryIngress *TelemetryIngress - Log *Log + AuditLogger *audit.AuditLoggerConfig + Log *Log WebServer *WebServer diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index e62fecf0fc9..daa949886ea 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -109,12 +109,12 @@ type wrappedAuditLog struct { func NewAuditLogger(logger logger.Logger, config *AuditLoggerConfig) (AuditLogger, error) { if config == nil { logger.Info("Audit logger configuration is nil. Cannot start audit logger subsystem and audit events will not be captured.") - return &AuditLoggerService{}, nil + return nil, nil } hostname, err := os.Hostname() if err != nil { - return &AuditLoggerService{}, errors.Errorf("Audit Log initialization error - unable to get hostname: %s", err) + return nil, errors.Errorf("Audit Log initialization error - unable to get hostname: %s", err) } ctx, cancel := context.WithCancel(context.Background()) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 0e10a401d6f..87b7117b468 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -207,7 +207,9 @@ func NewApplication(opts ApplicationOpts) (Application, error) { return nil, errors.Errorf("Unable to initialize audit logger: %s", err) } - subservices = append(subservices, auditLogger) + if auditLogger != nil { + subservices = append(subservices, auditLogger) + } var profiler *pyroscope.Profiler if cfg.PyroscopeServerAddress() != "" { From dbe1e751aed0495496e2aa174031305f464fbd78 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 17:29:30 -0700 Subject: [PATCH 43/92] Fix error handling --- core/logger/audit/audit_logger.go | 5 ++--- core/services/chainlink/application.go | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index daa949886ea..1575936e1c2 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -108,13 +108,12 @@ type wrappedAuditLog struct { // is disabled and short circuits execution via enabled flag. func NewAuditLogger(logger logger.Logger, config *AuditLoggerConfig) (AuditLogger, error) { if config == nil { - logger.Info("Audit logger configuration is nil. Cannot start audit logger subsystem and audit events will not be captured.") - return nil, nil + return &AuditLoggerService{}, errors.Errorf("Audit Log initialization error - no configuration") } hostname, err := os.Hostname() if err != nil { - return nil, errors.Errorf("Audit Log initialization error - unable to get hostname: %s", err) + return &AuditLoggerService{}, errors.Errorf("Audit Log initialization error - unable to get hostname: %s", err) } ctx, cancel := context.WithCancel(context.Background()) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 87b7117b468..fa72d756561 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -204,10 +204,8 @@ func NewApplication(opts ApplicationOpts) (Application, error) { // Configure and optionally start the audit log forwarder service auditLogger, err := audit.NewAuditLogger(globalLogger, cfg.AuditLoggerConfig()) if err != nil { - return nil, errors.Errorf("Unable to initialize audit logger: %s", err) - } - - if auditLogger != nil { + globalLogger.Info("Audit logger could not be started. err: ", err) + } else { subservices = append(subservices, auditLogger) } From e6e3d5240457d7116bd6939f16f41aac6d3ea2c1 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Fri, 16 Sep 2022 18:31:32 -0700 Subject: [PATCH 44/92] Try to fix tests --- .../chainlink/testdata/config-full.toml | 12 ++-- docs/CONFIG.md | 70 +++++++++---------- internal/config/docs.toml | 21 +++--- 3 files changed, 51 insertions(+), 52 deletions(-) diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 895fb58a4ea..0959879cd5d 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -3,12 +3,6 @@ InsecureFastScrypt = true RootDir = 'test/root/dir' ShutdownGracePeriod = '10s' -[AuditLogger] -ForwardToUrl = 'http://localhost:9898' -Environment = 'develop' -JsonWrapperKey = '' -Headers = [] - [Feature] FeedsManager = true LogPoller = true @@ -48,6 +42,12 @@ SendInterval = '1m0s' SendTimeout = '5s' UseBatchSend = true +[AuditLogger] +ForwardToUrl = 'http://localhost:9898' +Environment = 'develop' +JsonWrapperKey = '' +Headers = [] + [Log] DatabaseQueries = true FileDir = 'log/file/dir' diff --git a/docs/CONFIG.md b/docs/CONFIG.md index e966c98458b..5d22a1730e8 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -3,13 +3,13 @@ ## Table of contents - [Global](#Global) -- [AuditLogger](#AuditLogger) - [Feature](#Feature) - [Database](#Database) - [Backup](#Database-Backup) - [Listener](#Database-Listener) - [Lock](#Database-Lock) - [TelemetryIngress](#TelemetryIngress) +- [AuditLogger](#AuditLogger) - [Log](#Log) - [WebServer](#WebServer) - [RateLimit](#WebServer-RateLimit) @@ -76,40 +76,6 @@ ShutdownGracePeriod = '5s' # Default ``` ShutdownGracePeriod is the maximum time allowed to shut down gracefully. If exceeded, the node will terminate immediately to avoid being SIGKILLed. -## AuditLogger -```toml -[AuditLogger] -ForwardToUrl = 'http://localhost:9898' # Example -Environment = 'develop' # Example -JsonWrapperKey = '' # Default -Headers = [] # Default -``` - - -### ForwardToUrl -```toml -ForwardToUrl = 'http://localhost:9898' # Example -``` -ForwardToUrl is where you want to forward logs to - -### Environment -```toml -Environment = 'develop' # Example -``` -Environment is an identifier for the host/environment you're on - -### JsonWrapperKey -```toml -JsonWrapperKey = '' # Default -``` -JsonWrapperKey if set wraps the map of data under another single key to make parsing easier - -### Headers -```toml -Headers = [] # Default -``` -Headers is the set of headers you wish to pass along with each request - ## Feature ```toml [Feature] @@ -363,6 +329,40 @@ UseBatchSend = true # Default ``` UseBatchSend toggles sending telemetry to the ingress server using the batch client. +## AuditLogger +```toml +[AuditLogger] +ForwardToUrl = 'http://localhost:9898' # Example +Environment = 'develop' # Example +JsonWrapperKey = '' # Default +Headers = [] # Default +``` + + +### ForwardToUrl +```toml +ForwardToUrl = 'http://localhost:9898' # Example +``` +ForwardToUrl is where you want to forward logs to + +### Environment +```toml +Environment = 'develop' # Example +``` +Environment is an identifier for the host/environment you're on + +### JsonWrapperKey +```toml +JsonWrapperKey = '' # Default +``` +JsonWrapperKey if set wraps the map of data under another single key to make parsing easier + +### Headers +```toml +Headers = [] # Default +``` +Headers is the set of headers you wish to pass along with each request + ## Log ```toml [Log] diff --git a/internal/config/docs.toml b/internal/config/docs.toml index e30982ebf3f..dc0fc085053 100644 --- a/internal/config/docs.toml +++ b/internal/config/docs.toml @@ -8,17 +8,6 @@ RootDir = '~/.chainlink' # Default # ShutdownGracePeriod is the maximum time allowed to shut down gracefully. If exceeded, the node will terminate immediately to avoid being SIGKILLed. ShutdownGracePeriod = '5s' # Default -[AuditLogger] -# ForwardToUrl is where you want to forward logs to -ForwardToUrl = 'http://localhost:9898' # Example -# Environment is an identifier for the host/environment you're on -Environment = 'develop' # Example -# JsonWrapperKey if set wraps the map of data under another single key to make parsing easier -JsonWrapperKey = '' # Default -# Headers is the set of headers you wish to pass along with each request -Headers = [] # Default - - [Feature] # FeedsManager enables the experimental feeds manager service. FeedsManager = false # Default @@ -116,6 +105,16 @@ SendTimeout = '10s' # Default # UseBatchSend toggles sending telemetry to the ingress server using the batch client. UseBatchSend = true # Default +[AuditLogger] +# ForwardToUrl is where you want to forward logs to +ForwardToUrl = 'http://localhost:9898' # Example +# Environment is an identifier for the host/environment you're on +Environment = 'develop' # Example +# JsonWrapperKey if set wraps the map of data under another single key to make parsing easier +JsonWrapperKey = '' # Default +# Headers is the set of headers you wish to pass along with each request +Headers = [] # Default + [Log] # DatabaseQueries tells the Chainlink node to log database queries made using the default logger. SQL statements will be logged at `debug` level. Not all statements can be logged. The best way to get a true log of all SQL statements is to enable SQL statement logging on Postgres. DatabaseQueries = false # Default From 4905922921dd56fa89694ac3648a775af9e35f76 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Mon, 19 Sep 2022 18:45:00 -0700 Subject: [PATCH 45/92] Change initialization to be test friendly --- core/cmd/client.go | 8 ++ core/internal/cltest/cltest.go | 11 +++ core/logger/audit/audit_logger.go | 43 +++++++--- core/logger/audit/audit_logger_test.go | 109 +++++++++++++++++++++++++ core/services/chainlink/application.go | 63 +++++++------- 5 files changed, 190 insertions(+), 44 deletions(-) create mode 100644 core/logger/audit/audit_logger_test.go diff --git a/core/cmd/client.go b/core/cmd/client.go index 8f57f888216..0c835484806 100644 --- a/core/cmd/client.go +++ b/core/cmd/client.go @@ -33,6 +33,7 @@ import ( "github.com/smartcontractkit/chainlink/core/chains/terra" "github.com/smartcontractkit/chainlink/core/config" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/keystore" "github.com/smartcontractkit/chainlink/core/services/periodicbackup" @@ -217,6 +218,12 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg config.Gene } } + // Configure and optionally start the audit log forwarder service + auditLogger, err := audit.NewAuditLogger(appLggr, cfg.AuditLoggerConfig()) + if err != nil { + appLggr.Info("Audit logger could not be started. err: ", err) + } + restrictedClient := clhttp.NewRestrictedHTTPClient(cfg, appLggr) unrestrictedClient := clhttp.NewUnrestrictedHTTPClient() externalInitiatorManager := webhook.NewExternalInitiatorManager(db, unrestrictedClient, appLggr, cfg) @@ -227,6 +234,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg config.Gene Chains: chains, EventBroadcaster: eventBroadcaster, Logger: appLggr, + AuditLogger: auditLogger, CloseLogger: closeLggr, ExternalInitiatorManager: externalInitiatorManager, Version: static.Version, diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index d8a6c0f551c..5a920efc34a 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -63,6 +63,7 @@ import ( clhttptest "github.com/smartcontractkit/chainlink/core/internal/testutils/httptest" "github.com/smartcontractkit/chainlink/core/internal/testutils/keystest" "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/services/job" "github.com/smartcontractkit/chainlink/core/services/keystore" @@ -332,6 +333,15 @@ func NewApplicationWithConfig(t testing.TB, cfg *configtest.TestGeneralConfig, f lggr = logger.TestLogger(t) } + var auditLogger audit.AuditLogger + for _, dep := range flagsAndDeps { + audLgger, is := dep.(audit.AuditLogger) + if is { + auditLogger = audLgger + break + } + } + var eventBroadcaster pg.EventBroadcaster = pg.NewNullEventBroadcaster() url := cfg.DatabaseURL() @@ -445,6 +455,7 @@ func NewApplicationWithConfig(t testing.TB, cfg *configtest.TestGeneralConfig, f KeyStore: keyStore, Chains: chains, Logger: lggr, + AuditLogger: auditLogger, CloseLogger: lggr.Sync, ExternalInitiatorManager: externalInitiatorManager, RestrictedHTTPClient: c, diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 1575936e1c2..bdd84b3650d 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "fmt" "io/ioutil" "net" "net/http" @@ -36,6 +37,10 @@ type AuditLoggerConfig struct { Headers []ServiceHeader } +type HTTPAuditLoggerInterface interface { + Do(req *http.Request) (*http.Response, error) +} + func NewAuditLoggerConfig(forwardToUrl string, isDev bool, jsonWrapperKey string, encodedHeaders string) (AuditLoggerConfig, error) { if forwardToUrl == "" { return AuditLoggerConfig{}, errors.Errorf("No forwardToURL provided") @@ -75,14 +80,15 @@ func NewAuditLoggerConfig(forwardToUrl string, isDev bool, jsonWrapperKey string } type AuditLoggerService struct { - logger logger.Logger // The standard logger configured in the node - enabled bool // Whether the audit logger is enabled or not - forwardToUrl string // Location we are going to send logs to - headers []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 - localIP string // A non-loopback IP address as reported by the machine + logger logger.Logger // The standard logger configured in the node + enabled bool // Whether the audit logger is enabled or not + forwardToUrl string // Location we are going to send logs to + headers []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 + localIP string // A non-loopback IP address as reported by the machine + loggingClient HTTPAuditLoggerInterface // Abstract type for sending logs onward loggingChannel chan wrappedAuditLog ctx context.Context @@ -130,6 +136,7 @@ func NewAuditLogger(logger logger.Logger, config *AuditLoggerConfig) (AuditLogge environmentName: *config.Environment, hostname: hostname, localIP: getLocalIP(), + loggingClient: &http.Client{Timeout: time.Second * webRequestTimeout}, loggingChannel: loggingChannel, ctx: ctx, @@ -140,13 +147,19 @@ func NewAuditLogger(logger logger.Logger, config *AuditLoggerConfig) (AuditLogge return &auditLogger, nil } +func (l *AuditLoggerService) SetLoggingClient(newClient HTTPAuditLoggerInterface) { + l.loggingClient = newClient +} + // Entrypoint for new audit logs. This buffers all logs that come in they will // sent out by the goroutine that was started when the AuditLoggerService was // created. If this service was not enabled, this immeidately returns. // // This function never blocks. func (l *AuditLoggerService) Audit(eventID EventID, data Data) { + fmt.Println("An audit log is being sent!") if !l.enabled { + fmt.Println("Audit logger is not enabled?") return } @@ -155,6 +168,8 @@ func (l *AuditLoggerService) Audit(eventID EventID, data Data) { data: data, } + l.logger.Errorf("SEnding!!!") + select { case l.loggingChannel <- wrappedLog: default: @@ -171,9 +186,8 @@ func (l *AuditLoggerService) Start(context.Context) error { if !l.enabled { return errors.Errorf("The audit logger is not enabled") } - l.logger.Debugf("Enabled audit logger service") go l.runLoop() - + fmt.Println("Started the runloop") return nil } @@ -257,7 +271,6 @@ func (l *AuditLoggerService) postLogToLogService(eventID EventID, data Data) { } // Send to remote service - httpClient := &http.Client{Timeout: time.Second * webRequestTimeout} req, err := http.NewRequest("POST", l.forwardToUrl, bytes.NewReader(serializedLog)) if err != nil { l.logger.Errorf("Failed to create request to remote logging service!") @@ -265,12 +278,17 @@ func (l *AuditLoggerService) postLogToLogService(eventID EventID, data Data) { for _, header := range l.headers { req.Header.Add(header.Header, header.Value) } - resp, err := httpClient.Do(req) + resp, err := l.loggingClient.Do(req) if err != nil { l.logger.Errorw("Failed to send audit log to HTTP log service", "err", err, "logItem", logItem) return } if resp.StatusCode != 200 { + if resp.Body == nil { + l.logger.Errorw("There was no body to read. Possibly an error occured sending", "logItem", logItem) + return + } + bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { l.logger.Errorw("Error reading errored HTTP log service webhook response body", "err", err, "logItem", logItem) @@ -278,6 +296,7 @@ func (l *AuditLoggerService) postLogToLogService(eventID EventID, data Data) { } l.logger.Errorw("Error sending log to HTTP log service", "statusCode", resp.StatusCode, "bodyString", string(bodyBytes)) return + } } diff --git a/core/logger/audit/audit_logger_test.go b/core/logger/audit/audit_logger_test.go new file mode 100644 index 00000000000..eb3148d2b47 --- /dev/null +++ b/core/logger/audit/audit_logger_test.go @@ -0,0 +1,109 @@ +package audit_test + +import ( + "flag" + "io" + "net/http" + "testing" + "time" + + "github.com/smartcontractkit/chainlink/core/internal/cltest" + "github.com/smartcontractkit/chainlink/core/internal/testutils" + "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/logger/audit" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/urfave/cli" +) + +type MockedHTTPEvent struct { + body string +} + +type MockHTTPClient struct { + audit.HTTPAuditLoggerInterface + + loggingChannel chan MockedHTTPEvent +} + +func (mock *MockHTTPClient) Do(req *http.Request) (*http.Response, error) { + b, err := io.ReadAll(req.Body) + + if err != nil { + return nil, err + } + + message := MockedHTTPEvent{ + body: string(b), + } + + mock.loggingChannel <- message + + return &http.Response{}, nil +} + +func getAuditLoggerConfig() *audit.AuditLoggerConfig { + forwardToUrl := "empty" + environment := "test" + jsonWrapperKey := "" + + return &audit.AuditLoggerConfig{ + ForwardToUrl: &forwardToUrl, + Environment: &environment, + JsonWrapperKey: &jsonWrapperKey, + } +} + +func TestCheckLoginAuditLog(t *testing.T) { + t.Parallel() + + // Create a channel that will be used instead of an HTTP client + loggingChannel := make(chan MockedHTTPEvent, 2048) + + // Create the mock structure that will be used + mockHTTPClient := MockHTTPClient{ + loggingChannel: loggingChannel, + } + + // Set an environment variable to trick the system into starting the audit logger + // and adding it to the subsystems. We are going to swap it out later so it's not + // going to matter what we put here + // os.Setenv("AUDIT_LOGGER_FORWARD_TO_URL", "http://test.local:9999") + + logger := logger.TestLogger(t) + + // Create new AuditLoggerService + auditLogger, err := audit.NewAuditLogger(logger, getAuditLoggerConfig()) + assert.NoError(t, err) + + auditLoggerService, ok := auditLogger.(*audit.AuditLoggerService) + assert.True(t, ok) + + auditLoggerService.SetLoggingClient(&mockHTTPClient) + + assert.NoError(t, auditLoggerService.Ready()) + + app := cltest.NewApplication(t, logger, auditLogger) + + require.NoError(t, app.Start(testutils.Context(t))) + + enteredStrings := []string{cltest.APIEmailAdmin, cltest.Password} + prompter := &cltest.MockCountingPrompter{T: t, EnteredStrings: enteredStrings} + client := app.NewAuthenticatingClient(prompter) + + set := flag.NewFlagSet("test", 0) + set.Bool("bypass-version-check", true, "") + set.String("admin-credentials-file", "", "") + c := cli.NewContext(nil, set, nil) + + err = client.RemoteLogin(c) + assert.NoError(t, err) + + select { + case _ = <-loggingChannel: + return + case <-time.After(15 * time.Second): + } + + assert.True(t, false) +} diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index fa72d756561..1939d2604b7 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -131,11 +131,11 @@ type ChainlinkApplication struct { SessionReaper utils.SleeperTask shutdownOnce sync.Once explorerClient synchronization.ExplorerClient - subservices []services.ServiceCtx + Subservices []services.ServiceCtx HealthChecker services.Checker Nurse *services.Nurse logger logger.Logger - auditLogger audit.AuditLogger + AuditLogger audit.AuditLogger closeLogger func() error sqlxDB *sqlx.DB secretGenerator SecretGenerator @@ -152,6 +152,7 @@ type ApplicationOpts struct { KeyStore keystore.Master Chains Chains Logger logger.Logger + AuditLogger audit.AuditLogger CloseLogger func() error ExternalInitiatorManager webhook.ExternalInitiatorManager Version string @@ -190,23 +191,21 @@ func (c *Chains) services() (s []services.ServiceCtx) { // be used by the node. // TODO: Inject more dependencies here to save booting up useless stuff in tests func NewApplication(opts ApplicationOpts) (Application, error) { - var subservices []services.ServiceCtx - db := opts.SqlxDB + var Subservices []services.ServiceCtx + auditLogger := opts.AuditLogger cfg := opts.Config - keyStore := opts.KeyStore chains := opts.Chains - globalLogger := opts.Logger + db := opts.SqlxDB eventBroadcaster := opts.EventBroadcaster externalInitiatorManager := opts.ExternalInitiatorManager + globalLogger := opts.Logger + keyStore := opts.KeyStore restrictedHTTPClient := opts.RestrictedHTTPClient unrestrictedHTTPClient := opts.UnrestrictedHTTPClient - // Configure and optionally start the audit log forwarder service - auditLogger, err := audit.NewAuditLogger(globalLogger, cfg.AuditLoggerConfig()) - if err != nil { - globalLogger.Info("Audit logger could not be started. err: ", err) - } else { - subservices = append(subservices, auditLogger) + // If the audit logger is enabled + if auditLogger.Ready() == nil { + Subservices = append(Subservices, auditLogger) } var profiler *pyroscope.Profiler @@ -262,7 +261,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { monitoringEndpointGen = telemetry.NewIngressAgentWrapper(telemetryIngressClient) } } - subservices = append(subservices, explorerClient, telemetryIngressClient, telemetryIngressBatchClient) + Subservices = append(Subservices, explorerClient, telemetryIngressClient, telemetryIngressBatchClient) if cfg.DatabaseBackupMode() != config.DatabaseBackupModeNone && cfg.DatabaseBackupFrequency() > 0 { globalLogger.Infow("DatabaseBackup: periodic database backups are enabled", "frequency", cfg.DatabaseBackupFrequency()) @@ -271,15 +270,15 @@ func NewApplication(opts ApplicationOpts) (Application, error) { if err != nil { return nil, errors.Wrap(err, "NewApplication: failed to initialize database backup") } - subservices = append(subservices, databaseBackup) + Subservices = append(Subservices, databaseBackup) } else { globalLogger.Info("DatabaseBackup: periodic database backups are disabled. To enable automatic backups, set DATABASE_BACKUP_MODE=lite or DATABASE_BACKUP_MODE=full") } - subservices = append(subservices, eventBroadcaster) - subservices = append(subservices, chains.services()...) + Subservices = append(Subservices, eventBroadcaster) + Subservices = append(Subservices, chains.services()...) promReporter := promreporter.NewPromReporter(db.DB, globalLogger) - subservices = append(subservices, promReporter) + Subservices = append(Subservices, promReporter) var ( pipelineORM = pipeline.NewORM(db, globalLogger, cfg) @@ -352,7 +351,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { return nil, err } peerWrapper = ocrcommon.NewSingletonPeerWrapper(keyStore, cfg, db, globalLogger) - subservices = append(subservices, peerWrapper) + Subservices = append(Subservices, peerWrapper) } else { globalLogger.Debug("P2P stack disabled") } @@ -378,22 +377,22 @@ func NewApplication(opts ApplicationOpts) (Application, error) { if cfg.EVMEnabled() { evmRelayer := evmrelay.NewRelayer(db, chains.EVM, globalLogger.Named("EVM")) relayers[relay.EVM] = evmRelayer - subservices = append(subservices, evmRelayer) + Subservices = append(Subservices, evmRelayer) } if cfg.SolanaEnabled() { solanaRelayer := pkgsolana.NewRelayer(globalLogger.Named("Solana.Relayer"), chains.Solana) relayers[relay.Solana] = solanaRelayer - subservices = append(subservices, solanaRelayer) + Subservices = append(Subservices, solanaRelayer) } if cfg.TerraEnabled() { terraRelayer := pkgterra.NewRelayer(globalLogger.Named("Terra.Relayer"), chains.Terra) relayers[relay.Terra] = terraRelayer - subservices = append(subservices, terraRelayer) + Subservices = append(Subservices, terraRelayer) } if cfg.StarkNetEnabled() { starknetRelayer := starknetrelay.NewRelayer(globalLogger.Named("StarkNet.Relayer"), chains.StarkNet) relayers[relay.StarkNet] = starknetRelayer - subservices = append(subservices, starknetRelayer) + Subservices = append(Subservices, starknetRelayer) } delegates[job.OffchainReporting2] = ocr2.NewDelegate( db, @@ -426,13 +425,13 @@ func NewApplication(opts ApplicationOpts) (Application, error) { lbs = append(lbs, c.LogBroadcaster()) } jobSpawner := job.NewSpawner(jobORM, cfg, delegates, db, globalLogger, lbs) - subservices = append(subservices, jobSpawner, pipelineRunner) + Subservices = append(Subservices, jobSpawner, pipelineRunner) // We start the log poller after the job spawner // so jobs have a chance to apply their initial log filters. if cfg.FeatureLogPoller() { for _, c := range chains.EVM.Chains() { - subservices = append(subservices, c.LogPoller()) + Subservices = append(Subservices, c.LogPoller()) } } @@ -472,19 +471,19 @@ func NewApplication(opts ApplicationOpts) (Application, error) { HealthChecker: healthChecker, Nurse: nurse, logger: globalLogger, - auditLogger: auditLogger, + AuditLogger: auditLogger, closeLogger: opts.CloseLogger, secretGenerator: opts.SecretGenerator, profiler: profiler, sqlxDB: opts.SqlxDB, - // NOTE: Can keep things clean by putting more things in subservices + // NOTE: Can keep things clean by putting more things in Subservices // instead of manually start/closing - subservices: subservices, + Subservices: Subservices, } - for _, service := range app.subservices { + for _, service := range app.Subservices { checkable := service.(services.Checkable) if err := app.HealthChecker.Register(reflect.TypeOf(service).String(), checkable); err != nil { return nil, err @@ -517,7 +516,7 @@ func (app *ChainlinkApplication) Start(ctx context.Context) error { } } - for _, subservice := range app.subservices { + for _, subservice := range app.Subservices { if ctx.Err() != nil { return errors.Wrap(ctx.Err(), "aborting start") } @@ -570,8 +569,8 @@ func (app *ChainlinkApplication) stop() (err error) { app.logger.Info("Gracefully exiting...") // Stop services in the reverse order from which they were started - for i := len(app.subservices) - 1; i >= 0; i-- { - service := app.subservices[i] + for i := len(app.Subservices) - 1; i >= 0; i-- { + service := app.Subservices[i] app.logger.Debugw("Closing service...", "serviceType", reflect.TypeOf(service)) err = multierr.Append(err, service.Close()) } @@ -613,7 +612,7 @@ func (app *ChainlinkApplication) GetLogger() logger.Logger { } func (app *ChainlinkApplication) GetAuditLogger() audit.AuditLogger { - return app.auditLogger + return app.AuditLogger } func (app *ChainlinkApplication) GetHealthChecker() services.Checker { From 43f578d2e7e3fec2c5b8a4688fad10c7b7ab3994 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Mon, 19 Sep 2022 19:08:34 -0700 Subject: [PATCH 46/92] Remove old prints and do some clean up --- core/logger/audit/audit_logger.go | 6 ---- core/logger/audit/audit_logger_test.go | 36 ++++++++++++++++------ core/services/chainlink/application.go | 42 +++++++++++++------------- 3 files changed, 48 insertions(+), 36 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index bdd84b3650d..20399173e93 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "encoding/json" - "fmt" "io/ioutil" "net" "net/http" @@ -157,9 +156,7 @@ func (l *AuditLoggerService) SetLoggingClient(newClient HTTPAuditLoggerInterface // // This function never blocks. func (l *AuditLoggerService) Audit(eventID EventID, data Data) { - fmt.Println("An audit log is being sent!") if !l.enabled { - fmt.Println("Audit logger is not enabled?") return } @@ -168,8 +165,6 @@ func (l *AuditLoggerService) Audit(eventID EventID, data Data) { data: data, } - l.logger.Errorf("SEnding!!!") - select { case l.loggingChannel <- wrappedLog: default: @@ -187,7 +182,6 @@ func (l *AuditLoggerService) Start(context.Context) error { return errors.Errorf("The audit logger is not enabled") } go l.runLoop() - fmt.Println("Started the runloop") return nil } diff --git a/core/logger/audit/audit_logger_test.go b/core/logger/audit/audit_logger_test.go index eb3148d2b47..189112347f4 100644 --- a/core/logger/audit/audit_logger_test.go +++ b/core/logger/audit/audit_logger_test.go @@ -1,6 +1,7 @@ package audit_test import ( + "encoding/json" "flag" "io" "net/http" @@ -26,6 +27,16 @@ type MockHTTPClient struct { loggingChannel chan MockedHTTPEvent } +type LoginData struct { + Email string `json:"email"` +} + +type LoginLogItem struct { + EventID string `json:"eventID"` + Env string `json:"env"` + Data LoginData `json:"data"` +} + func (mock *MockHTTPClient) Do(req *http.Request) (*http.Response, error) { b, err := io.ReadAll(req.Body) @@ -65,26 +76,25 @@ func TestCheckLoginAuditLog(t *testing.T) { loggingChannel: loggingChannel, } - // Set an environment variable to trick the system into starting the audit logger - // and adding it to the subsystems. We are going to swap it out later so it's not - // going to matter what we put here - // os.Setenv("AUDIT_LOGGER_FORWARD_TO_URL", "http://test.local:9999") - + // Create a test logger because the audit logger relies on this logger + // as well logger := logger.TestLogger(t) // Create new AuditLoggerService auditLogger, err := audit.NewAuditLogger(logger, getAuditLoggerConfig()) assert.NoError(t, err) + // Cast to concrete type so we can swap out the internals auditLoggerService, ok := auditLogger.(*audit.AuditLoggerService) assert.True(t, ok) + // Swap the internals with a testing handler auditLoggerService.SetLoggingClient(&mockHTTPClient) - assert.NoError(t, auditLoggerService.Ready()) + // Create a new chainlink test application passing in our test logger + // and audit logger app := cltest.NewApplication(t, logger, auditLogger) - require.NoError(t, app.Start(testutils.Context(t))) enteredStrings := []string{cltest.APIEmailAdmin, cltest.Password} @@ -96,13 +106,21 @@ func TestCheckLoginAuditLog(t *testing.T) { set.String("admin-credentials-file", "", "") c := cli.NewContext(nil, set, nil) + // Login err = client.RemoteLogin(c) assert.NoError(t, err) select { - case _ = <-loggingChannel: + case event := <-loggingChannel: + deserialized := &LoginLogItem{} + assert.NoError(t, json.Unmarshal([]byte(event.body), deserialized)) + + assert.Equal(t, deserialized.Data.Email, cltest.APIEmailAdmin) + assert.Equal(t, deserialized.Env, "test") + + assert.Equal(t, deserialized.EventID, "AUTH_LOGIN_SUCCESS_NO_2FA") return - case <-time.After(15 * time.Second): + case <-time.After(5 * time.Second): } assert.True(t, false) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 1939d2604b7..428399f7fc4 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -131,7 +131,7 @@ type ChainlinkApplication struct { SessionReaper utils.SleeperTask shutdownOnce sync.Once explorerClient synchronization.ExplorerClient - Subservices []services.ServiceCtx + subservices []services.ServiceCtx HealthChecker services.Checker Nurse *services.Nurse logger logger.Logger @@ -191,7 +191,7 @@ func (c *Chains) services() (s []services.ServiceCtx) { // be used by the node. // TODO: Inject more dependencies here to save booting up useless stuff in tests func NewApplication(opts ApplicationOpts) (Application, error) { - var Subservices []services.ServiceCtx + var subservices []services.ServiceCtx auditLogger := opts.AuditLogger cfg := opts.Config chains := opts.Chains @@ -205,7 +205,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { // If the audit logger is enabled if auditLogger.Ready() == nil { - Subservices = append(Subservices, auditLogger) + subservices = append(subservices, auditLogger) } var profiler *pyroscope.Profiler @@ -261,7 +261,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { monitoringEndpointGen = telemetry.NewIngressAgentWrapper(telemetryIngressClient) } } - Subservices = append(Subservices, explorerClient, telemetryIngressClient, telemetryIngressBatchClient) + subservices = append(subservices, explorerClient, telemetryIngressClient, telemetryIngressBatchClient) if cfg.DatabaseBackupMode() != config.DatabaseBackupModeNone && cfg.DatabaseBackupFrequency() > 0 { globalLogger.Infow("DatabaseBackup: periodic database backups are enabled", "frequency", cfg.DatabaseBackupFrequency()) @@ -270,15 +270,15 @@ func NewApplication(opts ApplicationOpts) (Application, error) { if err != nil { return nil, errors.Wrap(err, "NewApplication: failed to initialize database backup") } - Subservices = append(Subservices, databaseBackup) + subservices = append(subservices, databaseBackup) } else { globalLogger.Info("DatabaseBackup: periodic database backups are disabled. To enable automatic backups, set DATABASE_BACKUP_MODE=lite or DATABASE_BACKUP_MODE=full") } - Subservices = append(Subservices, eventBroadcaster) - Subservices = append(Subservices, chains.services()...) + subservices = append(subservices, eventBroadcaster) + subservices = append(subservices, chains.services()...) promReporter := promreporter.NewPromReporter(db.DB, globalLogger) - Subservices = append(Subservices, promReporter) + subservices = append(subservices, promReporter) var ( pipelineORM = pipeline.NewORM(db, globalLogger, cfg) @@ -351,7 +351,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { return nil, err } peerWrapper = ocrcommon.NewSingletonPeerWrapper(keyStore, cfg, db, globalLogger) - Subservices = append(Subservices, peerWrapper) + subservices = append(subservices, peerWrapper) } else { globalLogger.Debug("P2P stack disabled") } @@ -377,22 +377,22 @@ func NewApplication(opts ApplicationOpts) (Application, error) { if cfg.EVMEnabled() { evmRelayer := evmrelay.NewRelayer(db, chains.EVM, globalLogger.Named("EVM")) relayers[relay.EVM] = evmRelayer - Subservices = append(Subservices, evmRelayer) + subservices = append(subservices, evmRelayer) } if cfg.SolanaEnabled() { solanaRelayer := pkgsolana.NewRelayer(globalLogger.Named("Solana.Relayer"), chains.Solana) relayers[relay.Solana] = solanaRelayer - Subservices = append(Subservices, solanaRelayer) + subservices = append(subservices, solanaRelayer) } if cfg.TerraEnabled() { terraRelayer := pkgterra.NewRelayer(globalLogger.Named("Terra.Relayer"), chains.Terra) relayers[relay.Terra] = terraRelayer - Subservices = append(Subservices, terraRelayer) + subservices = append(subservices, terraRelayer) } if cfg.StarkNetEnabled() { starknetRelayer := starknetrelay.NewRelayer(globalLogger.Named("StarkNet.Relayer"), chains.StarkNet) relayers[relay.StarkNet] = starknetRelayer - Subservices = append(Subservices, starknetRelayer) + subservices = append(subservices, starknetRelayer) } delegates[job.OffchainReporting2] = ocr2.NewDelegate( db, @@ -425,13 +425,13 @@ func NewApplication(opts ApplicationOpts) (Application, error) { lbs = append(lbs, c.LogBroadcaster()) } jobSpawner := job.NewSpawner(jobORM, cfg, delegates, db, globalLogger, lbs) - Subservices = append(Subservices, jobSpawner, pipelineRunner) + subservices = append(subservices, jobSpawner, pipelineRunner) // We start the log poller after the job spawner // so jobs have a chance to apply their initial log filters. if cfg.FeatureLogPoller() { for _, c := range chains.EVM.Chains() { - Subservices = append(Subservices, c.LogPoller()) + subservices = append(subservices, c.LogPoller()) } } @@ -478,12 +478,12 @@ func NewApplication(opts ApplicationOpts) (Application, error) { sqlxDB: opts.SqlxDB, - // NOTE: Can keep things clean by putting more things in Subservices + // NOTE: Can keep things clean by putting more things in subservices // instead of manually start/closing - Subservices: Subservices, + subservices: subservices, } - for _, service := range app.Subservices { + for _, service := range app.subservices { checkable := service.(services.Checkable) if err := app.HealthChecker.Register(reflect.TypeOf(service).String(), checkable); err != nil { return nil, err @@ -516,7 +516,7 @@ func (app *ChainlinkApplication) Start(ctx context.Context) error { } } - for _, subservice := range app.Subservices { + for _, subservice := range app.subservices { if ctx.Err() != nil { return errors.Wrap(ctx.Err(), "aborting start") } @@ -569,8 +569,8 @@ func (app *ChainlinkApplication) stop() (err error) { app.logger.Info("Gracefully exiting...") // Stop services in the reverse order from which they were started - for i := len(app.Subservices) - 1; i >= 0; i-- { - service := app.Subservices[i] + for i := len(app.subservices) - 1; i >= 0; i-- { + service := app.subservices[i] app.logger.Debugw("Closing service...", "serviceType", reflect.TypeOf(service)) err = multierr.Append(err, service.Close()) } From fe32a2dfa1149ee79aaf188fbaa8c3a274098b9c Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Mon, 19 Sep 2022 19:16:16 -0700 Subject: [PATCH 47/92] Address typo --- core/logger/audit/audit_logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 20399173e93..15b2c0ff043 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -279,7 +279,7 @@ func (l *AuditLoggerService) postLogToLogService(eventID EventID, data Data) { } if resp.StatusCode != 200 { if resp.Body == nil { - l.logger.Errorw("There was no body to read. Possibly an error occured sending", "logItem", logItem) + l.logger.Errorw("There was no body to read. Possibly an error occurred sending", "logItem", logItem) return } From f269bc47a66d7a3a8226e03b682f2383a260a030 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Mon, 19 Sep 2022 19:27:46 -0700 Subject: [PATCH 48/92] FIx io/ioutil --- core/logger/audit/audit_logger.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 15b2c0ff043..001a128ee40 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "encoding/json" - "io/ioutil" + "io" "net" "net/http" "os" @@ -283,7 +283,7 @@ func (l *AuditLoggerService) postLogToLogService(eventID EventID, data Data) { return } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { l.logger.Errorw("Error reading errored HTTP log service webhook response body", "err", err, "logItem", logItem) return From 2244bf299df92414cc9f9869d4278e61bb4aed6f Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Mon, 19 Sep 2022 19:58:43 -0700 Subject: [PATCH 49/92] Fix test crashes --- core/internal/cltest/cltest.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 5a920efc34a..c30de52ecc9 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -342,6 +342,10 @@ func NewApplicationWithConfig(t testing.TB, cfg *configtest.TestGeneralConfig, f } } + if auditLogger == nil { + auditLogger = &audit.AuditLoggerService{} + } + var eventBroadcaster pg.EventBroadcaster = pg.NewNullEventBroadcaster() url := cfg.DatabaseURL() From 3b78b2247606beb46829f9bc797f86e331f2549b Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Mon, 19 Sep 2022 20:44:51 -0700 Subject: [PATCH 50/92] Address comments --- core/web/chains_controller.go | 3 ++- core/web/nodes_controller.go | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/web/chains_controller.go b/core/web/chains_controller.go index 9feec8ed21d..c845fe01ddd 100644 --- a/core/web/chains_controller.go +++ b/core/web/chains_controller.go @@ -171,8 +171,9 @@ func (cc *chainsController[I, C, R]) Update(c *gin.Context) { chainj, err := json.Marshal(chain) if err != nil { cc.lggr.Errorf("Unable to marshal chain to json", "err", err) + } else { + cc.auditLogger.Audit(audit.ChainSpecUpdated, map[string]interface{}{"chain": chainj}) } - cc.auditLogger.Audit(audit.ChainSpecUpdated, map[string]interface{}{"chain": chainj}) jsonAPIResponse(c, cc.newResource(chain), cc.resourceName) } diff --git a/core/web/nodes_controller.go b/core/web/nodes_controller.go index 8a96a78ca05..cd4828660ec 100644 --- a/core/web/nodes_controller.go +++ b/core/web/nodes_controller.go @@ -46,7 +46,6 @@ func newNodesController[I chains.ID, N chains.Node, R jsonapi.EntityNamer]( parseChainID: parseChainID, newResource: newResource, createNode: createNode, - lggr: lggr, auditLogger: auditLogger, } } From 73ced32e16805e229de7609b733e971f85688856 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Mon, 19 Sep 2022 21:08:46 -0700 Subject: [PATCH 51/92] Address comments --- core/web/nodes_controller.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/web/nodes_controller.go b/core/web/nodes_controller.go index cd4828660ec..8607db94b4d 100644 --- a/core/web/nodes_controller.go +++ b/core/web/nodes_controller.go @@ -27,7 +27,6 @@ type nodesController[I chains.ID, N chains.Node, R jsonapi.EntityNamer] struct { errNotEnabled error newResource func(N) R createNode func(*gin.Context) (N, error) - lggr logger.Logger auditLogger audit.AuditLogger } From d0a76f9b17cd9ff7fbb2e46b77e48ab5bf542bbc Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Tue, 20 Sep 2022 19:00:31 -0700 Subject: [PATCH 52/92] Move to new validate config system --- core/cmd/client.go | 2 +- core/config/general_config.go | 23 +----- core/config/v2/types.go | 2 +- core/logger/audit/audit_logger.go | 89 ++++++++++++++++------- core/logger/audit/audit_logger_test.go | 4 +- core/services/chainlink/config_general.go | 2 +- core/services/chainlink/config_test.go | 2 +- docs/CHANGELOG.md | 2 +- 8 files changed, 72 insertions(+), 54 deletions(-) diff --git a/core/cmd/client.go b/core/cmd/client.go index 0c835484806..6c2b6d7a2e8 100644 --- a/core/cmd/client.go +++ b/core/cmd/client.go @@ -221,7 +221,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg config.Gene // Configure and optionally start the audit log forwarder service auditLogger, err := audit.NewAuditLogger(appLggr, cfg.AuditLoggerConfig()) if err != nil { - appLggr.Info("Audit logger could not be started. err: ", err) + return nil, err } restrictedClient := clhttp.NewRestrictedHTTPClient(cfg, appLggr) diff --git a/core/config/general_config.go b/core/config/general_config.go index 9a56acd4a0f..c7f2d52ce93 100644 --- a/core/config/general_config.go +++ b/core/config/general_config.go @@ -75,7 +75,7 @@ type GeneralOnlyConfig interface { AdvisoryLockID() int64 AllowOrigins() string AppID() uuid.UUID - AuditLoggerConfig() *audit.AuditLoggerConfig + AuditLoggerConfig() audit.AuditLoggerConfig AuthenticatedRateLimit() int64 AuthenticatedRateLimitPeriod() models.Duration AutoPprofBlockProfileRate() int @@ -488,28 +488,13 @@ func (c *generalConfig) AppID() uuid.UUID { // Create the audit logger configuration to send events to an // external service -func (c *generalConfig) AuditLoggerConfig() *audit.AuditLoggerConfig { - forwardToUrl := c.viper.GetString(envvar.Name("AuditLoggerForwardToUrl")) - // We have this check here to determine if we should enable the audit logger - // at all. If this is not set, then we don't need to output the error below - // when configuration fails. - if forwardToUrl == "" { - return nil - } - - auditLoggerConfig, err := audit.NewAuditLoggerConfig( - forwardToUrl, +func (c *generalConfig) AuditLoggerConfig() audit.AuditLoggerConfig { + return audit.NewAuditLoggerConfig( + c.viper.GetString(envvar.Name("AuditLoggerForwardToUrl")), c.viper.GetBool(envvar.Name("Dev")), c.viper.GetString(envvar.Name("AuditLoggerJsonWrapperKey")), c.viper.GetString(envvar.Name("AuditLoggerHeaders")), ) - - if err != nil { - c.lggr.Errorf("Audit logger configuration failed with: %s", err) - return nil - } - - return &auditLoggerConfig } // AuthenticatedRateLimit defines the threshold to which authenticated requests diff --git a/core/config/v2/types.go b/core/config/v2/types.go index 5f106e29d75..8f847bb21da 100644 --- a/core/config/v2/types.go +++ b/core/config/v2/types.go @@ -31,7 +31,7 @@ type Core struct { TelemetryIngress *TelemetryIngress - AuditLogger *audit.AuditLoggerConfig + AuditLogger audit.AuditLoggerConfig Log *Log WebServer *WebServer diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 001a128ee40..4b51914f18e 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -29,39 +29,66 @@ type AuditLogger interface { Audit(eventID EventID, data Data) } +type validatedAuditLoggerConfig struct { + forwardToUrl string + environment string + jsonWrapperKey string + headers []ServiceHeader +} + type AuditLoggerConfig struct { ForwardToUrl *string Environment *string JsonWrapperKey *string - Headers []ServiceHeader + Headers *string } type HTTPAuditLoggerInterface interface { Do(req *http.Request) (*http.Response, error) } -func NewAuditLoggerConfig(forwardToUrl string, isDev bool, jsonWrapperKey string, encodedHeaders string) (AuditLoggerConfig, error) { - if forwardToUrl == "" { - return AuditLoggerConfig{}, errors.Errorf("No forwardToURL provided") +// / Wrap all the items into a struct to easily pass them around. This does not +// / do any validation on the values given +func NewAuditLoggerConfig(forwardToUrl string, isDev bool, jsonWrapperKey string, headers string) AuditLoggerConfig { + environment := "production" + if isDev { + environment = "develop" } - if _, err := models.ParseURL(forwardToUrl); err != nil { - return AuditLoggerConfig{}, errors.Errorf("forwardToURL value is not a valid URL") + return AuditLoggerConfig{ + ForwardToUrl: &forwardToUrl, + Environment: &environment, + JsonWrapperKey: &jsonWrapperKey, + Headers: &headers, } +} - environment := "production" - if isDev { - environment = "develop" +// / Internal function to validate the values sent in to the AuditLogger +// / +// / Checks all sorts of things to eliminate nil pointers and invalid endpoints +// / as well as parsing out the headers into format that's easier to use later. +func validateConfig(alc AuditLoggerConfig) (validatedAuditLoggerConfig, error) { + if alc.ForwardToUrl == nil || alc.Environment == nil || alc.JsonWrapperKey == nil || alc.Headers == nil { + return validatedAuditLoggerConfig{}, errors.New("Audit configuration error: fields were nil and shouldn't have been.") + } + // We treat this as the audit logger not being configured so it is not an + // error + if *alc.ForwardToUrl == "" { + return validatedAuditLoggerConfig{}, nil + } + + if _, err := models.ParseURL(*alc.ForwardToUrl); err != nil { + return validatedAuditLoggerConfig{}, errors.New("forwardToURL value is not a valid URL") } // Split and prepare optional service client headers from env variable headers := []ServiceHeader{} - if encodedHeaders != "" { - headerLines := strings.Split(encodedHeaders, "\\") + if *alc.Headers != "" { + headerLines := strings.Split(*alc.Headers, "\\") for _, header := range headerLines { keyValue := strings.Split(header, "||") if len(keyValue) != 2 { - return AuditLoggerConfig{}, errors.Errorf("Invalid headers provided for the audit logger. Value, single pair split on || required, got: %s", keyValue) + return validatedAuditLoggerConfig{}, errors.Errorf("Invalid headers provided for the audit logger. Value, single pair split on || required, got: %s", keyValue) } headers = append(headers, ServiceHeader{ Header: keyValue[0], @@ -69,12 +96,11 @@ func NewAuditLoggerConfig(forwardToUrl string, isDev bool, jsonWrapperKey string }) } } - - return AuditLoggerConfig{ - ForwardToUrl: &forwardToUrl, - Environment: &environment, - JsonWrapperKey: &jsonWrapperKey, - Headers: headers, + return validatedAuditLoggerConfig{ + forwardToUrl: *alc.ForwardToUrl, + environment: *alc.Environment, + jsonWrapperKey: *alc.JsonWrapperKey, + headers: headers, }, nil } @@ -111,14 +137,22 @@ type wrappedAuditLog struct { // 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 *AuditLoggerConfig) (AuditLogger, error) { - if config == nil { - return &AuditLoggerService{}, errors.Errorf("Audit Log initialization error - no configuration") +func NewAuditLogger(logger logger.Logger, unverifiedConfig AuditLoggerConfig) (AuditLogger, error) { + config, err := validateConfig(unverifiedConfig) + + if err != nil { + return &AuditLoggerService{}, err + } + + // If there was no error and this is empty string then the logger + // is not configured + if config.forwardToUrl == "" { + return &AuditLoggerService{}, nil } hostname, err := os.Hostname() if err != nil { - return &AuditLoggerService{}, errors.Errorf("Audit Log initialization error - unable to get hostname: %s", err) + return nil, errors.Errorf("Audit Log initialization error - unable to get hostname: %s", err) } ctx, cancel := context.WithCancel(context.Background()) @@ -129,10 +163,10 @@ func NewAuditLogger(logger logger.Logger, config *AuditLoggerConfig) (AuditLogge auditLogger := AuditLoggerService{ logger: logger.Helper(1), enabled: true, - forwardToUrl: *config.ForwardToUrl, - headers: config.Headers, - jsonWrapperKey: *config.JsonWrapperKey, - environmentName: *config.Environment, + forwardToUrl: config.forwardToUrl, + headers: config.headers, + jsonWrapperKey: config.jsonWrapperKey, + environmentName: config.environment, hostname: hostname, localIP: getLocalIP(), loggingClient: &http.Client{Timeout: time.Second * webRequestTimeout}, @@ -228,8 +262,7 @@ func (l *AuditLoggerService) runLoop() { for { select { case <-l.ctx.Done(): - // I've made this an error since we expect it should never happen. - l.logger.Errorf("The audit logger has been requested to shut down!") + l.logger.Warn("The audit logger is shutting down") return case event := <-l.loggingChannel: l.postLogToLogService(event.eventID, event.data) diff --git a/core/logger/audit/audit_logger_test.go b/core/logger/audit/audit_logger_test.go index 189112347f4..e07508a7b4a 100644 --- a/core/logger/audit/audit_logger_test.go +++ b/core/logger/audit/audit_logger_test.go @@ -53,12 +53,12 @@ func (mock *MockHTTPClient) Do(req *http.Request) (*http.Response, error) { return &http.Response{}, nil } -func getAuditLoggerConfig() *audit.AuditLoggerConfig { +func getAuditLoggerConfig() audit.AuditLoggerConfig { forwardToUrl := "empty" environment := "test" jsonWrapperKey := "" - return &audit.AuditLoggerConfig{ + return audit.AuditLoggerConfig{ ForwardToUrl: &forwardToUrl, Environment: &environment, JsonWrapperKey: &jsonWrapperKey, diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index cf560bea724..191167b11b3 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -167,7 +167,7 @@ func (g *generalConfig) AllowOrigins() string { return *g.c.WebServer.AllowOrigins } -func (g *generalConfig) AuditLoggerConfig() *audit.AuditLoggerConfig { +func (g *generalConfig) AuditLoggerConfig() audit.AuditLoggerConfig { return g.c.AuditLogger } diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 46666e3f020..23fe0a1f729 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -206,7 +206,7 @@ func TestConfig_Marshal(t *testing.T) { full := global - full.AuditLogger = &audit.AuditLoggerConfig{ + full.AuditLogger = audit.AuditLoggerConfig{ ForwardToUrl: ptr("http://localhost:9898"), Environment: ptr("develop"), Headers: make([]audit.ServiceHeader, 0), diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 5d55c3397c9..e54b3450471 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -26,7 +26,7 @@ as an estimated gas limit (up to `ETH_GAS_LIMIT_MAX`, with `1,000,000,000` defau - Default: _none_ - When set, this environment variable configures and enables an optional HTTP logger which is used specifcally to send audit log events. Audit logs events are emitted when specific actions are performed by any of the users through the node's API. The value of this variable should be a full URL. Log items will be sent via POST + When set, this environment variable configures and enables an optional HTTP logger which is used specifically to send audit log events. Audit logs events are emitted when specific actions are performed by any of the users through the node's API. The value of this variable should be a full URL. Log items will be sent via POST There are audit log implemented for the following events: - Auth & Sessions (new session, login success, login failed, 2FA enrolled, 2FA failed, password reset, password reset failed, etc.) From e02f811fb13a8569108bcf3f14245593b3c2ef4d Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Tue, 20 Sep 2022 19:15:58 -0700 Subject: [PATCH 53/92] NewRequestWithContext --- core/logger/audit/audit_logger.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 4b51914f18e..272665c6629 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -215,6 +215,7 @@ func (l *AuditLoggerService) Start(context.Context) error { if !l.enabled { return errors.Errorf("The audit logger is not enabled") } + go l.runLoop() return nil } @@ -298,7 +299,7 @@ func (l *AuditLoggerService) postLogToLogService(eventID EventID, data Data) { } // Send to remote service - req, err := http.NewRequest("POST", l.forwardToUrl, bytes.NewReader(serializedLog)) + req, err := http.NewRequestWithContext(l.ctx, "POST", l.forwardToUrl, bytes.NewReader(serializedLog)) if err != nil { l.logger.Errorf("Failed to create request to remote logging service!") } From aa43056c3611dea5e7e1a86f27fdc94ec8b76c12 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Wed, 21 Sep 2022 01:45:34 -0700 Subject: [PATCH 54/92] Fix tests --- core/config/mocks/general_config.go | 38 ++++++++++++-------------- core/services/chainlink/config_test.go | 2 +- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/core/config/mocks/general_config.go b/core/config/mocks/general_config.go index dda8050c399..7e559387852 100644 --- a/core/config/mocks/general_config.go +++ b/core/config/mocks/general_config.go @@ -103,16 +103,14 @@ func (_m *GeneralConfig) AppID() uuid.UUID { } // AuditLoggerConfig provides a mock function with given fields: -func (_m *GeneralConfig) AuditLoggerConfig() *audit.AuditLoggerConfig { +func (_m *GeneralConfig) AuditLoggerConfig() audit.AuditLoggerConfig { ret := _m.Called() - var r0 *audit.AuditLoggerConfig - if rf, ok := ret.Get(0).(func() *audit.AuditLoggerConfig); ok { + var r0 audit.AuditLoggerConfig + if rf, ok := ret.Get(0).(func() audit.AuditLoggerConfig); ok { r0 = rf() } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*audit.AuditLoggerConfig) - } + r0 = ret.Get(0).(audit.AuditLoggerConfig) } return r0 @@ -2232,8 +2230,8 @@ func (_m *GeneralConfig) KeeperRegistryCheckGasOverhead() uint32 { return r0 } -// KeeperRegistryPerformGasOverhead provides a mock function with given fields: -func (_m *GeneralConfig) KeeperRegistryPerformGasOverhead() uint32 { +// KeeperRegistryMaxPerformDataSize provides a mock function with given fields: +func (_m *GeneralConfig) KeeperRegistryMaxPerformDataSize() uint32 { ret := _m.Called() var r0 uint32 @@ -2246,36 +2244,36 @@ func (_m *GeneralConfig) KeeperRegistryPerformGasOverhead() uint32 { return r0 } -// KeeperRegistrySyncInterval provides a mock function with given fields: -func (_m *GeneralConfig) KeeperRegistrySyncInterval() time.Duration { +// KeeperRegistryPerformGasOverhead provides a mock function with given fields: +func (_m *GeneralConfig) KeeperRegistryPerformGasOverhead() uint32 { ret := _m.Called() - var r0 time.Duration - if rf, ok := ret.Get(0).(func() time.Duration); ok { + var r0 uint32 + if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() } else { - r0 = ret.Get(0).(time.Duration) + r0 = ret.Get(0).(uint32) } return r0 } -// KeeperRegistrySyncUpkeepQueueSize provides a mock function with given fields: -func (_m *GeneralConfig) KeeperRegistrySyncUpkeepQueueSize() uint32 { +// KeeperRegistrySyncInterval provides a mock function with given fields: +func (_m *GeneralConfig) KeeperRegistrySyncInterval() time.Duration { ret := _m.Called() - var r0 uint32 - if rf, ok := ret.Get(0).(func() uint32); ok { + var r0 time.Duration + if rf, ok := ret.Get(0).(func() time.Duration); ok { r0 = rf() } else { - r0 = ret.Get(0).(uint32) + r0 = ret.Get(0).(time.Duration) } return r0 } -// KeeperRegistryMaxPerformDataSize provides a mock function with given fields: -func (_m *GeneralConfig) KeeperRegistryMaxPerformDataSize() uint32 { +// KeeperRegistrySyncUpkeepQueueSize provides a mock function with given fields: +func (_m *GeneralConfig) KeeperRegistrySyncUpkeepQueueSize() uint32 { ret := _m.Called() var r0 uint32 diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 23fe0a1f729..1ffa2253b26 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -209,7 +209,7 @@ func TestConfig_Marshal(t *testing.T) { full.AuditLogger = audit.AuditLoggerConfig{ ForwardToUrl: ptr("http://localhost:9898"), Environment: ptr("develop"), - Headers: make([]audit.ServiceHeader, 0), + Headers: ptr(""), JsonWrapperKey: ptr(""), } From df73757d0a331ade72afb3e3d7e19f428deba0fb Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Wed, 21 Sep 2022 01:56:01 -0700 Subject: [PATCH 55/92] Fix punctuation --- core/logger/audit/audit_logger.go | 32 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 272665c6629..fe88d7b8ba3 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -69,7 +69,7 @@ func NewAuditLoggerConfig(forwardToUrl string, isDev bool, jsonWrapperKey string // / as well as parsing out the headers into format that's easier to use later. func validateConfig(alc AuditLoggerConfig) (validatedAuditLoggerConfig, error) { if alc.ForwardToUrl == nil || alc.Environment == nil || alc.JsonWrapperKey == nil || alc.Headers == nil { - return validatedAuditLoggerConfig{}, errors.New("Audit configuration error: fields were nil and shouldn't have been.") + return validatedAuditLoggerConfig{}, errors.New("configuration error: fields were nil and shouldn't have been") } // We treat this as the audit logger not being configured so it is not an // error @@ -88,7 +88,7 @@ func validateConfig(alc AuditLoggerConfig) (validatedAuditLoggerConfig, error) { for _, header := range headerLines { keyValue := strings.Split(header, "||") if len(keyValue) != 2 { - return validatedAuditLoggerConfig{}, errors.Errorf("Invalid headers provided for the audit logger. Value, single pair split on || required, got: %s", keyValue) + return validatedAuditLoggerConfig{}, errors.Errorf("invalid headers provided for the audit logger. Value, single pair split on || required, got: %s", keyValue) } headers = append(headers, ServiceHeader{ Header: keyValue[0], @@ -152,7 +152,7 @@ func NewAuditLogger(logger logger.Logger, unverifiedConfig AuditLoggerConfig) (A hostname, err := os.Hostname() if err != nil { - return nil, errors.Errorf("Audit Log initialization error - unable to get hostname: %s", err) + return nil, errors.Errorf("initialization error - unable to get hostname: %s", err) } ctx, cancel := context.WithCancel(context.Background()) @@ -203,9 +203,9 @@ func (l *AuditLoggerService) Audit(eventID EventID, data Data) { case l.loggingChannel <- wrappedLog: default: if l.loggingChannel == nil { - l.logger.Errorw("Could not send log to audit subsystem because it has gone away!") + l.logger.Errorw("could not send log to audit subsystem because it has gone away!") } else { - l.logger.Errorw("Audit log buffer is full. Dropping log with eventID: %s", eventID) + l.logger.Errorf("buffer is full. Dropping log with eventID: %s", eventID) } } } @@ -213,7 +213,7 @@ func (l *AuditLoggerService) Audit(eventID EventID, data Data) { // Start the audit logger and begin processing logs on the channel func (l *AuditLoggerService) Start(context.Context) error { if !l.enabled { - return errors.Errorf("The audit logger is not enabled") + return errors.New("The audit logger is not enabled") } go l.runLoop() @@ -223,7 +223,7 @@ func (l *AuditLoggerService) Start(context.Context) error { // Stops the logger and will close the channel. func (l *AuditLoggerService) Close() error { if !l.enabled { - return errors.Errorf("The audit logger is not enabled") + return errors.New("The audit logger is not enabled") } l.logger.Warnf("Disabled the audit logger service") @@ -235,11 +235,11 @@ func (l *AuditLoggerService) Close() error { func (l *AuditLoggerService) Healthy() error { if !l.enabled { - return errors.Errorf("The audit logger is not enabled") + return errors.New("the audit logger is not enabled") } if len(l.loggingChannel) == bufferCapacity { - return errors.Errorf("The audit log buffer is full") + return errors.New("buffer is full") } return nil @@ -247,7 +247,7 @@ func (l *AuditLoggerService) Healthy() error { func (l *AuditLoggerService) Ready() error { if !l.enabled { - return errors.Errorf("The audit logger is not enabled") + return errors.New("the audit logger is not enabled") } return nil @@ -294,35 +294,35 @@ func (l *AuditLoggerService) postLogToLogService(eventID EventID, data Data) { serializedLog, err := json.Marshal(logItem) if err != nil { - l.logger.Errorw("Unable to serialize wrapped audit log item to JSON", "err", err, "logItem", logItem) + l.logger.Errorw("unable to serialize wrapped audit log item to JSON", "err", err, "logItem", logItem) return } // Send to remote service req, err := http.NewRequestWithContext(l.ctx, "POST", l.forwardToUrl, bytes.NewReader(serializedLog)) if err != nil { - l.logger.Errorf("Failed to create request to remote logging service!") + l.logger.Error("failed to create request to remote logging service!") } for _, header := range l.headers { req.Header.Add(header.Header, header.Value) } resp, err := l.loggingClient.Do(req) if err != nil { - l.logger.Errorw("Failed to send audit log to HTTP log service", "err", err, "logItem", logItem) + l.logger.Errorw("failed to send audit log to HTTP log service", "err", err, "logItem", logItem) return } if resp.StatusCode != 200 { if resp.Body == nil { - l.logger.Errorw("There was no body to read. Possibly an error occurred sending", "logItem", logItem) + l.logger.Errorw("no body to read. Possibly an error occurred sending", "logItem", logItem) return } bodyBytes, err := io.ReadAll(resp.Body) if err != nil { - l.logger.Errorw("Error reading errored HTTP log service webhook response body", "err", err, "logItem", logItem) + l.logger.Errorw("error reading errored HTTP log service webhook response body", "err", err, "logItem", logItem) return } - l.logger.Errorw("Error sending log to HTTP log service", "statusCode", resp.StatusCode, "bodyString", string(bodyBytes)) + l.logger.Errorw("error sending log to HTTP log service", "statusCode", resp.StatusCode, "bodyString", string(bodyBytes)) return } From 554c45dce59c9116b242c846ac8f992d8ac936d7 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Wed, 21 Sep 2022 02:18:07 -0700 Subject: [PATCH 56/92] Fix tests --- core/chains/evm/config/mocks/chain_scoped_config.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/core/chains/evm/config/mocks/chain_scoped_config.go b/core/chains/evm/config/mocks/chain_scoped_config.go index 7e0213a323b..77e703802d2 100644 --- a/core/chains/evm/config/mocks/chain_scoped_config.go +++ b/core/chains/evm/config/mocks/chain_scoped_config.go @@ -107,16 +107,14 @@ func (_m *ChainScopedConfig) AppID() uuid.UUID { } // AuditLoggerConfig provides a mock function with given fields: -func (_m *ChainScopedConfig) AuditLoggerConfig() *audit.AuditLoggerConfig { +func (_m *ChainScopedConfig) AuditLoggerConfig() audit.AuditLoggerConfig { ret := _m.Called() - var r0 *audit.AuditLoggerConfig - if rf, ok := ret.Get(0).(func() *audit.AuditLoggerConfig); ok { + var r0 audit.AuditLoggerConfig + if rf, ok := ret.Get(0).(func() audit.AuditLoggerConfig); ok { r0 = rf() } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*audit.AuditLoggerConfig) - } + r0 = ret.Get(0).(audit.AuditLoggerConfig) } return r0 From 09be73d2d0a94e9315a996ca4a107d8cd62ffd85 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Wed, 21 Sep 2022 02:38:44 -0700 Subject: [PATCH 57/92] Fix tests --- core/logger/audit/audit_logger_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/logger/audit/audit_logger_test.go b/core/logger/audit/audit_logger_test.go index e07508a7b4a..c8fe1063896 100644 --- a/core/logger/audit/audit_logger_test.go +++ b/core/logger/audit/audit_logger_test.go @@ -57,11 +57,13 @@ func getAuditLoggerConfig() audit.AuditLoggerConfig { forwardToUrl := "empty" environment := "test" jsonWrapperKey := "" + headers := "" return audit.AuditLoggerConfig{ ForwardToUrl: &forwardToUrl, Environment: &environment, JsonWrapperKey: &jsonWrapperKey, + Headers: &headers, } } From 3306e68524d6989d0724a1a10a5279d4c69d152b Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Wed, 21 Sep 2022 02:53:10 -0700 Subject: [PATCH 58/92] Update docs --- core/services/chainlink/testdata/config-full.toml | 2 +- docs/CONFIG.md | 6 +++--- internal/config/docs.toml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 4089e974253..48c45291e9e 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -46,7 +46,7 @@ UseBatchSend = true ForwardToUrl = 'http://localhost:9898' Environment = 'develop' JsonWrapperKey = '' -Headers = [] +Headers = '' [Log] DatabaseQueries = true diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 375d27a7436..651e03e4fb3 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -335,7 +335,7 @@ UseBatchSend toggles sending telemetry to the ingress server using the batch cli ForwardToUrl = 'http://localhost:9898' # Example Environment = 'develop' # Example JsonWrapperKey = '' # Default -Headers = [] # Default +Headers = '' # Default ``` @@ -359,9 +359,9 @@ JsonWrapperKey if set wraps the map of data under another single key to make par ### Headers ```toml -Headers = [] # Default +Headers = '' # Default ``` -Headers is the set of headers you wish to pass along with each request +Headers is the set of headers you wish to pass along with each request formatted as specified in the documentation ## Log ```toml diff --git a/internal/config/docs.toml b/internal/config/docs.toml index df22d7c0a6e..97f53774eca 100644 --- a/internal/config/docs.toml +++ b/internal/config/docs.toml @@ -112,8 +112,8 @@ ForwardToUrl = 'http://localhost:9898' # Example Environment = 'develop' # Example # JsonWrapperKey if set wraps the map of data under another single key to make parsing easier JsonWrapperKey = '' # Default -# Headers is the set of headers you wish to pass along with each request -Headers = [] # Default +# Headers is the set of headers you wish to pass along with each request formatted as specified in the documentation +Headers = '' # Default [Log] # DatabaseQueries tells the Chainlink node to log database queries made using the default logger. SQL statements will be logged at `debug` level. Not all statements can be logged. The best way to get a true log of all SQL statements is to enable SQL statement logging on Postgres. From 2c8dc1ef69342aecc2e784547e3b8dde1e96cb3f Mon Sep 17 00:00:00 2001 From: HenryNguyen5 <6404866+HenryNguyen5@users.noreply.github.com> Date: Mon, 16 May 2022 16:55:34 -0400 Subject: [PATCH 59/92] Remove non-root suffix to tags Since we default to non-root images now, the suffix has been removed for them. For root images, we publish a latest tag for root images with a -root suffix now. --- .github/actions/build-sign-publish-chainlink/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-sign-publish-chainlink/action.yml b/.github/actions/build-sign-publish-chainlink/action.yml index caf26db657c..d2174f4ad65 100644 --- a/.github/actions/build-sign-publish-chainlink/action.yml +++ b/.github/actions/build-sign-publish-chainlink/action.yml @@ -114,7 +114,7 @@ runs: uses: docker/metadata-action@e5622373a38e60fb6d795a4421e56882f2d7a681 # v3.6.2 with: flavor: | - suffix=-root + suffix=-root,onlatest=true # list of Docker images to use as base name for tags images: ${{ env.shared-images }} tags: ${{ env.shared-tag-list }} @@ -147,7 +147,7 @@ runs: flavor: | latest=auto prefix= - suffix=-nonroot,onlatest=true + suffix= images: ${{ env.shared-images }} tags: ${{ env.shared-tag-list }} From da65ec82fd84889e4f8749229d04d5f3be53baa9 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 21 Sep 2022 07:03:18 -0500 Subject: [PATCH 60/92] core/services: ServiceCtx docs (#7483) --- core/services/service.go | 164 +++++++++++++++++++-------------------- 1 file changed, 80 insertions(+), 84 deletions(-) diff --git a/core/services/service.go b/core/services/service.go index 462998a2745..9ca50716e4d 100644 --- a/core/services/service.go +++ b/core/services/service.go @@ -2,88 +2,84 @@ package services import "context" -type ( - // Service represents a long-running service inside the Application. - // - // Typically, a ServiceCtx will leverage utils.StartStopOnce to implement these - // calls in a safe manner. - // - // Template - // - // Mockable Foo service with a run loop - // //go:generate mockery --name Foo --output ../internal/mocks/ --case=underscore - // type ( - // // Expose a public interface so we can mock the service. - // Foo interface { - // service.ServiceCtx - // - // // ... - // } - // - // foo struct { - // // ... - // - // stop chan struct{} - // done chan struct{} - // - // utils.StartStopOnce - // } - // ) - // - // var _ Foo = (*foo)(nil) - // - // func NewFoo() Foo { - // f := &foo{ - // // ... - // } - // - // return f - // } - // - // func (f *foo) Start(ctx context.Context) error { - // return f.StartOnce("Foo", func() error { - // go f.run() - // - // return nil - // }) - // } - // - // func (f *foo) Close() error { - // return f.StopOnce("Foo", func() error { - // // trigger goroutine cleanup - // close(f.stop) - // // wait for cleanup to complete - // <-f.done - // return nil - // }) - // } - // - // func (f *foo) run() { - // // signal cleanup completion - // defer close(f.done) - // - // for { - // select { - // // ... - // case <-f.stop: - // // stop the routine - // return - // } - // } - // - // } +// ServiceCtx represents a long-running service inside the Application. +// +// Typically, a ServiceCtx will leverage utils.StartStopOnce to implement these +// calls in a safe manner. +// +// # Template +// +// Mockable Foo service with a run loop +// +// //go:generate mockery --name Foo --output ../internal/mocks/ --case=underscore +// type ( +// // Expose a public interface so we can mock the service. +// Foo interface { +// service.ServiceCtx +// +// // ... +// } +// +// foo struct { +// // ... +// +// stop chan struct{} +// done chan struct{} +// +// utils.StartStopOnce +// } +// ) +// +// var _ Foo = (*foo)(nil) +// +// func NewFoo() Foo { +// f := &foo{ +// // ... +// } +// +// return f +// } +// +// func (f *foo) Start(ctx context.Context) error { +// return f.StartOnce("Foo", func() error { +// go f.run() +// +// return nil +// }) +// } +// +// func (f *foo) Close() error { +// return f.StopOnce("Foo", func() error { +// // trigger goroutine cleanup +// close(f.stop) +// // wait for cleanup to complete +// <-f.done +// return nil +// }) +// } +// +// func (f *foo) run() { +// // signal cleanup completion +// defer close(f.done) +// +// for { +// select { +// // ... +// case <-f.stop: +// // stop the routine +// return +// } +// } +// +// } +type ServiceCtx interface { + // Start the service. Must quit immediately if the context is cancelled. + // The given context applies to Start function only and must not be retained. + Start(context.Context) error + // Close stops the Service. + // Invariants: Usually after this call the Service cannot be started + // again, you need to build a new Service to do so. + Close() error - // ServiceCtx is a former Service interface, that changed Start function to receive a context. - // This is needed for services that make HTTP calls or DB queries in Start. - ServiceCtx interface { - // Start the service. Must quit immediately if the context is cancelled. - // The given context applies to Start function only and must not be retained. - Start(context.Context) error - // Close stops the Service. - // Invariants: Usually after this call the Service cannot be started - // again, you need to build a new Service to do so. - Close() error - - Checkable - } -) + Checkable +} From a48363ce1ee5caab1f02159fa20cee3774959b48 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 21 Sep 2022 15:06:37 -0500 Subject: [PATCH 61/92] core/services/chainlink: clean up service naming (#7487) --- core/services/chainlink/application.go | 52 +++++++++++++------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 428399f7fc4..d1f8c2f9192 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -16,10 +16,11 @@ import ( "go.uber.org/multierr" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/sqlx" + pkgsolana "github.com/smartcontractkit/chainlink-solana/pkg/solana" starknetrelay "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink" pkgterra "github.com/smartcontractkit/chainlink-terra/pkg/terra" - "github.com/smartcontractkit/sqlx" relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" @@ -131,7 +132,7 @@ type ChainlinkApplication struct { SessionReaper utils.SleeperTask shutdownOnce sync.Once explorerClient synchronization.ExplorerClient - subservices []services.ServiceCtx + srvcs []services.ServiceCtx HealthChecker services.Checker Nurse *services.Nurse logger logger.Logger @@ -191,11 +192,11 @@ func (c *Chains) services() (s []services.ServiceCtx) { // be used by the node. // TODO: Inject more dependencies here to save booting up useless stuff in tests func NewApplication(opts ApplicationOpts) (Application, error) { - var subservices []services.ServiceCtx + var srvcs []services.ServiceCtx auditLogger := opts.AuditLogger + db := opts.SqlxDB cfg := opts.Config chains := opts.Chains - db := opts.SqlxDB eventBroadcaster := opts.EventBroadcaster externalInitiatorManager := opts.ExternalInitiatorManager globalLogger := opts.Logger @@ -205,7 +206,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { // If the audit logger is enabled if auditLogger.Ready() == nil { - subservices = append(subservices, auditLogger) + srvcs = append(srvcs, auditLogger) } var profiler *pyroscope.Profiler @@ -261,7 +262,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { monitoringEndpointGen = telemetry.NewIngressAgentWrapper(telemetryIngressClient) } } - subservices = append(subservices, explorerClient, telemetryIngressClient, telemetryIngressBatchClient) + srvcs = append(srvcs, explorerClient, telemetryIngressClient, telemetryIngressBatchClient) if cfg.DatabaseBackupMode() != config.DatabaseBackupModeNone && cfg.DatabaseBackupFrequency() > 0 { globalLogger.Infow("DatabaseBackup: periodic database backups are enabled", "frequency", cfg.DatabaseBackupFrequency()) @@ -270,15 +271,15 @@ func NewApplication(opts ApplicationOpts) (Application, error) { if err != nil { return nil, errors.Wrap(err, "NewApplication: failed to initialize database backup") } - subservices = append(subservices, databaseBackup) + srvcs = append(srvcs, databaseBackup) } else { globalLogger.Info("DatabaseBackup: periodic database backups are disabled. To enable automatic backups, set DATABASE_BACKUP_MODE=lite or DATABASE_BACKUP_MODE=full") } - subservices = append(subservices, eventBroadcaster) - subservices = append(subservices, chains.services()...) + srvcs = append(srvcs, eventBroadcaster) + srvcs = append(srvcs, chains.services()...) promReporter := promreporter.NewPromReporter(db.DB, globalLogger) - subservices = append(subservices, promReporter) + srvcs = append(srvcs, promReporter) var ( pipelineORM = pipeline.NewORM(db, globalLogger, cfg) @@ -351,7 +352,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { return nil, err } peerWrapper = ocrcommon.NewSingletonPeerWrapper(keyStore, cfg, db, globalLogger) - subservices = append(subservices, peerWrapper) + srvcs = append(srvcs, peerWrapper) } else { globalLogger.Debug("P2P stack disabled") } @@ -377,22 +378,22 @@ func NewApplication(opts ApplicationOpts) (Application, error) { if cfg.EVMEnabled() { evmRelayer := evmrelay.NewRelayer(db, chains.EVM, globalLogger.Named("EVM")) relayers[relay.EVM] = evmRelayer - subservices = append(subservices, evmRelayer) + srvcs = append(srvcs, evmRelayer) } if cfg.SolanaEnabled() { solanaRelayer := pkgsolana.NewRelayer(globalLogger.Named("Solana.Relayer"), chains.Solana) relayers[relay.Solana] = solanaRelayer - subservices = append(subservices, solanaRelayer) + srvcs = append(srvcs, solanaRelayer) } if cfg.TerraEnabled() { terraRelayer := pkgterra.NewRelayer(globalLogger.Named("Terra.Relayer"), chains.Terra) relayers[relay.Terra] = terraRelayer - subservices = append(subservices, terraRelayer) + srvcs = append(srvcs, terraRelayer) } if cfg.StarkNetEnabled() { starknetRelayer := starknetrelay.NewRelayer(globalLogger.Named("StarkNet.Relayer"), chains.StarkNet) relayers[relay.StarkNet] = starknetRelayer - subservices = append(subservices, starknetRelayer) + srvcs = append(srvcs, starknetRelayer) } delegates[job.OffchainReporting2] = ocr2.NewDelegate( db, @@ -425,13 +426,13 @@ func NewApplication(opts ApplicationOpts) (Application, error) { lbs = append(lbs, c.LogBroadcaster()) } jobSpawner := job.NewSpawner(jobORM, cfg, delegates, db, globalLogger, lbs) - subservices = append(subservices, jobSpawner, pipelineRunner) + srvcs = append(srvcs, jobSpawner, pipelineRunner) // We start the log poller after the job spawner // so jobs have a chance to apply their initial log filters. if cfg.FeatureLogPoller() { for _, c := range chains.EVM.Chains() { - subservices = append(subservices, c.LogPoller()) + srvcs = append(srvcs, c.LogPoller()) } } @@ -478,12 +479,11 @@ func NewApplication(opts ApplicationOpts) (Application, error) { sqlxDB: opts.SqlxDB, - // NOTE: Can keep things clean by putting more things in subservices - // instead of manually start/closing - subservices: subservices, + // NOTE: Can keep things clean by putting more things in srvcs instead of manually start/closing + srvcs: srvcs, } - for _, service := range app.subservices { + for _, service := range app.srvcs { checkable := service.(services.Checkable) if err := app.HealthChecker.Register(reflect.TypeOf(service).String(), checkable); err != nil { return nil, err @@ -516,14 +516,14 @@ func (app *ChainlinkApplication) Start(ctx context.Context) error { } } - for _, subservice := range app.subservices { + for _, service := range app.srvcs { if ctx.Err() != nil { return errors.Wrap(ctx.Err(), "aborting start") } - app.logger.Debugw("Starting service...", "serviceType", reflect.TypeOf(subservice)) + app.logger.Debugw("Starting service...", "serviceType", reflect.TypeOf(service)) - if err := subservice.Start(ctx); err != nil { + if err := service.Start(ctx); err != nil { return err } } @@ -569,8 +569,8 @@ func (app *ChainlinkApplication) stop() (err error) { app.logger.Info("Gracefully exiting...") // Stop services in the reverse order from which they were started - for i := len(app.subservices) - 1; i >= 0; i-- { - service := app.subservices[i] + for i := len(app.srvcs) - 1; i >= 0; i-- { + service := app.srvcs[i] app.logger.Debugw("Closing service...", "serviceType", reflect.TypeOf(service)) err = multierr.Append(err, service.Close()) } From bbb7a99055fd0ff94758c09bcbb2a0edc391468c Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 21 Sep 2022 18:37:29 -0500 Subject: [PATCH 62/92] core/chains/evm/txmgr: skip disabled keys from nonce syncer (#7496) --- core/chains/evm/txmgr/nonce_syncer.go | 7 +++++-- core/chains/evm/txmgr/txmgr_test.go | 16 +++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/core/chains/evm/txmgr/nonce_syncer.go b/core/chains/evm/txmgr/nonce_syncer.go index c9843f16185..6ad2748cded 100644 --- a/core/chains/evm/txmgr/nonce_syncer.go +++ b/core/chains/evm/txmgr/nonce_syncer.go @@ -81,7 +81,7 @@ func NewNonceSyncer(db *sqlx.DB, lggr logger.Logger, cfg pg.LogConfig, ethClient } } -// SyncAll syncs nonces for all keys in parallel +// SyncAll syncs nonces for all enabled keys in parallel // // This should only be called once, before the EthBroadcaster has started. // Calling it later is not safe and could lead to races. @@ -89,8 +89,11 @@ func (s NonceSyncer) SyncAll(ctx context.Context, keyStates []ethkey.State) (mer var wg sync.WaitGroup var errMu sync.Mutex - wg.Add(len(keyStates)) for _, keyState := range keyStates { + if keyState.Disabled { + continue + } + wg.Add(1) go func(k ethkey.State) { defer wg.Done() if err := s.fastForwardNonceIfNecessary(ctx, k.Address.Address()); err != nil { diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 6ebc4a19939..51bb8af7613 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -871,7 +871,7 @@ func TestTxmgr_AssignsNonceOnStart(t *testing.T) { kst := cltest.NewKeyStore(t, db, cfg).Eth() _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, kst, true) - _, dummyAddress := cltest.MustInsertRandomKeyReturningState(t, kst, false) + _, disabledAddress := cltest.MustInsertRandomKeyReturningState(t, kst, false) cfg.Overrides.GlobalEvmNonceAutoSync = null.BoolFrom(true) evmcfg := evmtest.NewChainScopedConfig(t, cfg) @@ -890,9 +890,6 @@ func TestTxmgr_AssignsNonceOnStart(t *testing.T) { txm := txmgr.NewTxm(db, ethClient, evmcfg, kst, eventBroadcaster, logger.TestLogger(t), checkerFactory, nil) - ethClient.On("PendingNonceAt", mock.Anything, mock.MatchedBy(func(account gethCommon.Address) bool { - return account.Hex() == dummyAddress.Hex() - })).Return(uint64(0), nil).Once() ethClient.On("PendingNonceAt", mock.Anything, mock.MatchedBy(func(account gethCommon.Address) bool { return account.Hex() == fromAddress.Hex() })).Return(ethNodeNonce, errors.New("something exploded")).Once() @@ -902,9 +899,9 @@ func TestTxmgr_AssignsNonceOnStart(t *testing.T) { defer txm.Close() require.Contains(t, err.Error(), "something exploded") - // dummy address got updated + // disabled address did not get updated var n int - err := db.Get(&n, `SELECT next_nonce FROM evm_key_states WHERE address = $1`, dummyAddress) + err := db.Get(&n, `SELECT next_nonce FROM evm_key_states WHERE address = $1`, disabledAddress) require.NoError(t, err) require.Equal(t, 0, n) @@ -919,9 +916,6 @@ func TestTxmgr_AssignsNonceOnStart(t *testing.T) { txm := txmgr.NewTxm(db, ethClient, evmcfg, kst, eventBroadcaster, logger.TestLogger(t), checkerFactory, nil) - ethClient.On("PendingNonceAt", mock.Anything, mock.MatchedBy(func(account gethCommon.Address) bool { - return account.Hex() == dummyAddress.Hex() - })).Return(uint64(0), nil).Once() ethClient.On("PendingNonceAt", mock.Anything, mock.MatchedBy(func(account gethCommon.Address) bool { return account.Hex() == fromAddress.Hex() })).Return(ethNodeNonce, nil).Once() @@ -936,8 +930,8 @@ func TestTxmgr_AssignsNonceOnStart(t *testing.T) { require.NoError(t, err) assert.Equal(t, int64(ethNodeNonce), nonce) - // The dummy key did not get updated - err = db.Get(&nonce, `SELECT next_nonce FROM evm_key_states WHERE address = $1 ORDER BY created_at ASC, id ASC`, dummyAddress) + // The disabled key did not get updated + err = db.Get(&nonce, `SELECT next_nonce FROM evm_key_states WHERE address = $1 ORDER BY created_at ASC, id ASC`, disabledAddress) require.NoError(t, err) assert.Equal(t, int64(0), nonce) }) From cbccc6be4c7133184a0aa1c6b4989f5373e8336a Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Wed, 21 Sep 2022 17:04:53 -0700 Subject: [PATCH 63/92] Refactor with Jordan's help --- .../evm/config/mocks/chain_scoped_config.go | 82 ++++++++- core/cmd/client.go | 2 +- core/config/general_config.go | 40 +++-- core/config/mocks/general_config.go | 82 ++++++++- core/config/v2/types.go | 2 +- core/logger/audit/audit_logger.go | 158 +++++++++--------- core/logger/audit/audit_logger_test.go | 39 +++-- core/services/chainlink/config_general.go | 20 ++- core/services/chainlink/config_test.go | 15 +- .../chainlink/testdata/config-full.toml | 1 + docs/CONFIG.md | 9 +- internal/config/docs.toml | 4 +- 12 files changed, 336 insertions(+), 118 deletions(-) diff --git a/core/chains/evm/config/mocks/chain_scoped_config.go b/core/chains/evm/config/mocks/chain_scoped_config.go index 77e703802d2..bfe0b8acdf5 100644 --- a/core/chains/evm/config/mocks/chain_scoped_config.go +++ b/core/chains/evm/config/mocks/chain_scoped_config.go @@ -106,15 +106,87 @@ func (_m *ChainScopedConfig) AppID() uuid.UUID { return r0 } -// AuditLoggerConfig provides a mock function with given fields: -func (_m *ChainScopedConfig) AuditLoggerConfig() audit.AuditLoggerConfig { +// AuditLoggerEnabled provides a mock function with given fields: +func (_m *ChainScopedConfig) AuditLoggerEnabled() bool { ret := _m.Called() - var r0 audit.AuditLoggerConfig - if rf, ok := ret.Get(0).(func() audit.AuditLoggerConfig); ok { + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// AuditLoggerEnvironment provides a mock function with given fields: +func (_m *ChainScopedConfig) AuditLoggerEnvironment() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() } else { - r0 = ret.Get(0).(audit.AuditLoggerConfig) + r0 = ret.Get(0).(string) + } + + return r0 +} + +// AuditLoggerForwardToUrl provides a mock function with given fields: +func (_m *ChainScopedConfig) AuditLoggerForwardToUrl() (models.URL, error) { + ret := _m.Called() + + var r0 models.URL + if rf, ok := ret.Get(0).(func() models.URL); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(models.URL) + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AuditLoggerHeaders provides a mock function with given fields: +func (_m *ChainScopedConfig) AuditLoggerHeaders() (audit.ServiceHeaders, error) { + ret := _m.Called() + + var r0 audit.ServiceHeaders + if rf, ok := ret.Get(0).(func() audit.ServiceHeaders); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(audit.ServiceHeaders) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AuditLoggerJsonWrapperKey provides a mock function with given fields: +func (_m *ChainScopedConfig) AuditLoggerJsonWrapperKey() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) } return r0 diff --git a/core/cmd/client.go b/core/cmd/client.go index 6c2b6d7a2e8..d67648b3102 100644 --- a/core/cmd/client.go +++ b/core/cmd/client.go @@ -219,7 +219,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg config.Gene } // Configure and optionally start the audit log forwarder service - auditLogger, err := audit.NewAuditLogger(appLggr, cfg.AuditLoggerConfig()) + auditLogger, err := audit.NewAuditLogger(appLggr, cfg) if err != nil { return nil, err } diff --git a/core/config/general_config.go b/core/config/general_config.go index c7f2d52ce93..b37a541b18c 100644 --- a/core/config/general_config.go +++ b/core/config/general_config.go @@ -70,12 +70,12 @@ type GeneralOnlyConfig interface { SetLogSQL(logSQL bool) FeatureFlags + audit.Config AdvisoryLockCheckInterval() time.Duration AdvisoryLockID() int64 AllowOrigins() string AppID() uuid.UUID - AuditLoggerConfig() audit.AuditLoggerConfig AuthenticatedRateLimit() int64 AuthenticatedRateLimitPeriod() models.Duration AutoPprofBlockProfileRate() int @@ -486,15 +486,35 @@ func (c *generalConfig) AppID() uuid.UUID { return c.appID } -// Create the audit logger configuration to send events to an -// external service -func (c *generalConfig) AuditLoggerConfig() audit.AuditLoggerConfig { - return audit.NewAuditLoggerConfig( - c.viper.GetString(envvar.Name("AuditLoggerForwardToUrl")), - c.viper.GetBool(envvar.Name("Dev")), - c.viper.GetString(envvar.Name("AuditLoggerJsonWrapperKey")), - c.viper.GetString(envvar.Name("AuditLoggerHeaders")), - ) +func (c *generalConfig) AuditLoggerEnabled() bool { + return c.viper.GetBool(envvar.Name("AuditLoggerEnabled")) +} + +func (c *generalConfig) AuditLoggerForwardToUrl() (models.URL, error) { + url, err := models.ParseURL(c.viper.GetString(envvar.Name("AuditLoggerForwardToUrl"))) + if err != nil { + return models.URL{}, err + } + return *url, nil +} + +func (c *generalConfig) AuditLoggerEnvironment() string { + if c.viper.GetBool(envvar.Name("Dev")) { + return "develop" + } + return "production" +} + +func (c *generalConfig) AuditLoggerJsonWrapperKey() string { + return c.viper.GetString(envvar.Name("AuditLoggerJsonWrapperKey")) +} + +func (c *generalConfig) AuditLoggerHeaders() (audit.ServiceHeaders, error) { + headers := c.viper.GetString(envvar.Name("AuditLoggerHeaders")) + serviceHeaders := audit.ServiceHeaders{} + err := serviceHeaders.UnmarshalText([]byte(headers)) + + return serviceHeaders, err } // AuthenticatedRateLimit defines the threshold to which authenticated requests diff --git a/core/config/mocks/general_config.go b/core/config/mocks/general_config.go index 7e559387852..1918c25fc6d 100644 --- a/core/config/mocks/general_config.go +++ b/core/config/mocks/general_config.go @@ -102,15 +102,87 @@ func (_m *GeneralConfig) AppID() uuid.UUID { return r0 } -// AuditLoggerConfig provides a mock function with given fields: -func (_m *GeneralConfig) AuditLoggerConfig() audit.AuditLoggerConfig { +// AuditLoggerEnabled provides a mock function with given fields: +func (_m *GeneralConfig) AuditLoggerEnabled() bool { ret := _m.Called() - var r0 audit.AuditLoggerConfig - if rf, ok := ret.Get(0).(func() audit.AuditLoggerConfig); ok { + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// AuditLoggerEnvironment provides a mock function with given fields: +func (_m *GeneralConfig) AuditLoggerEnvironment() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() } else { - r0 = ret.Get(0).(audit.AuditLoggerConfig) + r0 = ret.Get(0).(string) + } + + return r0 +} + +// AuditLoggerForwardToUrl provides a mock function with given fields: +func (_m *GeneralConfig) AuditLoggerForwardToUrl() (models.URL, error) { + ret := _m.Called() + + var r0 models.URL + if rf, ok := ret.Get(0).(func() models.URL); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(models.URL) + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AuditLoggerHeaders provides a mock function with given fields: +func (_m *GeneralConfig) AuditLoggerHeaders() (audit.ServiceHeaders, error) { + ret := _m.Called() + + var r0 audit.ServiceHeaders + if rf, ok := ret.Get(0).(func() audit.ServiceHeaders); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(audit.ServiceHeaders) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AuditLoggerJsonWrapperKey provides a mock function with given fields: +func (_m *GeneralConfig) AuditLoggerJsonWrapperKey() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) } return r0 diff --git a/core/config/v2/types.go b/core/config/v2/types.go index 8f847bb21da..5f106e29d75 100644 --- a/core/config/v2/types.go +++ b/core/config/v2/types.go @@ -31,7 +31,7 @@ type Core struct { TelemetryIngress *TelemetryIngress - AuditLogger audit.AuditLoggerConfig + AuditLogger *audit.AuditLoggerConfig Log *Log WebServer *WebServer diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index fe88d7b8ba3..46022ee34f2 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -7,6 +7,7 @@ import ( "io" "net" "net/http" + "net/url" "os" "strings" "time" @@ -14,6 +15,7 @@ import ( "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services" "github.com/smartcontractkit/chainlink/core/store/models" + "github.com/smartcontractkit/chainlink/core/utils" "github.com/pkg/errors" ) @@ -29,85 +31,85 @@ type AuditLogger interface { Audit(eventID EventID, data Data) } -type validatedAuditLoggerConfig struct { - forwardToUrl string - environment string - jsonWrapperKey string - headers []ServiceHeader -} - type AuditLoggerConfig struct { - ForwardToUrl *string + Enabled *bool + ForwardToUrl *models.URL Environment *string JsonWrapperKey *string - Headers *string -} - -type HTTPAuditLoggerInterface interface { - Do(req *http.Request) (*http.Response, error) + Headers *ServiceHeaders } -// / Wrap all the items into a struct to easily pass them around. This does not -// / do any validation on the values given -func NewAuditLoggerConfig(forwardToUrl string, isDev bool, jsonWrapperKey string, headers string) AuditLoggerConfig { - environment := "production" - if isDev { - environment = "develop" - } - - return AuditLoggerConfig{ - ForwardToUrl: &forwardToUrl, - Environment: &environment, - JsonWrapperKey: &jsonWrapperKey, - Headers: &headers, - } -} +type ServiceHeaders []ServiceHeader -// / Internal function to validate the values sent in to the AuditLogger -// / -// / Checks all sorts of things to eliminate nil pointers and invalid endpoints -// / as well as parsing out the headers into format that's easier to use later. -func validateConfig(alc AuditLoggerConfig) (validatedAuditLoggerConfig, error) { - if alc.ForwardToUrl == nil || alc.Environment == nil || alc.JsonWrapperKey == nil || alc.Headers == nil { - return validatedAuditLoggerConfig{}, errors.New("configuration error: fields were nil and shouldn't have been") - } - // We treat this as the audit logger not being configured so it is not an - // error - if *alc.ForwardToUrl == "" { - return validatedAuditLoggerConfig{}, nil +func (sh *ServiceHeaders) UnmarshalText(input []byte) error { + if sh == nil { + return errors.New("Cannot unmarshal to a nil receiver") } - if _, err := models.ParseURL(*alc.ForwardToUrl); err != nil { - return validatedAuditLoggerConfig{}, errors.New("forwardToURL value is not a valid URL") - } + headers := string(input) - // Split and prepare optional service client headers from env variable - headers := []ServiceHeader{} - if *alc.Headers != "" { - headerLines := strings.Split(*alc.Headers, "\\") + parsed_headers := []ServiceHeader{} + if headers != "" { + headerLines := strings.Split(headers, "\\") for _, header := range headerLines { keyValue := strings.Split(header, "||") if len(keyValue) != 2 { - return validatedAuditLoggerConfig{}, errors.Errorf("invalid headers provided for the audit logger. Value, single pair split on || required, got: %s", keyValue) + return errors.Errorf("invalid headers provided for the audit logger. Value, single pair split on || required, got: %s", keyValue) } - headers = append(headers, ServiceHeader{ + parsed_headers = append(parsed_headers, ServiceHeader{ Header: keyValue[0], Value: keyValue[1], }) } } - return validatedAuditLoggerConfig{ - forwardToUrl: *alc.ForwardToUrl, - environment: *alc.Environment, - jsonWrapperKey: *alc.JsonWrapperKey, - headers: headers, - }, nil + + *sh = parsed_headers + return nil } +func (sh *ServiceHeaders) MarshalText() ([]byte, error) { + if sh == nil { + return nil, errors.New("Cannot unmarshal 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 { + AuditLoggerEnabled() bool + AuditLoggerForwardToUrl() (models.URL, error) + AuditLoggerEnvironment() string + AuditLoggerJsonWrapperKey() string + AuditLoggerHeaders() (ServiceHeaders, error) +} + +type HTTPAuditLoggerInterface interface { + Do(req *http.Request) (*http.Response, error) +} + +// environment := "production" +// if isDev { +// environment = "develop" +// } + type AuditLoggerService struct { logger logger.Logger // The standard logger configured in the node enabled bool // Whether the audit logger is enabled or not - forwardToUrl string // Location we are going to send logs to + forwardToUrl models.URL // Location we are going to send logs to headers []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 @@ -116,8 +118,7 @@ type AuditLoggerService struct { loggingClient HTTPAuditLoggerInterface // Abstract type for sending logs onward loggingChannel chan wrappedAuditLog - ctx context.Context - cancel context.CancelFunc + chStop chan struct{} chDone chan struct{} } @@ -137,43 +138,44 @@ type wrappedAuditLog struct { // 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, unverifiedConfig AuditLoggerConfig) (AuditLogger, error) { - config, err := validateConfig(unverifiedConfig) +func NewAuditLogger(logger logger.Logger, config Config) (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.AuditLoggerEnabled() { + return &AuditLoggerService{}, nil + } + hostname, err := os.Hostname() if err != nil { - return &AuditLoggerService{}, err + return nil, errors.Errorf("initialization error - unable to get hostname: %s", err) } - // If there was no error and this is empty string then the logger - // is not configured - if config.forwardToUrl == "" { + forwardToUrl, err := config.AuditLoggerForwardToUrl() + if err != nil { return &AuditLoggerService{}, nil } - hostname, err := os.Hostname() + headers, err := config.AuditLoggerHeaders() if err != nil { - return nil, errors.Errorf("initialization error - unable to get hostname: %s", err) + return &AuditLoggerService{}, nil } - ctx, cancel := context.WithCancel(context.Background()) - loggingChannel := make(chan wrappedAuditLog, bufferCapacity) // Create new AuditLoggerService auditLogger := AuditLoggerService{ logger: logger.Helper(1), enabled: true, - forwardToUrl: config.forwardToUrl, - headers: config.headers, - jsonWrapperKey: config.jsonWrapperKey, - environmentName: config.environment, + forwardToUrl: forwardToUrl, + headers: headers, + jsonWrapperKey: config.AuditLoggerJsonWrapperKey(), + environmentName: config.AuditLoggerEnvironment(), hostname: hostname, localIP: getLocalIP(), loggingClient: &http.Client{Timeout: time.Second * webRequestTimeout}, loggingChannel: loggingChannel, - ctx: ctx, - cancel: cancel, + chStop: make(chan struct{}), chDone: make(chan struct{}), } @@ -227,7 +229,7 @@ func (l *AuditLoggerService) Close() error { } l.logger.Warnf("Disabled the audit logger service") - l.cancel() + close(l.chStop) <-l.chDone return nil @@ -262,7 +264,7 @@ func (l *AuditLoggerService) runLoop() { for { select { - case <-l.ctx.Done(): + case <-l.chStop: l.logger.Warn("The audit logger is shutting down") return case event := <-l.loggingChannel: @@ -297,9 +299,11 @@ func (l *AuditLoggerService) postLogToLogService(eventID EventID, data Data) { l.logger.Errorw("unable to serialize wrapped audit log item to JSON", "err", err, "logItem", logItem) return } + ctx, cancel := utils.ContextFromChan(l.chStop) + defer cancel() // Send to remote service - req, err := http.NewRequestWithContext(l.ctx, "POST", l.forwardToUrl, bytes.NewReader(serializedLog)) + req, err := http.NewRequestWithContext(ctx, "POST", (*url.URL)(&l.forwardToUrl).String(), bytes.NewReader(serializedLog)) if err != nil { l.logger.Error("failed to create request to remote logging service!") } diff --git a/core/logger/audit/audit_logger_test.go b/core/logger/audit/audit_logger_test.go index c8fe1063896..8e287254c91 100644 --- a/core/logger/audit/audit_logger_test.go +++ b/core/logger/audit/audit_logger_test.go @@ -12,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink/core/internal/testutils" "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/logger/audit" + "github.com/smartcontractkit/chainlink/core/store/models" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli" @@ -53,18 +54,30 @@ func (mock *MockHTTPClient) Do(req *http.Request) (*http.Response, error) { return &http.Response{}, nil } -func getAuditLoggerConfig() audit.AuditLoggerConfig { - forwardToUrl := "empty" - environment := "test" - jsonWrapperKey := "" - headers := "" - - return audit.AuditLoggerConfig{ - ForwardToUrl: &forwardToUrl, - Environment: &environment, - JsonWrapperKey: &jsonWrapperKey, - Headers: &headers, +type Config struct{} + +func (c Config) AuditLoggerEnabled() bool { + return true +} + +func (c Config) AuditLoggerEnvironment() string { + return "develop" +} + +func (c Config) AuditLoggerForwardToUrl() (models.URL, error) { + url, err := models.ParseURL("http://localhost:9898") + if err != nil { + return models.URL{}, err } + return *url, nil +} + +func (c Config) AuditLoggerHeaders() (audit.ServiceHeaders, error) { + return make(audit.ServiceHeaders, 0), nil +} + +func (c Config) AuditLoggerJsonWrapperKey() string { + return "" } func TestCheckLoginAuditLog(t *testing.T) { @@ -82,8 +95,10 @@ func TestCheckLoginAuditLog(t *testing.T) { // as well logger := logger.TestLogger(t) + auditLoggerTestConfig := Config{} + // Create new AuditLoggerService - auditLogger, err := audit.NewAuditLogger(logger, getAuditLoggerConfig()) + auditLogger, err := audit.NewAuditLogger(logger, &auditLoggerTestConfig) assert.NoError(t, err) // Cast to concrete type so we can swap out the internals diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 191167b11b3..1dba49d3850 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -167,8 +167,24 @@ func (g *generalConfig) AllowOrigins() string { return *g.c.WebServer.AllowOrigins } -func (g *generalConfig) AuditLoggerConfig() audit.AuditLoggerConfig { - return g.c.AuditLogger +func (g *generalConfig) AuditLoggerEnabled() bool { + return *g.c.AuditLogger.Enabled +} + +func (g *generalConfig) AuditLoggerForwardToUrl() (models.URL, error) { + return *g.c.AuditLogger.ForwardToUrl, nil +} + +func (g *generalConfig) AuditLoggerHeaders() (audit.ServiceHeaders, error) { + return *g.c.AuditLogger.Headers, nil +} + +func (g *generalConfig) AuditLoggerEnvironment() string { + return *g.c.AuditLogger.Environment +} + +func (g *generalConfig) AuditLoggerJsonWrapperKey() string { + return *g.c.AuditLogger.JsonWrapperKey } func (g *generalConfig) AuthenticatedRateLimit() int64 { diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 1ffa2253b26..7a7a85fffcb 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -206,10 +206,12 @@ func TestConfig_Marshal(t *testing.T) { full := global - full.AuditLogger = audit.AuditLoggerConfig{ - ForwardToUrl: ptr("http://localhost:9898"), + serviceHeaders := make(audit.ServiceHeaders, 0) + full.AuditLogger = &audit.AuditLoggerConfig{ + Enabled: ptr(false), + ForwardToUrl: mustURL("http://localhost:9898"), Environment: ptr("develop"), - Headers: ptr(""), + Headers: ptr(serviceHeaders), JsonWrapperKey: ptr(""), } @@ -576,6 +578,13 @@ func TestConfig_Marshal(t *testing.T) { InsecureFastScrypt = true RootDir = 'test/root/dir' ShutdownGracePeriod = '10s' +`}, + {"AuditLogger", Config{Core: config.Core{AuditLogger: full.AuditLogger}}, `[AuditLogger] +Enabled = false +ForwardToUrl = 'http://localhost:9898' +Environment = 'develop' +JsonWrapperKey = '' +Headers = '' `}, {"Feature", Config{Core: config.Core{Feature: full.Feature}}, `[Feature] FeedsManager = true diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 48c45291e9e..c1e4057f784 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -43,6 +43,7 @@ SendTimeout = '5s' UseBatchSend = true [AuditLogger] +Enabled = false ForwardToUrl = 'http://localhost:9898' Environment = 'develop' JsonWrapperKey = '' diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 651e03e4fb3..151c0469868 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -332,6 +332,7 @@ UseBatchSend toggles sending telemetry to the ingress server using the batch cli ## AuditLogger ```toml [AuditLogger] +Enabled = false # Default ForwardToUrl = 'http://localhost:9898' # Example Environment = 'develop' # Example JsonWrapperKey = '' # Default @@ -339,6 +340,12 @@ Headers = '' # Default ``` +### Enabled +```toml +Enabled = false # Default +``` +Enabled determines if this logger should be configured at all + ### ForwardToUrl ```toml ForwardToUrl = 'http://localhost:9898' # Example @@ -361,7 +368,7 @@ JsonWrapperKey if set wraps the map of data under another single key to make par ```toml Headers = '' # Default ``` -Headers is the set of headers you wish to pass along with each request formatted as specified in the documentation +Headers is the set of headers you wish to pass along with each request ## Log ```toml diff --git a/internal/config/docs.toml b/internal/config/docs.toml index 97f53774eca..1b491f4a8da 100644 --- a/internal/config/docs.toml +++ b/internal/config/docs.toml @@ -106,13 +106,15 @@ SendTimeout = '10s' # Default UseBatchSend = true # Default [AuditLogger] +# Enabled determines if this logger should be configured at all +Enabled = false # Default # ForwardToUrl is where you want to forward logs to ForwardToUrl = 'http://localhost:9898' # Example # Environment is an identifier for the host/environment you're on Environment = 'develop' # Example # JsonWrapperKey if set wraps the map of data under another single key to make parsing easier JsonWrapperKey = '' # Default -# Headers is the set of headers you wish to pass along with each request formatted as specified in the documentation +# Headers is the set of headers you wish to pass along with each request Headers = '' # Default [Log] From ab01f13039a60970a4c9d08f32c096ebc83dda34 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Wed, 21 Sep 2022 19:21:53 -0700 Subject: [PATCH 64/92] Fix tests --- core/logger/audit/audit_logger_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/logger/audit/audit_logger_test.go b/core/logger/audit/audit_logger_test.go index 8e287254c91..3771939d8ab 100644 --- a/core/logger/audit/audit_logger_test.go +++ b/core/logger/audit/audit_logger_test.go @@ -61,7 +61,7 @@ func (c Config) AuditLoggerEnabled() bool { } func (c Config) AuditLoggerEnvironment() string { - return "develop" + return "test" } func (c Config) AuditLoggerForwardToUrl() (models.URL, error) { From 861e22910ed4efce60681d5f92c06282568a66ba Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Thu, 22 Sep 2022 15:53:39 -0700 Subject: [PATCH 65/92] Unify key manipulation logs --- core/config/envvar/schema.go | 1 + core/logger/audit/audit_types.go | 30 ++--------- core/services/keystore/master.go | 6 +-- core/web/chains_controller.go | 4 +- core/web/eth_keys_controller.go | 30 +++++++---- core/web/keys_controller.go | 86 ++++++++++++++++++++------------ core/web/p2p_keys_controller.go | 23 ++++++--- core/web/resolver/mutation.go | 22 +++++--- core/web/vrf_keys_controller.go | 27 +++++++--- 9 files changed, 137 insertions(+), 92 deletions(-) diff --git a/core/config/envvar/schema.go b/core/config/envvar/schema.go index 8fe0e3a72f3..21ef9711181 100644 --- a/core/config/envvar/schema.go +++ b/core/config/envvar/schema.go @@ -61,6 +61,7 @@ type ConfigSchema struct { ShutdownGracePeriod time.Duration `env:"SHUTDOWN_GRACE_PERIOD" default:"5s"` // Audit Logger + AuditLoggerEnabled bool `env:"AUDIT_LOGGER_ENABLED" default:"false"` AuditLoggerForwardToUrl string `env:"AUDIT_LOGGER_FORWARD_TO_URL" default:""` AuditLoggerHeaders string `env:"AUDIT_LOGGER_HEADERS" default:""` AuditLoggerJsonWrapperKey string `env:"AUDIT_LOGGER_JSON_WRAPPER_KEY" default:""` diff --git a/core/logger/audit/audit_types.go b/core/logger/audit/audit_types.go index ca581fc4d2a..eaf32b1f7a4 100644 --- a/core/logger/audit/audit_types.go +++ b/core/logger/audit/audit_types.go @@ -42,31 +42,11 @@ const ( OCR2KeyBundleExported EventID = "OCR2_KEY_BUNDLE_EXPORTED" OCR2KeyBundleDeleted EventID = "OCR2_KEY_BUNDLE_DELETED" - ETHKeyCreated EventID = "ETH_KEY_CREATED" - ETHKeyUpdated EventID = "ETH_KEY_UPDATED" - ETHKeyImported EventID = "ETH_KEY_IMPORTED" - ETHKeyExported EventID = "ETH_KEY_EXPORTED" - ETHKeyDeleted EventID = "ETH_KEY_DELETED" - - P2PKeyCreated EventID = "P2P_KEY_CREATED" - P2PKeyImported EventID = "P2P_KEY_IMPORTED" - P2PKeyExported EventID = "P2P_KEY_EXPORTED" - P2PKeyDeleted EventID = "P2P_KEY_DELETED" - - VRFKeyCreated EventID = "VRF_KEY_CREATED" - VRFKeyImported EventID = "VRF_KEY_IMPORTED" - VRFKeyExported EventID = "VRF_KEY_EXPORTED" - VRFKeyDeleted EventID = "VRF_KEY_DELETED" - - TerraKeyCreated EventID = "TERRA_KEY_CREATED" - TerraKeyImported EventID = "TERRA_KEY_IMPORTED" - TerraKeyExported EventID = "TERRA_KEY_EXPORTED" - TerraKeyDeleted EventID = "TERRA_KEY_DELETED" - - SolanaKeyCreated EventID = "SOLANA_KEY_CREATED" - SolanaKeyImported EventID = "SOLANA_KEY_IMPORTED" - SolanaKeyExported EventID = "SOLANA_KEY_EXPORTED" - SolanaKeyDeleted EventID = "SOLANA_KEY_DELETED" + KeyCreated EventID = "KEY_CREATED" + KeyUpdated EventID = "KEY_UPDATED" + KeyImported EventID = "KEY_IMPORTED" + KeyExported EventID = "KEY_EXPORTED" + KeyDeleted EventID = "KEY_DELETED" EthTransactionCreated EventID = "ETH_TRANSACTION_CREATED" TerraTransactionCreated EventID = "TERRA_TRANSACTION_CREATED" diff --git a/core/services/keystore/master.go b/core/services/keystore/master.go index 18a7f2349ff..c155bfa66bf 100644 --- a/core/services/keystore/master.go +++ b/core/services/keystore/master.go @@ -287,7 +287,7 @@ func (km *keyManager) save(callbacks ...func(pg.Queryer) error) error { // caller must hold lock! func (km *keyManager) safeAddKey(unknownKey Key, callbacks ...func(pg.Queryer) error) error { - fieldName, err := getFieldNameForKey(unknownKey) + fieldName, err := GetFieldNameForKey(unknownKey) if err != nil { return err } @@ -309,7 +309,7 @@ func (km *keyManager) safeAddKey(unknownKey Key, callbacks ...func(pg.Queryer) e // caller must hold lock! func (km *keyManager) safeRemoveKey(unknownKey Key, callbacks ...func(pg.Queryer) error) (err error) { - fieldName, err := getFieldNameForKey(unknownKey) + fieldName, err := GetFieldNameForKey(unknownKey) if err != nil { return err } @@ -333,7 +333,7 @@ func (km *keyManager) isLocked() bool { return len(km.password) == 0 } -func getFieldNameForKey(unknownKey Key) (string, error) { +func GetFieldNameForKey(unknownKey Key) (string, error) { switch unknownKey.(type) { case csakey.KeyV2: return "CSA", nil diff --git a/core/web/chains_controller.go b/core/web/chains_controller.go index c845fe01ddd..ab608a76443 100644 --- a/core/web/chains_controller.go +++ b/core/web/chains_controller.go @@ -111,8 +111,10 @@ func (cc *chainsController[I, C, R]) Create(c *gin.Context) { chainj, err := json.Marshal(chain) if err != nil { cc.lggr.Errorf("Unable to marshal chain to json", "err", err) + cc.auditLogger.Audit(audit.ChainAdded, map[string]interface{}{"chain": nil}) + } else { + cc.auditLogger.Audit(audit.ChainAdded, map[string]interface{}{"chain": chainj}) } - cc.auditLogger.Audit(audit.ChainAdded, map[string]interface{}{"chain": chainj}) jsonAPIResponseWithStatus(c, cc.newResource(chain), cc.resourceName, http.StatusCreated) } diff --git a/core/web/eth_keys_controller.go b/core/web/eth_keys_controller.go index 80026427ca3..c225c07784d 100644 --- a/core/web/eth_keys_controller.go +++ b/core/web/eth_keys_controller.go @@ -132,9 +132,9 @@ func (ekc *ETHKeysController) Create(c *gin.Context) { return } - ekc.App.GetAuditLogger().Audit(audit.ETHKeyCreated, map[string]interface{}{ - "ethPublicKey": key.Address, - "ethID": key.ID(), + ekc.App.GetAuditLogger().Audit(audit.KeyCreated, map[string]interface{}{ + "type": "ethereum", + "id": key.ID(), }) jsonAPIResponseWithStatus(c, r, "account", http.StatusCreated) @@ -196,9 +196,9 @@ func (ekc *ETHKeysController) Update(c *gin.Context) { return } - ekc.App.GetAuditLogger().Audit(audit.ETHKeyUpdated, map[string]interface{}{ - "ethPublicKey": key.Address, - "ethID": key.ID(), + ekc.App.GetAuditLogger().Audit(audit.KeyUpdated, map[string]interface{}{ + "type": "ethereum", + "id": keyID, }) jsonAPIResponseWithStatus(c, r, "account", http.StatusOK) @@ -238,7 +238,10 @@ func (ekc *ETHKeysController) Delete(c *gin.Context) { return } - ekc.App.GetAuditLogger().Audit(audit.ETHKeyDeleted, map[string]interface{}{"id": keyID}) + ekc.App.GetAuditLogger().Audit(audit.KeyDeleted, map[string]interface{}{ + "type": "ethereum", + "id": keyID, + }) c.Status(http.StatusNoContent) } @@ -286,9 +289,9 @@ func (ekc *ETHKeysController) Import(c *gin.Context) { return } - ekc.App.GetAuditLogger().Audit(audit.ETHKeyImported, map[string]interface{}{ - "ethPublicKey": key.Address, - "ethID": key.ID(), + ekc.App.GetAuditLogger().Audit(audit.KeyImported, map[string]interface{}{ + "type": "ethereum", + "id": key.ID(), }) jsonAPIResponse(c, r, "account") @@ -306,7 +309,12 @@ func (ekc *ETHKeysController) Export(c *gin.Context) { return } - ekc.App.GetAuditLogger().Audit(audit.ETHKeyExported, map[string]interface{}{"address": address}) + ekc.App.GetAuditLogger().Audit(audit.KeyExported, map[string]interface{}{ + "type": "ethereum", + "id": "", // TODO: Figure out what the best way to fetch this is for consistency + "address": address, + }) + c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/keys_controller.go b/core/web/keys_controller.go index 2455dd5cf32..e1492174ab9 100644 --- a/core/web/keys_controller.go +++ b/core/web/keys_controller.go @@ -3,7 +3,6 @@ package web import ( "io/ioutil" "net/http" - "strings" "github.com/gin-gonic/gin" "github.com/manyminds/api2go/jsonapi" @@ -11,8 +10,6 @@ import ( "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/keystore" - "github.com/smartcontractkit/chainlink/core/services/keystore/keys/solkey" - "github.com/smartcontractkit/chainlink/core/services/keystore/keys/terrakey" ) type Keystore[K keystore.Key] interface { @@ -74,17 +71,17 @@ func (kc *keysController[K, R]) Create(c *gin.Context) { return } - // Emit audit log, determine if Terra or Solana key - switch unwrappedKey := any(key).(type) { - case terrakey.Key: - kc.auditLogger.Audit(audit.TerraKeyCreated, map[string]interface{}{ - "publicKey": unwrappedKey.PublicKey(), - "id": unwrappedKey.ID(), + type_, err := keystore.GetFieldNameForKey(key) + + if err != nil { + kc.auditLogger.Audit(audit.KeyCreated, map[string]interface{}{ + "type": "unknown", + "id": key.ID(), }) - case solkey.Key: - kc.auditLogger.Audit(audit.SolanaKeyCreated, map[string]interface{}{ - "publicKey": unwrappedKey.PublicKey(), - "id": unwrappedKey.ID(), + } else { + kc.auditLogger.Audit(audit.KeyCreated, map[string]interface{}{ + "type": type_, + "id": key.ID(), }) } @@ -104,12 +101,18 @@ func (kc *keysController[K, R]) Delete(c *gin.Context) { return } - // Emit audit log, determine if Terra or Solana key - switch any(key).(type) { - case terrakey.Key: - kc.auditLogger.Audit(audit.TerraKeyDeleted, map[string]interface{}{"id": keyID}) - case solkey.Key: - kc.auditLogger.Audit(audit.SolanaKeyDeleted, map[string]interface{}{"id": keyID}) + type_, err := keystore.GetFieldNameForKey(key) + + if err != nil { + kc.auditLogger.Audit(audit.KeyDeleted, map[string]interface{}{ + "type": "unknown", + "id": key.ID(), + }) + } else { + kc.auditLogger.Audit(audit.KeyDeleted, map[string]interface{}{ + "type": type_, + "id": key.ID(), + }) } jsonAPIResponse(c, kc.newResource(key), kc.resourceName) @@ -130,17 +133,17 @@ func (kc *keysController[K, R]) Import(c *gin.Context) { return } - // Emit audit log, determine if Terra or Solana key - switch unwrappedKey := any(key).(type) { - case terrakey.Key: - kc.auditLogger.Audit(audit.TerraKeyImported, map[string]interface{}{ - "publicKey": unwrappedKey.PublicKey(), - "id": unwrappedKey.ID(), + type_, err := keystore.GetFieldNameForKey(key) + + if err != nil { + kc.auditLogger.Audit(audit.KeyImported, map[string]interface{}{ + "type": "unknown", + "id": key.ID(), }) - case solkey.Key: - kc.auditLogger.Audit(audit.SolanaKeyImported, map[string]interface{}{ - "publicKey": unwrappedKey.PublicKey(), - "id": unwrappedKey.ID(), + } else { + kc.auditLogger.Audit(audit.KeyImported, map[string]interface{}{ + "type": type_, + "id": key.ID(), }) } @@ -158,10 +161,27 @@ func (kc *keysController[K, R]) Export(c *gin.Context) { return } - if strings.HasPrefix(c.Request.URL.Path, "/v2/keys/terra") { - kc.auditLogger.Audit(audit.TerraKeyExported, map[string]interface{}{"id": keyID}) - } else if strings.HasPrefix(c.Request.URL.Path, "/v2/keys/solana") { - kc.auditLogger.Audit(audit.SolanaKeyExported, map[string]interface{}{"id": keyID}) + key, err := kc.ks.Get(keyID) + if err != nil { + kc.auditLogger.Audit(audit.KeyExported, map[string]interface{}{ + "type": "unknown", + "id": key.ID(), + }) + } else { + + type_, err := keystore.GetFieldNameForKey(key) + + if err != nil { + kc.auditLogger.Audit(audit.KeyExported, map[string]interface{}{ + "type": "unknown", + "id": key.ID(), + }) + } else { + kc.auditLogger.Audit(audit.KeyExported, map[string]interface{}{ + "type": type_, + "id": key.ID(), + }) + } } c.Data(http.StatusOK, MediaType, bytes) diff --git a/core/web/p2p_keys_controller.go b/core/web/p2p_keys_controller.go index 59df2752852..95c517c0c9f 100644 --- a/core/web/p2p_keys_controller.go +++ b/core/web/p2p_keys_controller.go @@ -39,9 +39,10 @@ func (p2pkc *P2PKeysController) Create(c *gin.Context) { return } - p2pkc.App.GetAuditLogger().Audit(audit.P2PKeyCreated, map[string]interface{}{ + p2pkc.App.GetAuditLogger().Audit(audit.KeyCreated, map[string]interface{}{ + "type": "p2p", + "id": key.ID(), "p2pPublicKey": key.PublicKeyHex(), - "p2pID": key.ID(), "p2pPeerID": key.PeerID(), "p2pType": key.Type(), }) @@ -69,7 +70,11 @@ func (p2pkc *P2PKeysController) Delete(c *gin.Context) { return } - p2pkc.App.GetAuditLogger().Audit(audit.P2PKeyDeleted, map[string]interface{}{"id": keyID}) + p2pkc.App.GetAuditLogger().Audit(audit.KeyDeleted, map[string]interface{}{ + "type": "p2p", + "id": keyID, + }) + jsonAPIResponse(c, presenters.NewP2PKeyResource(key), "p2pKey") } @@ -91,12 +96,14 @@ func (p2pkc *P2PKeysController) Import(c *gin.Context) { return } - p2pkc.App.GetAuditLogger().Audit(audit.P2PKeyImported, map[string]interface{}{ + p2pkc.App.GetAuditLogger().Audit(audit.KeyImported, map[string]interface{}{ + "type": "p2p", + "id": key.ID(), "p2pPublicKey": key.PublicKeyHex(), - "p2pID": key.ID(), "p2pPeerID": key.PeerID(), "p2pType": key.Type(), }) + jsonAPIResponse(c, presenters.NewP2PKeyResource(key), "p2pKey") } @@ -119,6 +126,10 @@ func (p2pkc *P2PKeysController) Export(c *gin.Context) { return } - p2pkc.App.GetAuditLogger().Audit(audit.P2PKeyExported, map[string]interface{}{"keyID": keyID}) + p2pkc.App.GetAuditLogger().Audit(audit.KeyExported, map[string]interface{}{ + "type": "p2p", + "id": keyID, + }) + c.Data(http.StatusOK, MediaType, bytes) } diff --git a/core/web/resolver/mutation.go b/core/web/resolver/mutation.go index 071d1817822..a146ffe3a57 100644 --- a/core/web/resolver/mutation.go +++ b/core/web/resolver/mutation.go @@ -689,9 +689,10 @@ func (r *Resolver) CreateP2PKey(ctx context.Context) (*CreateP2PKeyPayloadResolv return nil, err } - r.App.GetAuditLogger().Audit(audit.P2PKeyCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(audit.KeyCreated, map[string]interface{}{ + "type": "p2p", + "id": key.ID(), "p2pPublicKey": key.PublicKeyHex(), - "p2pID": key.ID(), "p2pPeerID": key.PeerID(), "p2pType": key.Type(), }) @@ -719,7 +720,11 @@ func (r *Resolver) DeleteP2PKey(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(audit.P2PKeyDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(audit.KeyDeleted, map[string]interface{}{ + "type": "p2p", + "id": args.ID, + }) + return NewDeleteP2PKeyPayload(key, nil), nil } @@ -733,9 +738,10 @@ func (r *Resolver) CreateVRFKey(ctx context.Context) (*CreateVRFKeyPayloadResolv return nil, err } - r.App.GetAuditLogger().Audit(audit.VRFKeyCreated, map[string]interface{}{ + r.App.GetAuditLogger().Audit(audit.KeyCreated, map[string]interface{}{ + "type": "vrf", + "id": key.ID(), "vrfPublicKey": key.PublicKey, - "vrfID": key.ID(), "vrfPublicKeyAddress": key.PublicKey.Address(), }) @@ -757,7 +763,11 @@ func (r *Resolver) DeleteVRFKey(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(audit.VRFKeyDeleted, map[string]interface{}{"id": args.ID}) + r.App.GetAuditLogger().Audit(audit.KeyDeleted, map[string]interface{}{ + "type": "vrf", + "id": args.ID, + }) + return NewDeleteVRFKeyPayloadResolver(key, nil), nil } diff --git a/core/web/vrf_keys_controller.go b/core/web/vrf_keys_controller.go index f97950399ba..3e0aa113eb4 100644 --- a/core/web/vrf_keys_controller.go +++ b/core/web/vrf_keys_controller.go @@ -38,11 +38,13 @@ func (vrfkc *VRFKeysController) Create(c *gin.Context) { return } - vrfkc.App.GetAuditLogger().Audit(audit.VRFKeyCreated, map[string]interface{}{ + vrfkc.App.GetAuditLogger().Audit(audit.KeyCreated, map[string]interface{}{ + "type": "vrf", + "id": pk.ID(), "vrfPublicKey": pk.PublicKey, - "vrfID": pk.ID(), "vrfPublicKeyAddress": pk.PublicKey.Address(), }) + jsonAPIResponse(c, presenters.NewVRFKeyResource(pk, vrfkc.App.GetLogger()), "vrfKey") } @@ -63,7 +65,11 @@ func (vrfkc *VRFKeysController) Delete(c *gin.Context) { return } - vrfkc.App.GetAuditLogger().Audit(audit.VRFKeyDeleted, map[string]interface{}{"id": keyID}) + vrfkc.App.GetAuditLogger().Audit(audit.KeyDeleted, map[string]interface{}{ + "type": "vrf", + "id": keyID, + }) + jsonAPIResponse(c, presenters.NewVRFKeyResource(key, vrfkc.App.GetLogger()), "vrfKey") } @@ -85,10 +91,13 @@ func (vrfkc *VRFKeysController) Import(c *gin.Context) { return } - vrfkc.App.GetAuditLogger().Audit(audit.VRFKeyImported, map[string]interface{}{ - "vrfID": key.ID(), - "vrfPublicKey": key.PublicKey, + vrfkc.App.GetAuditLogger().Audit(audit.KeyImported, map[string]interface{}{ + "type": "vrf", + "id": key.ID(), + "vrfPublicKey": key.PublicKey, + "vrfPublicKeyAddress": key.PublicKey.Address(), }) + jsonAPIResponse(c, presenters.NewVRFKeyResource(key, vrfkc.App.GetLogger()), "vrfKey") } @@ -107,6 +116,10 @@ func (vrfkc *VRFKeysController) Export(c *gin.Context) { return } - vrfkc.App.GetAuditLogger().Audit(audit.VRFKeyExported, map[string]interface{}{"keyID": keyID}) + vrfkc.App.GetAuditLogger().Audit(audit.KeyExported, map[string]interface{}{ + "type": "vrf", + "id": keyID, + }) + c.Data(http.StatusOK, MediaType, bytes) } From 7b938fa25cc60d54f656a520cbe7310cf39458a7 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Thu, 22 Sep 2022 16:22:36 -0700 Subject: [PATCH 66/92] Add env defaults --- core/services/chainlink/testdata/dump/full-custom.env | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/services/chainlink/testdata/dump/full-custom.env b/core/services/chainlink/testdata/dump/full-custom.env index 5b7413b9943..d11c1d508ba 100644 --- a/core/services/chainlink/testdata/dump/full-custom.env +++ b/core/services/chainlink/testdata/dump/full-custom.env @@ -33,6 +33,11 @@ DATABASE_LOCKING_MODE=advisory LEASE_LOCK_DURATION=5s LEASE_LOCK_REFRESH_INTERVAL=2s +AUDIT_LOGGER_ENABLED=false +AUDIT_LOGGER_FORWARD_TO_URL=http://localhost:9898 +AUDIT_LOGGER_JSON_WRAPPER_KEY=event +AUDIT_LOGGER_HEADERS= + DATABASE_BACKUP_DIR=db/backup DATABASE_BACKUP_FREQUENCY=10m DATABASE_BACKUP_MODE=lite From faab18ffce32b2548667c0cbd1369afde2952214 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Thu, 22 Sep 2022 17:55:01 -0700 Subject: [PATCH 67/92] Use dev function --- core/config/general_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/config/general_config.go b/core/config/general_config.go index b37a541b18c..7450f1be304 100644 --- a/core/config/general_config.go +++ b/core/config/general_config.go @@ -499,7 +499,7 @@ func (c *generalConfig) AuditLoggerForwardToUrl() (models.URL, error) { } func (c *generalConfig) AuditLoggerEnvironment() string { - if c.viper.GetBool(envvar.Name("Dev")) { + if c.Dev() { return "develop" } return "production" From 8e6f44fa581f9ec754d6aa1d2963491bde3ba866 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 22 Sep 2022 21:32:21 -0500 Subject: [PATCH 68/92] keysController field names --- core/web/keys_controller.go | 85 +++++++++++-------------------------- 1 file changed, 24 insertions(+), 61 deletions(-) diff --git a/core/web/keys_controller.go b/core/web/keys_controller.go index e1492174ab9..7b58d177de5 100644 --- a/core/web/keys_controller.go +++ b/core/web/keys_controller.go @@ -1,6 +1,7 @@ package web import ( + "fmt" "io/ioutil" "net/http" @@ -38,6 +39,7 @@ type keysController[K keystore.Key, R jsonapi.EntityNamer] struct { ks Keystore[K] lggr logger.Logger auditLogger audit.AuditLogger + typ string resourceName string newResource func(K) *R newResources func([]K) []R @@ -45,10 +47,16 @@ type keysController[K keystore.Key, R jsonapi.EntityNamer] struct { func NewKeysController[K keystore.Key, R jsonapi.EntityNamer](ks Keystore[K], lggr logger.Logger, auditLogger audit.AuditLogger, resourceName string, newResource func(K) *R, newResources func([]K) []R) KeysController { + var k K + typ, err := keystore.GetFieldNameForKey(k) + if err != nil { + panic(fmt.Errorf("unable to create keys controller: %v", err)) + } return &keysController[K, R]{ ks: ks, lggr: lggr, auditLogger: auditLogger, + typ: typ, resourceName: resourceName, newResource: newResource, newResources: newResources, @@ -71,19 +79,10 @@ func (kc *keysController[K, R]) Create(c *gin.Context) { return } - type_, err := keystore.GetFieldNameForKey(key) - - if err != nil { - kc.auditLogger.Audit(audit.KeyCreated, map[string]interface{}{ - "type": "unknown", - "id": key.ID(), - }) - } else { - kc.auditLogger.Audit(audit.KeyCreated, map[string]interface{}{ - "type": type_, - "id": key.ID(), - }) - } + kc.auditLogger.Audit(audit.KeyCreated, map[string]interface{}{ + "type": kc.typ, + "id": key.ID(), + }) jsonAPIResponse(c, kc.newResource(key), kc.resourceName) } @@ -101,19 +100,10 @@ func (kc *keysController[K, R]) Delete(c *gin.Context) { return } - type_, err := keystore.GetFieldNameForKey(key) - - if err != nil { - kc.auditLogger.Audit(audit.KeyDeleted, map[string]interface{}{ - "type": "unknown", - "id": key.ID(), - }) - } else { - kc.auditLogger.Audit(audit.KeyDeleted, map[string]interface{}{ - "type": type_, - "id": key.ID(), - }) - } + kc.auditLogger.Audit(audit.KeyDeleted, map[string]interface{}{ + "type": kc.typ, + "id": key.ID(), + }) jsonAPIResponse(c, kc.newResource(key), kc.resourceName) } @@ -133,19 +123,10 @@ func (kc *keysController[K, R]) Import(c *gin.Context) { return } - type_, err := keystore.GetFieldNameForKey(key) - - if err != nil { - kc.auditLogger.Audit(audit.KeyImported, map[string]interface{}{ - "type": "unknown", - "id": key.ID(), - }) - } else { - kc.auditLogger.Audit(audit.KeyImported, map[string]interface{}{ - "type": type_, - "id": key.ID(), - }) - } + kc.auditLogger.Audit(audit.KeyImported, map[string]interface{}{ + "type": kc.typ, + "id": key.ID(), + }) jsonAPIResponse(c, kc.newResource(key), kc.resourceName) } @@ -161,28 +142,10 @@ func (kc *keysController[K, R]) Export(c *gin.Context) { return } - key, err := kc.ks.Get(keyID) - if err != nil { - kc.auditLogger.Audit(audit.KeyExported, map[string]interface{}{ - "type": "unknown", - "id": key.ID(), - }) - } else { - - type_, err := keystore.GetFieldNameForKey(key) - - if err != nil { - kc.auditLogger.Audit(audit.KeyExported, map[string]interface{}{ - "type": "unknown", - "id": key.ID(), - }) - } else { - kc.auditLogger.Audit(audit.KeyExported, map[string]interface{}{ - "type": type_, - "id": key.ID(), - }) - } - } + kc.auditLogger.Audit(audit.KeyExported, map[string]interface{}{ + "type": kc.typ, + "id": keyID, + }) c.Data(http.StatusOK, MediaType, bytes) } From 021503eab55120b0c01ead291fc9753ccc9675a3 Mon Sep 17 00:00:00 2001 From: Notorious Enigma <85518954+NotoriousEnigma@users.noreply.github.com> Date: Mon, 26 Sep 2022 16:18:25 -0700 Subject: [PATCH 69/92] Fix tests? --- core/config/envvar/schema_test.go | 1 + core/config/presenters.go | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/core/config/envvar/schema_test.go b/core/config/envvar/schema_test.go index c973071fe32..b33587c07a5 100644 --- a/core/config/envvar/schema_test.go +++ b/core/config/envvar/schema_test.go @@ -13,6 +13,7 @@ func TestConfigSchema(t *testing.T) { items := map[string]string{ "AdvisoryLockCheckInterval": "ADVISORY_LOCK_CHECK_INTERVAL", "AdvisoryLockID": "ADVISORY_LOCK_ID", + "AuditLoggerEnabled": "AUDIT_LOGGER_ENABLED", "AuditLoggerForwardToUrl": "AUDIT_LOGGER_FORWARD_TO_URL", "AuditLoggerHeaders": "AUDIT_LOGGER_HEADERS", "AuditLoggerJsonWrapperKey": "AUDIT_LOGGER_JSON_WRAPPER_KEY", diff --git a/core/config/presenters.go b/core/config/presenters.go index 909c50f855d..09e2ae2e206 100644 --- a/core/config/presenters.go +++ b/core/config/presenters.go @@ -84,6 +84,9 @@ type EnvPrinter struct { LogFileMaxBackups int64 `json:"LOG_FILE_MAX_BACKUPS"` TriggerFallbackDBPollInterval time.Duration `json:"JOB_PIPELINE_DB_POLL_INTERVAL"` + // AuditLogger + AuditLoggerEnabled bool `json:"AUDIT_LOGGER_ENABLED"` + // OCR1 OCRContractTransmitterTransmitTimeout time.Duration `json:"OCR_CONTRACT_TRANSMITTER_TRANSMIT_TIMEOUT"` OCRDatabaseTimeout time.Duration `json:"OCR_DATABASE_TIMEOUT"` @@ -147,6 +150,7 @@ func NewConfigPrinter(cfg GeneralConfig) ConfigPrinter { ocrDatabaseTimeout, _ := cfg.GlobalOCRDatabaseTimeout() return ConfigPrinter{ EnvPrinter: EnvPrinter{ + AuditLoggerEnabled: cfg.AuditLoggerEnabled(), AdvisoryLockCheckInterval: cfg.AdvisoryLockCheckInterval(), AdvisoryLockID: cfg.AdvisoryLockID(), AllowOrigins: cfg.AllowOrigins(), From b577bf33a497dad32f4c44fa58b1e3a72c571cf8 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Wed, 5 Oct 2022 16:26:35 -0400 Subject: [PATCH 70/92] Change ID to name --- core/web/resolver/mutation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/web/resolver/mutation.go b/core/web/resolver/mutation.go index 2c98db35139..bf95c13535e 100644 --- a/core/web/resolver/mutation.go +++ b/core/web/resolver/mutation.go @@ -633,7 +633,7 @@ func (r *Resolver) DeleteNode(ctx context.Context, args struct { return nil, err } - r.App.GetAuditLogger().Audit(audit.ChainRpcNodeDeleted, map[string]interface{}{"id": id}) + r.App.GetAuditLogger().Audit(audit.ChainRpcNodeDeleted, map[string]interface{}{"name": name}) return NewDeleteNodePayloadResolver(&node, nil), nil } From d040a482672fbbb149120b4c4ea18133e1635773 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Wed, 5 Oct 2022 16:40:20 -0400 Subject: [PATCH 71/92] Fix mock error by stubbing. Needs review --- core/web/resolver/resolver_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go index be1bd866488..15010195416 100644 --- a/core/web/resolver/resolver_test.go +++ b/core/web/resolver/resolver_test.go @@ -53,6 +53,7 @@ type mocks struct { eIMgr *webhookmocks.ExternalInitiatorManager balM *evmORMMocks.BalanceMonitor txmORM *txmgrMocks.ORM + auditLogger *audit.AuditLoggerService } // gqlTestFramework is a framework wrapper containing the objects needed to run @@ -85,8 +86,6 @@ func setupFramework(t *testing.T) *gqlTestFramework { ctx = loader.InjectDataloader(testutils.Context(t), app) ) - app.Mock.On("GetAuditLogger", mock.Anything, mock.Anything).Return(&audit.AuditLoggerService{}).Maybe() - // Setup mocks // Note - If you add a new mock make sure you assert it's expectation below. m := &mocks{ @@ -112,6 +111,7 @@ func setupFramework(t *testing.T) *gqlTestFramework { eIMgr: webhookmocks.NewExternalInitiatorManager(t), balM: evmORMMocks.NewBalanceMonitor(t), txmORM: txmgrMocks.NewORM(t), + auditLogger: &audit.AuditLoggerService{}, } f := &gqlTestFramework{ From 1d3e3ae55ebe3dcb40091e4ec033ea4402f38703 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Wed, 5 Oct 2022 17:00:51 -0400 Subject: [PATCH 72/92] Fix missing import --- core/web/keys_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/web/keys_controller.go b/core/web/keys_controller.go index fddf963bf14..b4ea727d13d 100644 --- a/core/web/keys_controller.go +++ b/core/web/keys_controller.go @@ -1,7 +1,7 @@ package web import ( - + "fmt" "io" "net/http" From a3e8ea5c8bfecba5e0b1a7751f50fe08d56baae1 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Thu, 6 Oct 2022 17:30:43 -0400 Subject: [PATCH 73/92] Fix nil errors --- core/config/envvar/schema.go | 2 +- core/config/v2/types.go | 7 ++++ core/logger/audit/audit_logger.go | 24 +++++++++--- .../chainlink/cfgtest/dump/empty-strings.env | 5 +++ .../chainlink/cfgtest/dump/full-custom.toml | 6 +++ core/services/chainlink/config_dump.go | 37 +++++++++++++++++++ core/services/chainlink/config_test.go | 8 ++++ .../testdata/config-multi-chain.toml | 7 ++++ 8 files changed, 90 insertions(+), 6 deletions(-) diff --git a/core/config/envvar/schema.go b/core/config/envvar/schema.go index d7dde0f3c1d..a0fe4826212 100644 --- a/core/config/envvar/schema.go +++ b/core/config/envvar/schema.go @@ -63,7 +63,7 @@ type ConfigSchema struct { // Audit Logger AuditLoggerEnabled bool `env:"AUDIT_LOGGER_ENABLED" default:"false"` - AuditLoggerForwardToUrl string `env:"AUDIT_LOGGER_FORWARD_TO_URL" default:""` + AuditLoggerForwardToUrl string `env:"AUDIT_LOGGER_FORWARD_TO_URL" default:"http://localhost:9898"` AuditLoggerHeaders string `env:"AUDIT_LOGGER_HEADERS" default:""` AuditLoggerJsonWrapperKey string `env:"AUDIT_LOGGER_JSON_WRAPPER_KEY" default:""` diff --git a/core/config/v2/types.go b/core/config/v2/types.go index 4fed5320d48..921e8567fdb 100644 --- a/core/config/v2/types.go +++ b/core/config/v2/types.go @@ -95,6 +95,13 @@ func (c *Core) SetFrom(f *Core) { c.ShutdownGracePeriod = v } + if f.AuditLogger != nil { + if c.AuditLogger == nil { + c.AuditLogger = &audit.AuditLoggerConfig{} + } + c.AuditLogger.SetFrom(f.AuditLogger) + } + if f.Feature != nil { if c.Feature == nil { c.Feature = &Feature{} diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 46022ee34f2..bea2c9b091c 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -39,6 +39,25 @@ type AuditLoggerConfig struct { Headers *ServiceHeaders } +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.Environment; v != nil { + p.Environment = v + } + if v := f.JsonWrapperKey; v != nil { + p.JsonWrapperKey = v + } + if v := f.Headers; v != nil { + p.Headers = v + } + +} + type ServiceHeaders []ServiceHeader func (sh *ServiceHeaders) UnmarshalText(input []byte) error { @@ -101,11 +120,6 @@ type HTTPAuditLoggerInterface interface { Do(req *http.Request) (*http.Response, error) } -// environment := "production" -// if isDev { -// environment = "develop" -// } - type AuditLoggerService struct { logger logger.Logger // The standard logger configured in the node enabled bool // Whether the audit logger is enabled or not diff --git a/core/services/chainlink/cfgtest/dump/empty-strings.env b/core/services/chainlink/cfgtest/dump/empty-strings.env index bf7c29e144d..4cf74ecf4a4 100644 --- a/core/services/chainlink/cfgtest/dump/empty-strings.env +++ b/core/services/chainlink/cfgtest/dump/empty-strings.env @@ -1,5 +1,10 @@ DATABASE_URL= +AUDIT_LOGGER_ENABLED= +AUDIT_LOGGER_FORWARD_TO_URL= +AUDIT_LOGGER_HEADERS= +AUDIT_LOGGER_JSON_WRAPPER_KEY= + CHAIN_TYPE= CHAINLINK_DEV= EXPLORER_ACCESS_KEY= diff --git a/core/services/chainlink/cfgtest/dump/full-custom.toml b/core/services/chainlink/cfgtest/dump/full-custom.toml index 95603a3fe1b..879d25d6bc8 100644 --- a/core/services/chainlink/cfgtest/dump/full-custom.toml +++ b/core/services/chainlink/cfgtest/dump/full-custom.toml @@ -40,6 +40,12 @@ SendInterval = '10s' SendTimeout = '1m0s' UseBatchSend = false +[AuditLogger] +Enabled = false +ForwardToUrl = 'http://localhost:9898' +Environment = 'develop' +JsonWrapperKey = 'event' + [Log] DatabaseQueries = true JSONConsole = true diff --git a/core/services/chainlink/config_dump.go b/core/services/chainlink/config_dump.go index 4d85b8e3c93..7621785694a 100644 --- a/core/services/chainlink/config_dump.go +++ b/core/services/chainlink/config_dump.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink/core/chains/starknet" stktyp "github.com/smartcontractkit/chainlink/core/chains/starknet/types" "github.com/smartcontractkit/chainlink/core/chains/terra" + "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/assets" evmcfg "github.com/smartcontractkit/chainlink/core/chains/evm/config/v2" @@ -687,6 +688,18 @@ func (c *Config) loadLegacyCoreEnv() { c.Feature = nil } + c.AuditLogger = &audit.AuditLoggerConfig{ + Enabled: envvar.NewBool("AuditLoggerEnabled").ParsePtr(), + ForwardToUrl: envURL("AuditLoggerForwardToUrl"), + Environment: environment(), + JsonWrapperKey: envvar.NewString("AuditLoggerJsonWrapperKey").ParsePtr(), + Headers: serviceHeaders("AuditLoggerHeaders"), + } + + if isZeroPtr(c.AuditLogger) { + c.AuditLogger = nil + } + c.Database = &config.Database{ DefaultIdleInTxSessionTimeout: mustParseDuration(os.Getenv("DATABASE_DEFAULT_IDLE_IN_TX_SESSION_TIMEOUT")), DefaultLockTimeout: mustParseDuration(os.Getenv("DATABASE_DEFAULT_LOCK_TIMEOUT")), @@ -1010,6 +1023,30 @@ func envURL(s string) *models.URL { return nil } +func serviceHeaders(s string) *audit.ServiceHeaders { + return envvar.New(s, func(s string) (audit.ServiceHeaders, error) { + sh := make(audit.ServiceHeaders, 0) + err := sh.UnmarshalText([]byte(s)) + if err != nil { + return nil, nil + } + return sh, nil + }).ParsePtr() +} + +func environment() *string { + maybeDev := envvar.NewBool("Dev").ParsePtr() + if maybeDev == nil { + return nil + } + + environment := "develop" + if !*maybeDev { + environment = "production" + } + return &environment +} + func envIP(s string) *net.IP { ip := envvar.New(s, func(s string) (net.IP, error) { return net.ParseIP(s), nil diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 6733ab57646..f88bf6e8ed7 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -50,6 +50,14 @@ var ( Core: config.Core{ RootDir: ptr("my/root/dir"), + AuditLogger: &audit.AuditLoggerConfig{ + Enabled: ptr(false), + ForwardToUrl: mustURL("http://localhost:9898"), + Environment: ptr("develop"), + Headers: ptr(make(audit.ServiceHeaders, 0)), + JsonWrapperKey: ptr(""), + }, + Database: &config.Database{ Listener: &config.DatabaseListener{ FallbackPollInterval: models.MustNewDuration(2 * time.Minute), diff --git a/core/services/chainlink/testdata/config-multi-chain.toml b/core/services/chainlink/testdata/config-multi-chain.toml index 12b3418550a..220fe7c7213 100644 --- a/core/services/chainlink/testdata/config-multi-chain.toml +++ b/core/services/chainlink/testdata/config-multi-chain.toml @@ -4,6 +4,13 @@ RootDir = 'my/root/dir' [Database.Listener] FallbackPollInterval = '2m0s' +[AuditLogger] +Enabled = false +ForwardToUrl = 'http://localhost:9898' +Environment = 'develop' +JsonWrapperKey = '' +Headers = '' + [Log] JSONConsole = true From 3cb77a128fd3a23c49eec113cd0d164a4b511cf4 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Tue, 11 Oct 2022 13:00:06 -0400 Subject: [PATCH 74/92] fix tests. --- core/config/envvar/schema.go | 2 +- core/config/v2/docs/core.toml | 2 - core/logger/audit/audit_logger.go | 4 -- .../chainlink/cfgtest/dump/full-custom.toml | 1 - core/services/chainlink/config_dump.go | 1 - core/services/chainlink/config_general.go | 6 ++- core/services/chainlink/config_test.go | 3 -- .../testdata/config-empty-effective.toml | 6 +++ .../chainlink/testdata/config-full.toml | 1 - .../config-multi-chain-effective.toml | 6 +++ .../testdata/config-multi-chain.toml | 1 - core/services/mocks/config.go | 39 +++++++++++++++++++ core/web/resolver/config_test.go | 4 ++ core/web/resolver/resolver_test.go | 3 ++ docs/CONFIG.md | 7 ---- 15 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 core/services/mocks/config.go diff --git a/core/config/envvar/schema.go b/core/config/envvar/schema.go index 397ffffbeb2..60869266e43 100644 --- a/core/config/envvar/schema.go +++ b/core/config/envvar/schema.go @@ -63,7 +63,7 @@ type ConfigSchema struct { // Audit Logger AuditLoggerEnabled bool `env:"AUDIT_LOGGER_ENABLED" default:"false"` - AuditLoggerForwardToUrl string `env:"AUDIT_LOGGER_FORWARD_TO_URL" default:"http://localhost:9898"` + AuditLoggerForwardToUrl string `env:"AUDIT_LOGGER_FORWARD_TO_URL" default:""` AuditLoggerHeaders string `env:"AUDIT_LOGGER_HEADERS" default:""` AuditLoggerJsonWrapperKey string `env:"AUDIT_LOGGER_JSON_WRAPPER_KEY" default:""` diff --git a/core/config/v2/docs/core.toml b/core/config/v2/docs/core.toml index 4d4717f3d31..e8e89e1733e 100644 --- a/core/config/v2/docs/core.toml +++ b/core/config/v2/docs/core.toml @@ -110,8 +110,6 @@ UseBatchSend = true # Default Enabled = false # Default # ForwardToUrl is where you want to forward logs to ForwardToUrl = 'http://localhost:9898' # Example -# Environment is an identifier for the host/environment you're on -Environment = 'develop' # Example # JsonWrapperKey if set wraps the map of data under another single key to make parsing easier JsonWrapperKey = '' # Default # Headers is the set of headers you wish to pass along with each request diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index bea2c9b091c..966f4068f23 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -34,7 +34,6 @@ type AuditLogger interface { type AuditLoggerConfig struct { Enabled *bool ForwardToUrl *models.URL - Environment *string JsonWrapperKey *string Headers *ServiceHeaders } @@ -46,9 +45,6 @@ func (p *AuditLoggerConfig) SetFrom(f *AuditLoggerConfig) { if v := f.ForwardToUrl; v != nil { p.ForwardToUrl = v } - if v := f.Environment; v != nil { - p.Environment = v - } if v := f.JsonWrapperKey; v != nil { p.JsonWrapperKey = v } diff --git a/core/services/chainlink/cfgtest/dump/full-custom.toml b/core/services/chainlink/cfgtest/dump/full-custom.toml index 879d25d6bc8..64866ea3734 100644 --- a/core/services/chainlink/cfgtest/dump/full-custom.toml +++ b/core/services/chainlink/cfgtest/dump/full-custom.toml @@ -43,7 +43,6 @@ UseBatchSend = false [AuditLogger] Enabled = false ForwardToUrl = 'http://localhost:9898' -Environment = 'develop' JsonWrapperKey = 'event' [Log] diff --git a/core/services/chainlink/config_dump.go b/core/services/chainlink/config_dump.go index 66518d9c028..a5599d3bf07 100644 --- a/core/services/chainlink/config_dump.go +++ b/core/services/chainlink/config_dump.go @@ -697,7 +697,6 @@ func (c *Config) loadLegacyCoreEnv() { c.AuditLogger = &audit.AuditLoggerConfig{ Enabled: envvar.NewBool("AuditLoggerEnabled").ParsePtr(), ForwardToUrl: envURL("AuditLoggerForwardToUrl"), - Environment: environment(), JsonWrapperKey: envvar.NewString("AuditLoggerJsonWrapperKey").ParsePtr(), Headers: serviceHeaders("AuditLoggerHeaders"), } diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index fdad3bb99b3..532d262a879 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -210,7 +210,11 @@ func (g *generalConfig) AuditLoggerHeaders() (audit.ServiceHeaders, error) { } func (g *generalConfig) AuditLoggerEnvironment() string { - return *g.c.AuditLogger.Environment + if g.Dev() { + return "develop" + } else { + return "production" + } } func (g *generalConfig) AuditLoggerJsonWrapperKey() string { diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index f5798397567..5765796edac 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -54,7 +54,6 @@ var ( AuditLogger: &audit.AuditLoggerConfig{ Enabled: ptr(false), ForwardToUrl: mustURL("http://localhost:9898"), - Environment: ptr("develop"), Headers: ptr(make(audit.ServiceHeaders, 0)), JsonWrapperKey: ptr(""), }, @@ -222,7 +221,6 @@ func TestConfig_Marshal(t *testing.T) { full.AuditLogger = &audit.AuditLoggerConfig{ Enabled: ptr(false), ForwardToUrl: mustURL("http://localhost:9898"), - Environment: ptr("develop"), Headers: ptr(serviceHeaders), JsonWrapperKey: ptr(""), } @@ -604,7 +602,6 @@ ShutdownGracePeriod = '10s' {"AuditLogger", Config{Core: config.Core{AuditLogger: full.AuditLogger}}, `[AuditLogger] Enabled = false ForwardToUrl = 'http://localhost:9898' -Environment = 'develop' JsonWrapperKey = '' Headers = '' `}, diff --git a/core/services/chainlink/testdata/config-empty-effective.toml b/core/services/chainlink/testdata/config-empty-effective.toml index 0219a6204cc..74dd80c3d99 100644 --- a/core/services/chainlink/testdata/config-empty-effective.toml +++ b/core/services/chainlink/testdata/config-empty-effective.toml @@ -42,6 +42,12 @@ SendInterval = '500ms' SendTimeout = '10s' UseBatchSend = true +[AuditLogger] +Enabled = false +ForwardToUrl = '' +JsonWrapperKey = '' +Headers = '' + [Log] DatabaseQueries = false JSONConsole = false diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index ef9c61ed3b4..0999e1642e0 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -45,7 +45,6 @@ UseBatchSend = true [AuditLogger] Enabled = false ForwardToUrl = 'http://localhost:9898' -Environment = 'develop' JsonWrapperKey = '' Headers = '' diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index bc4848a59b0..cec657d6902 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -42,6 +42,12 @@ SendInterval = '500ms' SendTimeout = '10s' UseBatchSend = true +[AuditLogger] +Enabled = false +ForwardToUrl = 'http://localhost:9898' +JsonWrapperKey = '' +Headers = '' + [Log] DatabaseQueries = false JSONConsole = true diff --git a/core/services/chainlink/testdata/config-multi-chain.toml b/core/services/chainlink/testdata/config-multi-chain.toml index 220fe7c7213..92e8c303973 100644 --- a/core/services/chainlink/testdata/config-multi-chain.toml +++ b/core/services/chainlink/testdata/config-multi-chain.toml @@ -7,7 +7,6 @@ FallbackPollInterval = '2m0s' [AuditLogger] Enabled = false ForwardToUrl = 'http://localhost:9898' -Environment = 'develop' JsonWrapperKey = '' Headers = '' diff --git a/core/services/mocks/config.go b/core/services/mocks/config.go new file mode 100644 index 00000000000..13ec40108e6 --- /dev/null +++ b/core/services/mocks/config.go @@ -0,0 +1,39 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// Config is an autogenerated mock type for the Config type +type Config struct { + mock.Mock +} + +// LogSQL provides a mock function with given fields: +func (_m *Config) LogSQL() bool { + ret := _m.Called() + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +type mockConstructorTestingTNewConfig interface { + mock.TestingT + Cleanup(func()) +} + +// NewConfig creates a new instance of Config. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewConfig(t mockConstructorTestingTNewConfig) *Config { + mock := &Config{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/web/resolver/config_test.go b/core/web/resolver/config_test.go index a766a739894..c5e19573b3b 100644 --- a/core/web/resolver/config_test.go +++ b/core/web/resolver/config_test.go @@ -329,6 +329,10 @@ func TestResolver_Config(t *testing.T) { "key": "TRIGGER_FALLBACK_DB_POLL_INTERVAL", "value": "30s" }, + { + "key": "AUDIT_LOGGER_ENABLED", + "value": "false" + }, { "key": "OCR_DEFAULT_TRANSACTION_QUEUE_DEPTH", "value": "1" diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go index 15010195416..c9514e74dcc 100644 --- a/core/web/resolver/resolver_test.go +++ b/core/web/resolver/resolver_test.go @@ -8,6 +8,7 @@ import ( "github.com/graph-gophers/graphql-go" gqlerrors "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/gqltesting" + "github.com/stretchr/testify/mock" bridgeORMMocks "github.com/smartcontractkit/chainlink/core/bridges/mocks" evmConfigMocks "github.com/smartcontractkit/chainlink/core/chains/evm/config/mocks" @@ -114,6 +115,8 @@ func setupFramework(t *testing.T) *gqlTestFramework { auditLogger: &audit.AuditLoggerService{}, } + app.Mock.On("GetAuditLogger", mock.Anything, mock.Anything).Return(&audit.AuditLoggerService{}).Maybe() + f := &gqlTestFramework{ t: t, App: app, diff --git a/docs/CONFIG.md b/docs/CONFIG.md index d72c35ae23f..9f726d598b4 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -339,7 +339,6 @@ UseBatchSend toggles sending telemetry to the ingress server using the batch cli [AuditLogger] Enabled = false # Default ForwardToUrl = 'http://localhost:9898' # Example -Environment = 'develop' # Example JsonWrapperKey = '' # Default Headers = '' # Default ``` @@ -357,12 +356,6 @@ ForwardToUrl = 'http://localhost:9898' # Example ``` ForwardToUrl is where you want to forward logs to -### Environment -```toml -Environment = 'develop' # Example -``` -Environment is an identifier for the host/environment you're on - ### JsonWrapperKey ```toml JsonWrapperKey = '' # Default From 121e8b235cc5b78f1dfbb2f9f3a8c226d71ee653 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Tue, 11 Oct 2022 13:17:45 -0400 Subject: [PATCH 75/92] Fix last issues --- core/logger/audit/audit_logger.go | 2 ++ core/sessions/reaper_test.go | 2 +- core/web/eth_keys_controller.go | 9 ++++----- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 966f4068f23..7492af39e2b 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -143,6 +143,8 @@ type wrappedAuditLog struct { data Data } +var NoopLogger AuditLogger = &AuditLoggerService{} + // NewAuditLogger returns a buffer push system that ingests audit log events and // asynchronously pushes them up to an HTTP log service. // Parses and validates the AUDIT_LOGS_* environment values and returns an enabled diff --git a/core/sessions/reaper_test.go b/core/sessions/reaper_test.go index 85ba9ef9167..9dc65a0328d 100644 --- a/core/sessions/reaper_test.go +++ b/core/sessions/reaper_test.go @@ -34,7 +34,7 @@ func TestSessionReaper_ReapSessions(t *testing.T) { config := sessionReaperConfig{} lggr := logger.TestLogger(t) cfg := cltest.NewTestGeneralConfig(t) - orm := sessions.NewORM(db, config.SessionTimeout().Duration(), lggr, cfg, &audit.AuditLoggerService{}) + orm := sessions.NewORM(db, config.SessionTimeout().Duration(), lggr, cfg, audit.NoopLogger) r := sessions.NewSessionReaper(db.DB, config, lggr) defer r.Stop() diff --git a/core/web/eth_keys_controller.go b/core/web/eth_keys_controller.go index 240fd258bd2..12a226daab7 100644 --- a/core/web/eth_keys_controller.go +++ b/core/web/eth_keys_controller.go @@ -300,19 +300,18 @@ func (ekc *ETHKeysController) Import(c *gin.Context) { func (ekc *ETHKeysController) Export(c *gin.Context) { defer ekc.App.GetLogger().ErrorIfClosing(c.Request.Body, "Export request body") - address := c.Param("address") + id := c.Param("address") newPassword := c.Query("newpassword") - bytes, err := ekc.App.GetKeyStore().Eth().Export(address, newPassword) + bytes, err := ekc.App.GetKeyStore().Eth().Export(id, newPassword) if err != nil { jsonAPIError(c, http.StatusInternalServerError, err) return } ekc.App.GetAuditLogger().Audit(audit.KeyExported, map[string]interface{}{ - "type": "ethereum", - "id": "", // TODO: Figure out what the best way to fetch this is for consistency - "address": address, + "type": "ethereum", + "id": id, }) c.Data(http.StatusOK, MediaType, bytes) From 03bde974fd6ba39570952ecfdac636557db12c77 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Tue, 11 Oct 2022 14:22:36 -0400 Subject: [PATCH 76/92] Remove unused function --- core/services/chainlink/config_dump.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/core/services/chainlink/config_dump.go b/core/services/chainlink/config_dump.go index a5599d3bf07..5492f6deac7 100644 --- a/core/services/chainlink/config_dump.go +++ b/core/services/chainlink/config_dump.go @@ -1039,19 +1039,6 @@ func serviceHeaders(s string) *audit.ServiceHeaders { }).ParsePtr() } -func environment() *string { - maybeDev := envvar.NewBool("Dev").ParsePtr() - if maybeDev == nil { - return nil - } - - environment := "develop" - if !*maybeDev { - environment = "production" - } - return &environment -} - func envIP(s string) *net.IP { ip := envvar.New(s, func(s string) (net.IP, error) { return net.ParseIP(s), nil From 2b7c5e796b5fe2b11138df26c52d5b0080d44ae6 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Tue, 11 Oct 2022 15:56:58 -0400 Subject: [PATCH 77/92] Fix lint --- core/services/chainlink/config_general.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 532d262a879..89867c278e2 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -212,9 +212,8 @@ func (g *generalConfig) AuditLoggerHeaders() (audit.ServiceHeaders, error) { func (g *generalConfig) AuditLoggerEnvironment() string { if g.Dev() { return "develop" - } else { - return "production" } + return "production" } func (g *generalConfig) AuditLoggerJsonWrapperKey() string { From 19ceb14c91ee1c08b89256a5037625366f166f88 Mon Sep 17 00:00:00 2001 From: Mitchell Grenier Date: Tue, 11 Oct 2022 12:59:30 -0700 Subject: [PATCH 78/92] Update core/config/v2/types.go Co-authored-by: Jordan Krage --- core/config/v2/types.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/config/v2/types.go b/core/config/v2/types.go index 921e8567fdb..430b037a241 100644 --- a/core/config/v2/types.go +++ b/core/config/v2/types.go @@ -40,7 +40,8 @@ type Core struct { TelemetryIngress *TelemetryIngress AuditLogger *audit.AuditLoggerConfig - Log *Log + + Log *Log WebServer *WebServer From 1ea2d292a8f09c1a96bfbe53c5dd0bf02f099c26 Mon Sep 17 00:00:00 2001 From: Mitchell Grenier Date: Tue, 11 Oct 2022 12:59:38 -0700 Subject: [PATCH 79/92] Update core/web/resolver/resolver_test.go Co-authored-by: Jordan Krage --- core/web/resolver/resolver_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go index c9514e74dcc..7761ddc3d0d 100644 --- a/core/web/resolver/resolver_test.go +++ b/core/web/resolver/resolver_test.go @@ -115,7 +115,7 @@ func setupFramework(t *testing.T) *gqlTestFramework { auditLogger: &audit.AuditLoggerService{}, } - app.Mock.On("GetAuditLogger", mock.Anything, mock.Anything).Return(&audit.AuditLoggerService{}).Maybe() + app.Mock.On("GetAuditLogger", mock.Anything, mock.Anything).Return(audit.NopLogger).Maybe() f := &gqlTestFramework{ t: t, From 04de8e2871d25837f771f0f60a6c8ae8a5776bbf Mon Sep 17 00:00:00 2001 From: Mitchell Date: Tue, 11 Oct 2022 16:11:31 -0400 Subject: [PATCH 80/92] Fix naming --- core/web/resolver/resolver_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go index 7761ddc3d0d..44924ec7f7c 100644 --- a/core/web/resolver/resolver_test.go +++ b/core/web/resolver/resolver_test.go @@ -115,7 +115,7 @@ func setupFramework(t *testing.T) *gqlTestFramework { auditLogger: &audit.AuditLoggerService{}, } - app.Mock.On("GetAuditLogger", mock.Anything, mock.Anything).Return(audit.NopLogger).Maybe() + app.Mock.On("GetAuditLogger", mock.Anything, mock.Anything).Return(audit.NoopLogger).Maybe() f := &gqlTestFramework{ t: t, From bf8c3408a8c1705898fc768d24329886d093ed45 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Tue, 11 Oct 2022 16:58:49 -0400 Subject: [PATCH 81/92] Remove nil check, add extra JSON definitions --- core/config/presenters.go | 5 ++++- core/logger/audit/audit_logger.go | 6 +----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/core/config/presenters.go b/core/config/presenters.go index 09e2ae2e206..5eb1c623680 100644 --- a/core/config/presenters.go +++ b/core/config/presenters.go @@ -85,7 +85,10 @@ type EnvPrinter struct { TriggerFallbackDBPollInterval time.Duration `json:"JOB_PIPELINE_DB_POLL_INTERVAL"` // AuditLogger - AuditLoggerEnabled bool `json:"AUDIT_LOGGER_ENABLED"` + AuditLoggerEnabled bool `json:"AUDIT_LOGGER_ENABLED"` + AuditLoggerForwardToUrl string `json:"AUDIT_LOGGER_FORWARD_TO_URL"` + AuditLoggerJsonWrapperKey string `json:"AUDIT_LOGGER_JSON_WRAPPER_KEY"` + AuditLoggerHeaders string `json:"AUDIT_LOGGER_HEADERS"` // OCR1 OCRContractTransmitterTransmitTimeout time.Duration `json:"OCR_CONTRACT_TRANSMITTER_TRANSMIT_TIMEOUT"` diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 7492af39e2b..b30a869f129 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -216,11 +216,7 @@ func (l *AuditLoggerService) Audit(eventID EventID, data Data) { select { case l.loggingChannel <- wrappedLog: default: - if l.loggingChannel == nil { - l.logger.Errorw("could not send log to audit subsystem because it has gone away!") - } else { - l.logger.Errorf("buffer is full. Dropping log with eventID: %s", eventID) - } + l.logger.Errorf("buffer is full. Dropping log with eventID: %s", eventID) } } From d18a57dabcea92a4b394110da940fd8fcbbccf41 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Tue, 11 Oct 2022 19:14:33 -0400 Subject: [PATCH 82/92] Add example --- core/services/chainlink/testdata/config-full.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 0999e1642e0..9dc4921d6b2 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -45,8 +45,8 @@ UseBatchSend = true [AuditLogger] Enabled = false ForwardToUrl = 'http://localhost:9898' -JsonWrapperKey = '' -Headers = '' +JsonWrapperKey = 'event' +Headers = 'Authorization||a token here\\Some-Other-Header||other header data here' [Log] DatabaseQueries = true From 4688dbbd3d23bcba7c02dce60aa78024fbaa9bc1 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Wed, 12 Oct 2022 11:41:21 -0400 Subject: [PATCH 83/92] Do some more header validation --- core/logger/audit/audit_logger.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index b30a869f129..54e1c8006ff 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -9,6 +9,7 @@ import ( "net/http" "net/url" "os" + "regexp" "strings" "time" @@ -62,6 +63,12 @@ func (sh *ServiceHeaders) UnmarshalText(input []byte) error { } headers := string(input) + // 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 + headerNameRegex, _ := regexp.Compile("^[A-Za-z\\-]+$") + headerValueRegex, _ := regexp.Compile("^[A-Za-z_ :;.,\\/\"'?!(){}[\\]@<>=\\-+*#$&`|~^%]+$") parsed_headers := []ServiceHeader{} if headers != "" { @@ -71,9 +78,19 @@ func (sh *ServiceHeaders) UnmarshalText(input []byte) error { if len(keyValue) != 2 { return errors.Errorf("invalid headers provided for the audit logger. Value, single pair split on || required, got: %s", keyValue) } + header := keyValue[0] + value := keyValue[1] + + if !headerNameRegex.MatchString(header) { + return errors.Errorf("invalid header name: %s", header) + } + + if !headerValueRegex.MatchString(value) { + return errors.Errorf("invalid header value: %s", value) + } parsed_headers = append(parsed_headers, ServiceHeader{ - Header: keyValue[0], - Value: keyValue[1], + Header: header, + Value: value, }) } } From 6dd7102263cf66227aad6ea909c2bff102ee2e20 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Wed, 12 Oct 2022 11:45:58 -0400 Subject: [PATCH 84/92] Add examples --- core/config/v2/docs/core.toml | 4 ++-- core/services/chainlink/cfgtest/dump/full-custom.env | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/config/v2/docs/core.toml b/core/config/v2/docs/core.toml index 9c63b9c0d45..13805bbf7fa 100644 --- a/core/config/v2/docs/core.toml +++ b/core/config/v2/docs/core.toml @@ -111,9 +111,9 @@ Enabled = false # Default # ForwardToUrl is where you want to forward logs to ForwardToUrl = 'http://localhost:9898' # Example # JsonWrapperKey if set wraps the map of data under another single key to make parsing easier -JsonWrapperKey = '' # Default +JsonWrapperKey = '' # Example # Headers is the set of headers you wish to pass along with each request -Headers = '' # Default +Headers = '' # Example [Log] # DatabaseQueries tells the Chainlink node to log database queries made using the default logger. SQL statements will be logged at `debug` level. Not all statements can be logged. The best way to get a true log of all SQL statements is to enable SQL statement logging on Postgres. diff --git a/core/services/chainlink/cfgtest/dump/full-custom.env b/core/services/chainlink/cfgtest/dump/full-custom.env index d11c1d508ba..03c45335257 100644 --- a/core/services/chainlink/cfgtest/dump/full-custom.env +++ b/core/services/chainlink/cfgtest/dump/full-custom.env @@ -33,10 +33,10 @@ DATABASE_LOCKING_MODE=advisory LEASE_LOCK_DURATION=5s LEASE_LOCK_REFRESH_INTERVAL=2s -AUDIT_LOGGER_ENABLED=false +AUDIT_LOGGER_ENABLED=true AUDIT_LOGGER_FORWARD_TO_URL=http://localhost:9898 AUDIT_LOGGER_JSON_WRAPPER_KEY=event -AUDIT_LOGGER_HEADERS= +AUDIT_LOGGER_HEADERS="Authorization||token\\X-SomeOther-Header||value with spaces | and a bar+*" DATABASE_BACKUP_DIR=db/backup DATABASE_BACKUP_FREQUENCY=10m From fc83c1e39ffec79f645d8858b0875bfbd461c001 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Wed, 12 Oct 2022 11:52:14 -0400 Subject: [PATCH 85/92] Modify core.toml --- core/config/v2/docs/core.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/config/v2/docs/core.toml b/core/config/v2/docs/core.toml index 13805bbf7fa..aff9fbb8b5d 100644 --- a/core/config/v2/docs/core.toml +++ b/core/config/v2/docs/core.toml @@ -111,9 +111,9 @@ Enabled = false # Default # ForwardToUrl is where you want to forward logs to ForwardToUrl = 'http://localhost:9898' # Example # JsonWrapperKey if set wraps the map of data under another single key to make parsing easier -JsonWrapperKey = '' # Example +JsonWrapperKey = 'event' # Example # Headers is the set of headers you wish to pass along with each request -Headers = '' # Example +Headers = 'Authorization||token\\X-SomeOther-Header||value with spaces | and a bar+*' # Example [Log] # DatabaseQueries tells the Chainlink node to log database queries made using the default logger. SQL statements will be logged at `debug` level. Not all statements can be logged. The best way to get a true log of all SQL statements is to enable SQL statement logging on Postgres. From f238117f001d4a91701dbf52197a638506caa853 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Wed, 12 Oct 2022 12:19:09 -0400 Subject: [PATCH 86/92] Raw string for a regex --- core/logger/audit/audit_logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 54e1c8006ff..82f5700d043 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -67,7 +67,7 @@ func (sh *ServiceHeaders) UnmarshalText(input []byte) error { // technically allow instead following the guidelines of // cloudflare transforms. // https://developers.cloudflare.com/rules/transform/request-header-modification/reference/header-format - headerNameRegex, _ := regexp.Compile("^[A-Za-z\\-]+$") + headerNameRegex, _ := regexp.Compile(`^[A-Za-z\-]+$`) headerValueRegex, _ := regexp.Compile("^[A-Za-z_ :;.,\\/\"'?!(){}[\\]@<>=\\-+*#$&`|~^%]+$") parsed_headers := []ServiceHeader{} From aad806950dcd35cd1383066226a516a71589fe5b Mon Sep 17 00:00:00 2001 From: Mitchell Date: Wed, 12 Oct 2022 14:28:16 -0400 Subject: [PATCH 87/92] Updates to tests --- core/config/v2/docs/core.toml | 2 +- .../chainlink/cfgtest/dump/full-custom.env | 2 +- .../chainlink/cfgtest/dump/full-custom.toml | 3 ++- core/services/chainlink/config_test.go | 19 ++++++++++++++----- .../chainlink/testdata/config-full.toml | 6 +++--- .../config-multi-chain-effective.toml | 2 +- .../testdata/config-multi-chain.toml | 6 +++--- core/web/resolver/config_test.go | 12 ++++++++++++ docs/CONFIG.md | 8 ++++---- 9 files changed, 41 insertions(+), 19 deletions(-) diff --git a/core/config/v2/docs/core.toml b/core/config/v2/docs/core.toml index aff9fbb8b5d..7f224a7db5e 100644 --- a/core/config/v2/docs/core.toml +++ b/core/config/v2/docs/core.toml @@ -113,7 +113,7 @@ ForwardToUrl = 'http://localhost:9898' # Example # JsonWrapperKey if set wraps the map of data under another single key to make parsing easier JsonWrapperKey = 'event' # Example # Headers is the set of headers you wish to pass along with each request -Headers = 'Authorization||token\\X-SomeOther-Header||value with spaces | and a bar+*' # Example +Headers = 'Authorization||token\X-SomeOther-Header||value with spaces | and a bar+*' # Example [Log] # DatabaseQueries tells the Chainlink node to log database queries made using the default logger. SQL statements will be logged at `debug` level. Not all statements can be logged. The best way to get a true log of all SQL statements is to enable SQL statement logging on Postgres. diff --git a/core/services/chainlink/cfgtest/dump/full-custom.env b/core/services/chainlink/cfgtest/dump/full-custom.env index 03c45335257..e9155179f65 100644 --- a/core/services/chainlink/cfgtest/dump/full-custom.env +++ b/core/services/chainlink/cfgtest/dump/full-custom.env @@ -36,7 +36,7 @@ LEASE_LOCK_REFRESH_INTERVAL=2s AUDIT_LOGGER_ENABLED=true AUDIT_LOGGER_FORWARD_TO_URL=http://localhost:9898 AUDIT_LOGGER_JSON_WRAPPER_KEY=event -AUDIT_LOGGER_HEADERS="Authorization||token\\X-SomeOther-Header||value with spaces | and a bar+*" +AUDIT_LOGGER_HEADERS="Authorization||token\X-SomeOther-Header||value with spaces | and a bar+*" DATABASE_BACKUP_DIR=db/backup DATABASE_BACKUP_FREQUENCY=10m diff --git a/core/services/chainlink/cfgtest/dump/full-custom.toml b/core/services/chainlink/cfgtest/dump/full-custom.toml index 64866ea3734..319540ceee5 100644 --- a/core/services/chainlink/cfgtest/dump/full-custom.toml +++ b/core/services/chainlink/cfgtest/dump/full-custom.toml @@ -41,9 +41,10 @@ SendTimeout = '1m0s' UseBatchSend = false [AuditLogger] -Enabled = false +Enabled = true ForwardToUrl = 'http://localhost:9898' JsonWrapperKey = 'event' +Headers = '' [Log] DatabaseQueries = true diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index e46a5d8a0b0..0c928f735a2 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -52,10 +52,19 @@ var ( RootDir: ptr("my/root/dir"), AuditLogger: &audit.AuditLoggerConfig{ - Enabled: ptr(false), - ForwardToUrl: mustURL("http://localhost:9898"), - Headers: ptr(make(audit.ServiceHeaders, 0)), - JsonWrapperKey: ptr(""), + Enabled: ptr(false), + ForwardToUrl: mustURL("http://localhost:9898"), + Headers: ptr(audit.ServiceHeaders{ + audit.ServiceHeader{ + Header: "Authorization", + Value: "a token here", + }, + audit.ServiceHeader{ + Header: "Some-Other-Header", + Value: "other header data here", + }, + }), + JsonWrapperKey: ptr("event"), }, Database: &config.Database{ @@ -600,7 +609,7 @@ RootDir = 'test/root/dir' ShutdownGracePeriod = '10s' `}, {"AuditLogger", Config{Core: config.Core{AuditLogger: full.AuditLogger}}, `[AuditLogger] -Enabled = false +Enabled = true ForwardToUrl = 'http://localhost:9898' JsonWrapperKey = '' Headers = '' diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 9dc4921d6b2..38a141d9f60 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -43,10 +43,10 @@ SendTimeout = '5s' UseBatchSend = true [AuditLogger] -Enabled = false +Enabled = true ForwardToUrl = 'http://localhost:9898' -JsonWrapperKey = 'event' -Headers = 'Authorization||a token here\\Some-Other-Header||other header data here' +JsonWrapperKey = '' +Headers = '' [Log] DatabaseQueries = true diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index a14dd3f1b71..ed44cb0d582 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -45,7 +45,7 @@ UseBatchSend = true [AuditLogger] Enabled = false ForwardToUrl = 'http://localhost:9898' -JsonWrapperKey = '' +JsonWrapperKey = 'event' Headers = '' [Log] diff --git a/core/services/chainlink/testdata/config-multi-chain.toml b/core/services/chainlink/testdata/config-multi-chain.toml index 92e8c303973..398ea751166 100644 --- a/core/services/chainlink/testdata/config-multi-chain.toml +++ b/core/services/chainlink/testdata/config-multi-chain.toml @@ -5,10 +5,10 @@ RootDir = 'my/root/dir' FallbackPollInterval = '2m0s' [AuditLogger] -Enabled = false +Enabled = true ForwardToUrl = 'http://localhost:9898' -JsonWrapperKey = '' -Headers = '' +JsonWrapperKey = 'event' +Headers = 'Authorization||a token here\Some-Other-Header||other header data here' [Log] JSONConsole = true diff --git a/core/web/resolver/config_test.go b/core/web/resolver/config_test.go index c5e19573b3b..01611bca90c 100644 --- a/core/web/resolver/config_test.go +++ b/core/web/resolver/config_test.go @@ -333,6 +333,18 @@ func TestResolver_Config(t *testing.T) { "key": "AUDIT_LOGGER_ENABLED", "value": "false" }, + { + "key": "AUDIT_LOGGER_FORWARD_TO_URL", + "value": "" + }, + { + "key": "AUDIT_LOGGER_JSON_WRAPPER_KEY", + "value": "" + }, + { + "key": "AUDIT_LOGGER_HEADERS", + "value": "" + }, { "key": "OCR_DEFAULT_TRANSACTION_QUEUE_DEPTH", "value": "1" diff --git a/docs/CONFIG.md b/docs/CONFIG.md index a2e54187a41..a49e2aabd54 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -339,8 +339,8 @@ UseBatchSend toggles sending telemetry to the ingress server using the batch cli [AuditLogger] Enabled = false # Default ForwardToUrl = 'http://localhost:9898' # Example -JsonWrapperKey = '' # Default -Headers = '' # Default +JsonWrapperKey = 'event' # Example +Headers = 'Authorization||token\X-SomeOther-Header||value with spaces | and a bar+*' # Example ``` @@ -358,13 +358,13 @@ ForwardToUrl is where you want to forward logs to ### JsonWrapperKey ```toml -JsonWrapperKey = '' # Default +JsonWrapperKey = 'event' # Example ``` JsonWrapperKey if set wraps the map of data under another single key to make parsing easier ### Headers ```toml -Headers = '' # Default +Headers = 'Authorization||token\X-SomeOther-Header||value with spaces | and a bar+*' # Example ``` Headers is the set of headers you wish to pass along with each request From a7e891a0ab01bd8dd40a8f7f69d1c01544feb44f Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 12 Oct 2022 14:01:11 -0500 Subject: [PATCH 88/92] cleanup --- .../chainlink/cfgtest/dump/full-custom.env | 2 +- .../chainlink/cfgtest/dump/full-custom.toml | 2 +- core/services/chainlink/config_dump.go | 2 +- core/services/chainlink/config_test.go | 21 +++++++++++-------- .../chainlink/testdata/config-full.toml | 4 ++-- .../config-multi-chain-effective.toml | 4 ++-- .../testdata/config-multi-chain.toml | 2 +- 7 files changed, 20 insertions(+), 17 deletions(-) diff --git a/core/services/chainlink/cfgtest/dump/full-custom.env b/core/services/chainlink/cfgtest/dump/full-custom.env index e9155179f65..be5cb42420c 100644 --- a/core/services/chainlink/cfgtest/dump/full-custom.env +++ b/core/services/chainlink/cfgtest/dump/full-custom.env @@ -36,7 +36,7 @@ LEASE_LOCK_REFRESH_INTERVAL=2s AUDIT_LOGGER_ENABLED=true AUDIT_LOGGER_FORWARD_TO_URL=http://localhost:9898 AUDIT_LOGGER_JSON_WRAPPER_KEY=event -AUDIT_LOGGER_HEADERS="Authorization||token\X-SomeOther-Header||value with spaces | and a bar+*" +AUDIT_LOGGER_HEADERS=Authorization||token\X-SomeOther-Header||value with spaces | and a bar+* DATABASE_BACKUP_DIR=db/backup DATABASE_BACKUP_FREQUENCY=10m diff --git a/core/services/chainlink/cfgtest/dump/full-custom.toml b/core/services/chainlink/cfgtest/dump/full-custom.toml index 319540ceee5..a32400de53f 100644 --- a/core/services/chainlink/cfgtest/dump/full-custom.toml +++ b/core/services/chainlink/cfgtest/dump/full-custom.toml @@ -44,7 +44,7 @@ UseBatchSend = false Enabled = true ForwardToUrl = 'http://localhost:9898' JsonWrapperKey = 'event' -Headers = '' +Headers = 'Authorization||token\X-SomeOther-Header||value with spaces | and a bar+*' [Log] DatabaseQueries = true diff --git a/core/services/chainlink/config_dump.go b/core/services/chainlink/config_dump.go index e03d2dbdae1..2872de8a595 100644 --- a/core/services/chainlink/config_dump.go +++ b/core/services/chainlink/config_dump.go @@ -1015,7 +1015,7 @@ func serviceHeaders(s string) *audit.ServiceHeaders { sh := make(audit.ServiceHeaders, 0) err := sh.UnmarshalText([]byte(s)) if err != nil { - return nil, nil + return nil, err } return sh, nil }).ParsePtr() diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 0c928f735a2..80f6cdbf19a 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -52,16 +52,16 @@ var ( RootDir: ptr("my/root/dir"), AuditLogger: &audit.AuditLoggerConfig{ - Enabled: ptr(false), + Enabled: ptr(true), ForwardToUrl: mustURL("http://localhost:9898"), Headers: ptr(audit.ServiceHeaders{ audit.ServiceHeader{ Header: "Authorization", - Value: "a token here", + Value: "token", }, audit.ServiceHeader{ - Header: "Some-Other-Header", - Value: "other header data here", + Header: "X-SomeOther-Header", + Value: "value with spaces | and a bar+*", }, }), JsonWrapperKey: ptr("event"), @@ -226,12 +226,15 @@ func TestConfig_Marshal(t *testing.T) { full := global - serviceHeaders := make(audit.ServiceHeaders, 0) + serviceHeaders := audit.ServiceHeaders{ + {"Authorization", "token"}, + {"X-SomeOther-Header", "value with spaces | and a bar+*"}, + } full.AuditLogger = &audit.AuditLoggerConfig{ - Enabled: ptr(false), + Enabled: ptr(true), ForwardToUrl: mustURL("http://localhost:9898"), Headers: ptr(serviceHeaders), - JsonWrapperKey: ptr(""), + JsonWrapperKey: ptr("event"), } full.Feature = &config.Feature{ @@ -611,8 +614,8 @@ ShutdownGracePeriod = '10s' {"AuditLogger", Config{Core: config.Core{AuditLogger: full.AuditLogger}}, `[AuditLogger] Enabled = true ForwardToUrl = 'http://localhost:9898' -JsonWrapperKey = '' -Headers = '' +JsonWrapperKey = 'event' +Headers = 'Authorization||token\X-SomeOther-Header||value with spaces | and a bar+*' `}, {"Feature", Config{Core: config.Core{Feature: full.Feature}}, `[Feature] FeedsManager = true diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 38a141d9f60..c35ec420464 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -45,8 +45,8 @@ UseBatchSend = true [AuditLogger] Enabled = true ForwardToUrl = 'http://localhost:9898' -JsonWrapperKey = '' -Headers = '' +JsonWrapperKey = 'event' +Headers = 'Authorization||token\X-SomeOther-Header||value with spaces | and a bar+*' [Log] DatabaseQueries = true diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index ed44cb0d582..74f3a6c8982 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -43,10 +43,10 @@ SendTimeout = '10s' UseBatchSend = true [AuditLogger] -Enabled = false +Enabled = true ForwardToUrl = 'http://localhost:9898' JsonWrapperKey = 'event' -Headers = '' +Headers = 'Authorization||token\X-SomeOther-Header||value with spaces | and a bar+*' [Log] DatabaseQueries = false diff --git a/core/services/chainlink/testdata/config-multi-chain.toml b/core/services/chainlink/testdata/config-multi-chain.toml index 398ea751166..c2bdfff77a1 100644 --- a/core/services/chainlink/testdata/config-multi-chain.toml +++ b/core/services/chainlink/testdata/config-multi-chain.toml @@ -8,7 +8,7 @@ FallbackPollInterval = '2m0s' Enabled = true ForwardToUrl = 'http://localhost:9898' JsonWrapperKey = 'event' -Headers = 'Authorization||a token here\Some-Other-Header||other header data here' +Headers = 'Authorization||token\X-SomeOther-Header||value with spaces | and a bar+*' [Log] JSONConsole = true From 24e75bc263d0993612cf29b853bcf864175e899e Mon Sep 17 00:00:00 2001 From: Mitchell Date: Wed, 12 Oct 2022 15:11:59 -0400 Subject: [PATCH 89/92] Fix service headers --- core/services/chainlink/config_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 80f6cdbf19a..1867566a460 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -227,8 +227,8 @@ func TestConfig_Marshal(t *testing.T) { full := global serviceHeaders := audit.ServiceHeaders{ - {"Authorization", "token"}, - {"X-SomeOther-Header", "value with spaces | and a bar+*"}, + {Header: "Authorization", Value: "token"}, + {Header: "X-SomeOther-Header", Value: "value with spaces | and a bar+*"}, } full.AuditLogger = &audit.AuditLoggerConfig{ Enabled: ptr(true), From a47bd5b8cc755a767fa9d44ec785919e92e458e9 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 12 Oct 2022 14:16:23 -0500 Subject: [PATCH 90/92] align ServiceHeaders --- core/config/general_config.go | 10 +++++----- core/logger/audit/audit_logger.go | 16 +++++++++++++--- core/services/chainlink/config_dump.go | 13 +------------ 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/core/config/general_config.go b/core/config/general_config.go index 374e7754160..4c70cebacee 100644 --- a/core/config/general_config.go +++ b/core/config/general_config.go @@ -517,11 +517,11 @@ func (c *generalConfig) AuditLoggerJsonWrapperKey() string { } func (c *generalConfig) AuditLoggerHeaders() (audit.ServiceHeaders, error) { - headers := c.viper.GetString(envvar.Name("AuditLoggerHeaders")) - serviceHeaders := audit.ServiceHeaders{} - err := serviceHeaders.UnmarshalText([]byte(headers)) - - return serviceHeaders, err + sh, invalid := audit.AuditLoggerHeaders.Parse() + if invalid != "" { + return nil, errors.New(invalid) + } + return sh, nil } // AuthenticatedRateLimit defines the threshold to which authenticated requests diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 82f5700d043..4f6feaab70f 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -13,6 +13,7 @@ import ( "strings" "time" + "github.com/smartcontractkit/chainlink/core/config/envvar" "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services" "github.com/smartcontractkit/chainlink/core/store/models" @@ -70,7 +71,7 @@ func (sh *ServiceHeaders) UnmarshalText(input []byte) error { headerNameRegex, _ := regexp.Compile(`^[A-Za-z\-]+$`) headerValueRegex, _ := regexp.Compile("^[A-Za-z_ :;.,\\/\"'?!(){}[\\]@<>=\\-+*#$&`|~^%]+$") - parsed_headers := []ServiceHeader{} + var parsedHeaders []ServiceHeader if headers != "" { headerLines := strings.Split(headers, "\\") for _, header := range headerLines { @@ -88,14 +89,14 @@ func (sh *ServiceHeaders) UnmarshalText(input []byte) error { if !headerValueRegex.MatchString(value) { return errors.Errorf("invalid header value: %s", value) } - parsed_headers = append(parsed_headers, ServiceHeader{ + parsedHeaders = append(parsedHeaders, ServiceHeader{ Header: header, Value: value, }) } } - *sh = parsed_headers + *sh = parsedHeaders return nil } @@ -121,6 +122,15 @@ func (sh *ServiceHeaders) MarshalText() ([]byte, error) { return []byte(serialized), nil } +var AuditLoggerHeaders = envvar.New("AuditLoggerHeaders", func(s string) (ServiceHeaders, error) { + sh := make(ServiceHeaders, 0) + err := sh.UnmarshalText([]byte(s)) + if err != nil { + return nil, err + } + return sh, nil +}) + type Config interface { AuditLoggerEnabled() bool AuditLoggerForwardToUrl() (models.URL, error) diff --git a/core/services/chainlink/config_dump.go b/core/services/chainlink/config_dump.go index 2872de8a595..3b53f11054e 100644 --- a/core/services/chainlink/config_dump.go +++ b/core/services/chainlink/config_dump.go @@ -680,7 +680,7 @@ func (c *Config) loadLegacyCoreEnv() { Enabled: envvar.NewBool("AuditLoggerEnabled").ParsePtr(), ForwardToUrl: envURL("AuditLoggerForwardToUrl"), JsonWrapperKey: envvar.NewString("AuditLoggerJsonWrapperKey").ParsePtr(), - Headers: serviceHeaders("AuditLoggerHeaders"), + Headers: audit.AuditLoggerHeaders.ParsePtr(), } if isZeroPtr(c.AuditLogger) { @@ -1010,17 +1010,6 @@ func envURL(s string) *models.URL { return nil } -func serviceHeaders(s string) *audit.ServiceHeaders { - return envvar.New(s, func(s string) (audit.ServiceHeaders, error) { - sh := make(audit.ServiceHeaders, 0) - err := sh.UnmarshalText([]byte(s)) - if err != nil { - return nil, err - } - return sh, nil - }).ParsePtr() -} - func envIP(s string) *net.IP { ip := envvar.New(s, func(s string) (net.IP, error) { return net.ParseIP(s), nil From 5c480e5d179242845241215c93f1dc0485eb1369 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 12 Oct 2022 15:35:48 -0500 Subject: [PATCH 91/92] skip exceptional method --- core/cmd/cfgtest/app_test.go | 1 + core/cmd/local_client_test.go | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/core/cmd/cfgtest/app_test.go b/core/cmd/cfgtest/app_test.go index a12298bc22a..069249a9598 100644 --- a/core/cmd/cfgtest/app_test.go +++ b/core/cmd/cfgtest/app_test.go @@ -47,6 +47,7 @@ func TestDefaultConfig(t *testing.T) { "LogFileDir", "RootDir", "TLSDir", + "AuditLoggerEnvironment", // same problem being derived from Dev() } assertMethodsReturnEqual[config.GeneralConfig](t, configtest.NewTestGeneralConfig(t), configtest2.NewTestGeneralConfig(t), testRoot) }) diff --git a/core/cmd/local_client_test.go b/core/cmd/local_client_test.go index 4672ce1cec4..0d871929f47 100644 --- a/core/cmd/local_client_test.go +++ b/core/cmd/local_client_test.go @@ -168,6 +168,10 @@ LOG_FILE_MAX_SIZE: 5.12gb LOG_FILE_MAX_AGE: 0 LOG_FILE_MAX_BACKUPS: 1 TRIGGER_FALLBACK_DB_POLL_INTERVAL: 30s +AUDIT_LOGGER_ENABLED: false +AUDIT_LOGGER_FORWARD_TO_URL: +AUDIT_LOGGER_JSON_WRAPPER_KEY: +AUDIT_LOGGER_HEADERS: OCR_CONTRACT_TRANSMITTER_TRANSMIT_TIMEOUT: OCR_DATABASE_TIMEOUT: OCR_DEFAULT_TRANSACTION_QUEUE_DEPTH: 1 From f1bd29c4a1c140b8f49b9240f43ebc1aa1ea259d Mon Sep 17 00:00:00 2001 From: Mitchell Date: Thu, 13 Oct 2022 09:49:11 -0400 Subject: [PATCH 92/92] Noop Logger --- core/cmd/client_test.go | 8 ++++---- core/cmd/local_client_test.go | 6 +++--- core/internal/cltest/cltest.go | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/cmd/client_test.go b/core/cmd/client_test.go index 448e5036135..42441cdae90 100644 --- a/core/cmd/client_test.go +++ b/core/cmd/client_test.go @@ -136,7 +136,7 @@ func TestTerminalAPIInitializer_InitializeWithoutAPIUser(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { db := pgtest.NewSqlxDB(t) - orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cltest.NewTestGeneralConfig(t), &audit.AuditLoggerService{}) + orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cltest.NewTestGeneralConfig(t), audit.NoopLogger) mock := &cltest.MockCountingPrompter{T: t, EnteredStrings: test.enteredStrings, NotTerminal: !test.isTerminal} tai := cmd.NewPromptingAPIInitializer(mock, logger.TestLogger(t)) @@ -166,7 +166,7 @@ func TestTerminalAPIInitializer_InitializeWithoutAPIUser(t *testing.T) { func TestTerminalAPIInitializer_InitializeWithExistingAPIUser(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := cltest.NewTestGeneralConfig(t) - orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cfg, &audit.AuditLoggerService{}) + orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cfg, audit.NoopLogger) // Clear out fixture users/users created from the other test cases // This asserts that on initial run with an empty users table that the credentials file will instantiate and @@ -202,7 +202,7 @@ func TestFileAPIInitializer_InitializeWithoutAPIUser(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { db := pgtest.NewSqlxDB(t) - orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cltest.NewTestGeneralConfig(t), &audit.AuditLoggerService{}) + orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cltest.NewTestGeneralConfig(t), audit.NoopLogger) // Clear out fixture users/users created from the other test cases // This asserts that on initial run with an empty users table that the credentials file will instantiate and @@ -227,7 +227,7 @@ func TestFileAPIInitializer_InitializeWithoutAPIUser(t *testing.T) { func TestFileAPIInitializer_InitializeWithExistingAPIUser(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := cltest.NewTestGeneralConfig(t) - orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cfg, &audit.AuditLoggerService{}) + orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cfg, audit.NoopLogger) tests := []struct { name string diff --git a/core/cmd/local_client_test.go b/core/cmd/local_client_test.go index 0d871929f47..a874c804511 100644 --- a/core/cmd/local_client_test.go +++ b/core/cmd/local_client_test.go @@ -52,7 +52,7 @@ func TestClient_RunNodeShowsEnv(t *testing.T) { db := pgtest.NewSqlxDB(t) pCfg := cltest.NewTestGeneralConfig(t) - sessionORM := sessions.NewORM(db, time.Minute, lggr, pCfg, &audit.AuditLoggerService{}) + sessionORM := sessions.NewORM(db, time.Minute, lggr, pCfg, audit.NoopLogger) keyStore := cltest.NewKeyStore(t, db, cfg) _, err := keyStore.Eth().Create(&cltest.FixtureChainID) require.NoError(t, err) @@ -225,7 +225,7 @@ func TestClient_RunNodeWithPasswords(t *testing.T) { cfg := cltest.NewTestGeneralConfig(t) db := pgtest.NewSqlxDB(t) keyStore := cltest.NewKeyStore(t, db, cfg) - sessionORM := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cltest.NewTestGeneralConfig(t), &audit.AuditLoggerService{}) + sessionORM := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cltest.NewTestGeneralConfig(t), audit.NoopLogger) // Purge the fixture users to test assumption of single admin // initialUser user created above @@ -287,7 +287,7 @@ func TestClient_RunNodeWithAPICredentialsFile(t *testing.T) { t.Run(test.name, func(t *testing.T) { cfg := cltest.NewTestGeneralConfig(t) db := pgtest.NewSqlxDB(t) - sessionORM := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cltest.NewTestGeneralConfig(t), &audit.AuditLoggerService{}) + sessionORM := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cltest.NewTestGeneralConfig(t), audit.NoopLogger) // Clear out fixture users/users created from the other test cases // This asserts that on initial run with an empty users table that the credentials file will instantiate and diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index c94172d67b7..488a38945f5 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -370,7 +370,7 @@ func NewApplicationWithConfig(t testing.TB, cfg config.GeneralConfig, flagsAndDe } if auditLogger == nil { - auditLogger = &audit.AuditLoggerService{} + auditLogger = audit.NoopLogger } var eventBroadcaster pg.EventBroadcaster = pg.NewNullEventBroadcaster()