Skip to content

Commit

Permalink
Added new config var enable_key_logging added (TykTechnologies#1301)
Browse files Browse the repository at this point in the history
If enabled, tyk logs will show direct key hashes in logs. If turned off, all hash keys will be replaced by `<hidden>`
  • Loading branch information
dencoded authored and buger committed Nov 28, 2017
1 parent ffc3979 commit f6d170f
Show file tree
Hide file tree
Showing 19 changed files with 255 additions and 172 deletions.
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ type Config struct {
ProxyDefaultTimeout int `json:"proxy_default_timeout"`
LogLevel string `json:"log_level"`
Security SecurityConfig `json:"security"`
EnableKeyLogging bool `json:"enable_key_logging"`
}

type CertData struct {
Expand Down
7 changes: 2 additions & 5 deletions coprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,8 @@ func (m *CoProcessMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Requ
// The CP middleware indicates this is a bad auth:
if returnObject.Request.ReturnOverrides.ResponseCode > 400 {

log.WithFields(logrus.Fields{
"path": r.URL.Path,
"origin": requestIP(r),
"key": token,
}).Info("Attempted access with invalid key.")
logEntry := getLogEntryForRequest(r, token, nil)
logEntry.Info("Attempted access with invalid key.")

// Fire Authfailed Event
AuthFailed(m, r, token)
Expand Down
6 changes: 2 additions & 4 deletions coprocess_id_extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,8 @@ func (e *BaseExtractor) ExtractBody(r *http.Request) (bodyValue string, err erro

// Error is a helper for logging the extractor errors. It always returns HTTP 400 (so we don't expose any details).
func (e *BaseExtractor) Error(r *http.Request, err error, message string) (returnOverrides ReturnOverrides) {
log.WithFields(logrus.Fields{
"path": r.URL.Path,
"origin": requestIP(r),
}).Info("Extractor error: ", message, ", ", err)
logEntry := getLogEntryForRequest(r, "", nil)
logEntry.Info("Extractor error: ", message, ", ", err)

return ReturnOverrides{
ResponseCode: 400,
Expand Down
3 changes: 3 additions & 0 deletions lint/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,9 @@ const confSchema = `{
}
}
}
},
"enable_key_logging": {
"type": "boolean"
}
}
}`
36 changes: 36 additions & 0 deletions log_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"net/http"

"github.com/Sirupsen/logrus"

"github.com/TykTechnologies/tyk/config"
)

// identifies that field value was hidden before output to the log
const (
logHiddenValue = "<hidden>"
)

func getLogEntryForRequest(r *http.Request, key string, data map[string]interface{}) *logrus.Entry {
// populate http request fields
fields := logrus.Fields{
"path": r.URL.Path,
"origin": requestIP(r),
}
// add key to log if configured to do so
if key != "" {
fields["key"] = key
if !config.Global.EnableKeyLogging {
fields["key"] = logHiddenValue
}
}
// add to log additional fields if any passed
if data != nil && len(data) > 0 {
for key, val := range data {
fields[key] = val
}
}
return log.WithFields(fields)
}
134 changes: 134 additions & 0 deletions log_helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package main

import (
"net/http/httptest"
"testing"

"github.com/Sirupsen/logrus"

"github.com/TykTechnologies/tyk/config"
)

func TestGetLogEntryForRequest(t *testing.T) {
testReq := httptest.NewRequest("GET", "http://tyk.io/test", nil)
testReq.RemoteAddr = "127.0.0.1:80"
testData := []struct {
EnableKeyLogging bool
Key string
Data map[string]interface{}
Result *logrus.Entry
}{
// enable_key_logging is set, key passed, no additional data fields
{
EnableKeyLogging: true,
Key: "abc",
Data: nil,
Result: logrus.WithFields(logrus.Fields{
"path": "/test",
"origin": "127.0.0.1",
"key": "abc",
}),
},
// enable_key_logging is set, key is not passed, no additional data fields
{
EnableKeyLogging: true,
Key: "",
Data: nil,
Result: logrus.WithFields(logrus.Fields{
"path": "/test",
"origin": "127.0.0.1",
}),
},
// enable_key_logging is set, key passed, additional data fields are passed
{
EnableKeyLogging: true,
Key: "abc",
Data: map[string]interface{}{"a": 1, "b": "test"},
Result: logrus.WithFields(logrus.Fields{
"path": "/test",
"origin": "127.0.0.1",
"key": "abc",
"a": 1,
"b": "test",
}),
},
// enable_key_logging is set, key is not passed, additional data fields are passed
{
EnableKeyLogging: true,
Key: "",
Data: map[string]interface{}{"a": 1, "b": "test"},
Result: logrus.WithFields(logrus.Fields{
"path": "/test",
"origin": "127.0.0.1",
"a": 1,
"b": "test",
}),
},
// enable_key_logging is not set, key passed, no additional data field
{
EnableKeyLogging: false,
Key: "abc",
Data: nil,
Result: logrus.WithFields(logrus.Fields{
"path": "/test",
"origin": "127.0.0.1",
"key": logHiddenValue,
}),
},
// enable_key_logging is not set, key is not passed, no additional data field
{
EnableKeyLogging: false,
Key: "",
Data: nil,
Result: logrus.WithFields(logrus.Fields{
"path": "/test",
"origin": "127.0.0.1",
}),
},
// enable_key_logging is not set, key passed, additional data fields are passed
{
EnableKeyLogging: false,
Key: "abc",
Data: map[string]interface{}{"a": 1, "b": "test"},
Result: logrus.WithFields(logrus.Fields{
"path": "/test",
"origin": "127.0.0.1",
"a": 1,
"b": "test",
"key": logHiddenValue,
}),
},
// enable_key_logging is not set, key is not passed, additional data fields are passed
{
EnableKeyLogging: false,
Key: "",
Data: map[string]interface{}{"a": 1, "b": "test"},
Result: logrus.WithFields(logrus.Fields{
"path": "/test",
"origin": "127.0.0.1",
"a": 1,
"b": "test",
}),
},
}
for _, test := range testData {
config.Global.EnableKeyLogging = test.EnableKeyLogging
logEntry := getLogEntryForRequest(testReq, test.Key, test.Data)
if logEntry.Data["path"] != test.Result.Data["path"] {
t.Error("Expected 'path':", test.Result.Data["path"], "Got:", logEntry.Data["path"])
}
if logEntry.Data["origin"] != test.Result.Data["origin"] {
t.Error("Expected 'origin':", test.Result.Data["origin"], "Got:", logEntry.Data["origin"])
}
if logEntry.Data["key"] != test.Result.Data["key"] {
t.Error("Expected 'key':", test.Result.Data["key"], "Got:", logEntry.Data["key"])
}
if test.Data != nil {
for key, val := range test.Data {
if logEntry.Data[key] != val {
t.Error("Expected data key:", key, "with value:", val, "Got:", logEntry.Data[key])
}
}
}
}
}
32 changes: 17 additions & 15 deletions mw_access_rights.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package main
import (
"errors"
"net/http"

"github.com/Sirupsen/logrus"
)

// AccessRightsCheck is a middleware that will check if the key bing used to access the API has
Expand All @@ -29,12 +27,14 @@ func (a *AccessRightsCheck) ProcessRequest(w http.ResponseWriter, r *http.Reques
// Otherwise, run auth checks
versionList, apiExists := session.AccessRights[a.Spec.APIID]
if !apiExists {
log.WithFields(logrus.Fields{
"path": r.URL.Path,
"origin": requestIP(r),
"key": token,
"api_found": false,
}).Info("Attempted access to unauthorised API.")
logEntry := getLogEntryForRequest(
r,
token,
map[string]interface{}{
"api_found": false,
},
)
logEntry.Info("Attempted access to unauthorised API.")

return errors.New("Access to this API has been disallowed"), 403
}
Expand All @@ -55,13 +55,15 @@ func (a *AccessRightsCheck) ProcessRequest(w http.ResponseWriter, r *http.Reques

if !found {
// Not found? Bounce
log.WithFields(logrus.Fields{
"path": r.URL.Path,
"origin": requestIP(r),
"key": token,
"api_found": true,
"version_found": false,
}).Info("Attempted access to unauthorised API version.")
logEntry := getLogEntryForRequest(
r,
token,
map[string]interface{}{
"api_found": true,
"version_found": false,
},
)
logEntry.Info("Attempted access to unauthorised API version.")

return errors.New("Access to this API has been disallowed"), 403
}
Expand Down
9 changes: 2 additions & 7 deletions mw_api_rate_limit.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"fmt"
"net/http"

"github.com/Sirupsen/logrus"

"strconv"
"time"

Expand Down Expand Up @@ -44,11 +42,8 @@ func (k *RateLimitForAPI) EnabledForSpec() bool {
}

func (k *RateLimitForAPI) handleRateLimitFailure(r *http.Request, token string) (error, int) {
log.WithFields(logrus.Fields{
"path": r.URL.Path,
"origin": requestIP(r),
"key": token,
}).Info("API rate limit exceeded.")
logEntry := getLogEntryForRequest(r, token, nil)
logEntry.Info("API rate limit exceeded.")

// Fire a rate limit exceeded event
k.FireEvent(EventRateLimitExceeded, EventKeyFailureMeta{
Expand Down
15 changes: 4 additions & 11 deletions mw_auth_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"net/http"
"strings"

"github.com/Sirupsen/logrus"

"github.com/TykTechnologies/tyk/apidef"
"github.com/TykTechnologies/tyk/certs"
)
Expand Down Expand Up @@ -76,10 +74,8 @@ func (k *AuthKey) ProcessRequest(w http.ResponseWriter, r *http.Request, _ inter

if key == "" {
// No header value, fail
log.WithFields(logrus.Fields{
"path": r.URL.Path,
"origin": requestIP(r),
}).Info("Attempted access with malformed header, no auth header found.")
logEntry := getLogEntryForRequest(r, "", nil)
logEntry.Info("Attempted access with malformed header, no auth header found.")

return errors.New("Authorization field missing"), 401
}
Expand All @@ -90,11 +86,8 @@ func (k *AuthKey) ProcessRequest(w http.ResponseWriter, r *http.Request, _ inter
// Check if API key valid
session, keyExists := k.CheckSessionAndIdentityForValidKey(key)
if !keyExists {
log.WithFields(logrus.Fields{
"path": r.URL.Path,
"origin": requestIP(r),
"key": key,
}).Info("Attempted access with non-existent key.")
logEntry := getLogEntryForRequest(r, key, nil)
logEntry.Info("Attempted access with non-existent key.")

// Fire Authfailed Event
AuthFailed(k, r, key)
Expand Down
Loading

0 comments on commit f6d170f

Please sign in to comment.