Skip to content

Commit a634551

Browse files
Users: Add request logging
This patch adds new configuration options to the web.config.file which makes it possible to enable logging of http requests. The "request_log_config" struct takes two arguments, a "file" which gets written to on request and "header_for_ip" which optionally allows a user to override how the ip gets retrieved. A log entry is formatted as json and currently contains the ip of the request, if authentication was successful, the url path and a timestamp Signed-off-by: networkException <[email protected]>
1 parent 7cd0e90 commit a634551

File tree

2 files changed

+79
-14
lines changed

2 files changed

+79
-14
lines changed

web/tls_config.go

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ import (
2020
"io/ioutil"
2121
"net"
2222
"net/http"
23+
"os"
2324
"path/filepath"
25+
"sync"
26+
"time"
2427

2528
"github.com/go-kit/log"
2629
"github.com/go-kit/log/level"
@@ -30,13 +33,18 @@ import (
3033
)
3134

3235
var (
33-
errNoTLSConfig = errors.New("TLS config is not present")
36+
errNoTLSConfig = errors.New("TLS config is not present")
37+
timestampFormat = log.TimestampFormat(
38+
func() time.Time { return time.Now().UTC() },
39+
"2006-01-02T15:04:05.000Z07:00",
40+
)
3441
)
3542

3643
type Config struct {
37-
TLSConfig TLSStruct `yaml:"tls_server_config"`
38-
HTTPConfig HTTPStruct `yaml:"http_server_config"`
39-
Users map[string]config_util.Secret `yaml:"basic_auth_users"`
44+
TLSConfig TLSStruct `yaml:"tls_server_config"`
45+
HTTPConfig HTTPStruct `yaml:"http_server_config"`
46+
RequestLogConfig RequestLogStruct `yaml:"request_log_config"`
47+
Users map[string]config_util.Secret `yaml:"basic_auth_users"`
4048
}
4149

4250
type TLSStruct struct {
@@ -62,6 +70,15 @@ type HTTPStruct struct {
6270
HTTP2 bool `yaml:"http2"`
6371
}
6472

73+
type RequestLogStruct struct {
74+
File string `yaml:"file"`
75+
HeaderForIp string `yaml:"header_for_ip"`
76+
}
77+
78+
func (r *RequestLogStruct) SetDirectory(dir string) {
79+
r.File = config_util.JoinDir(dir, r.File)
80+
}
81+
6582
func getConfig(configPath string) (*Config, error) {
6683
content, err := ioutil.ReadFile(configPath)
6784
if err != nil {
@@ -73,10 +90,12 @@ func getConfig(configPath string) (*Config, error) {
7390
MaxVersion: tls.VersionTLS13,
7491
PreferServerCipherSuites: true,
7592
},
76-
HTTPConfig: HTTPStruct{HTTP2: true},
93+
HTTPConfig: HTTPStruct{HTTP2: true},
94+
RequestLogConfig: RequestLogStruct{File: "", HeaderForIp: ""},
7795
}
7896
err = yaml.UnmarshalStrict(content, c)
7997
c.TLSConfig.SetDirectory(filepath.Dir(configPath))
98+
c.RequestLogConfig.SetDirectory(filepath.Dir(configPath))
8099
return c, err
81100
}
82101

@@ -207,11 +226,33 @@ func Serve(l net.Listener, server *http.Server, tlsConfigPath string, logger log
207226
return err
208227
}
209228

210-
server.Handler = &userAuthRoundtrip{
211-
tlsConfigPath: tlsConfigPath,
212-
logger: logger,
213-
handler: handler,
214-
cache: newCache(),
229+
if c.RequestLogConfig.File != "" {
230+
f, err := os.OpenFile(c.RequestLogConfig.File, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
231+
if err != nil {
232+
return err
233+
}
234+
235+
defer f.Close()
236+
237+
server.Handler = &userAuthRoundtrip{
238+
tlsConfigPath: tlsConfigPath,
239+
logger: logger,
240+
handler: handler,
241+
cache: newCache(),
242+
requestLogger: log.With(log.NewJSONLogger(f), "ts", timestampFormat),
243+
requestLoggerLock: sync.RWMutex{},
244+
}
245+
246+
level.Info(logger).Log("msg", "Request logging is enabled.", "file", c.RequestLogConfig.File, "headerForIp", c.RequestLogConfig.HeaderForIp)
247+
} else {
248+
server.Handler = &userAuthRoundtrip{
249+
tlsConfigPath: tlsConfigPath,
250+
logger: logger,
251+
handler: handler,
252+
cache: newCache(),
253+
}
254+
255+
level.Info(logger).Log("msg", "Request logging is disabled.")
215256
}
216257

217258
config, err := ConfigToTLSConfig(&c.TLSConfig)

web/users.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"sync"
2222

2323
"github.com/go-kit/log"
24+
"github.com/go-kit/log/level"
2425
"golang.org/x/crypto/bcrypt"
2526
)
2627

@@ -41,10 +42,12 @@ func validateUsers(configPath string) error {
4142
}
4243

4344
type userAuthRoundtrip struct {
44-
tlsConfigPath string
45-
handler http.Handler
46-
logger log.Logger
47-
cache *cache
45+
tlsConfigPath string
46+
handler http.Handler
47+
logger log.Logger
48+
requestLogger log.Logger
49+
requestLoggerLock sync.RWMutex
50+
cache *cache
4851
// bcryptMtx is there to ensure that bcrypt.CompareHashAndPassword is run
4952
// only once in parallel as this is CPU intensive.
5053
bcryptMtx sync.Mutex
@@ -89,10 +92,31 @@ func (u *userAuthRoundtrip) ServeHTTP(w http.ResponseWriter, r *http.Request) {
8992

9093
if authOk && validUser {
9194
u.handler.ServeHTTP(w, r)
95+
logRequest(u, r, c, true)
9296
return
9397
}
9498
}
9599

96100
w.Header().Set("WWW-Authenticate", "Basic")
97101
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
102+
103+
logRequest(u, r, c, false)
104+
}
105+
106+
func logRequest(u *userAuthRoundtrip, r *http.Request, c *Config, authorized bool) {
107+
u.requestLoggerLock.RLock()
108+
109+
if l := u.requestLogger; l != nil {
110+
var ip = r.RemoteAddr
111+
112+
if c.RequestLogConfig.HeaderForIp != "" {
113+
ip = r.Header.Get(c.RequestLogConfig.HeaderForIp)
114+
}
115+
116+
if err := l.Log("path", r.URL.Path, "authorized", authorized, "ip", ip); err != nil {
117+
level.Error(u.logger).Log("msg", "can't log query", "err", err)
118+
}
119+
}
120+
121+
u.requestLoggerLock.RUnlock()
98122
}

0 commit comments

Comments
 (0)