pomerium/internal/log/request_logger.go
Bobby DeSimone 90ab756de1
Added gif to the readme.
Simplified, and de-duplicated many of the configuration settings.
Removed configuration settings that could be deduced from other settings.
Added some basic documentation.
Removed the (duplicate?) user email domain validation check in proxy.
Removed the ClientID middleware check.
Added a shared key option to be used as a PSK instead of using the IDPs ClientID and ClientSecret.
Removed the CookieSecure setting as we only support secure.
Added a letsencrypt script to generate a wildcard certificate.
Removed the argument in proxy's constructor that allowed arbitrary fucntions to be passed in as validators.
Updated proxy's authenticator client to match the server implementation of just using a PSK.
Moved debug-mode logging into the log package.
Removed unused approval prompt setting.
Fixed a bug where identity provider urls were hardcoded.
Removed a bunch of unit tests. There have been so many changes many of these tests don't make sense and will need to be re-thought.
2019-01-04 18:25:03 -08:00

145 lines
3.6 KiB
Go

package log // import "github.com/pomerium/pomerium/internal/log"
import (
"net/http"
"net/url"
"strings"
"time"
)
// Used to stash the authenticated user in the response for access when logging requests.
const loggingUserHeader = "SSO-Authenticated-User"
const gapMetaDataHeader = "GAP-Auth"
// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP status
// code and body size
type responseLogger struct {
w http.ResponseWriter
status int
size int
proxyHost string
authInfo string
}
func (l *responseLogger) Header() http.Header {
return l.w.Header()
}
func (l *responseLogger) extractUser() {
authInfo := l.w.Header().Get(loggingUserHeader)
if authInfo != "" {
l.authInfo = authInfo
l.w.Header().Del(loggingUserHeader)
}
}
func (l *responseLogger) ExtractGAPMetadata() {
authInfo := l.w.Header().Get(gapMetaDataHeader)
if authInfo != "" {
l.authInfo = authInfo
l.w.Header().Del(gapMetaDataHeader)
}
}
func (l *responseLogger) Write(b []byte) (int, error) {
if l.status == 0 {
// The status will be StatusOK if WriteHeader has not been called yet
l.status = http.StatusOK
}
l.extractUser()
l.ExtractGAPMetadata()
size, err := l.w.Write(b)
l.size += size
return size, err
}
func (l *responseLogger) WriteHeader(s int) {
l.extractUser()
l.ExtractGAPMetadata()
l.w.WriteHeader(s)
l.status = s
}
func (l *responseLogger) Status() int {
return l.status
}
func (l *responseLogger) Size() int {
return l.size
}
func (l *responseLogger) Flush() {
f := l.w.(http.Flusher)
f.Flush()
}
// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends
type loggingHandler struct {
handler http.Handler
}
// NewLoggingHandler returns a new loggingHandler that wraps a handler, and writer.
func NewLoggingHandler(h http.Handler) http.Handler {
return loggingHandler{
handler: h,
}
}
func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
t := time.Now()
url := *req.URL
logger := &responseLogger{w: w, proxyHost: getProxyHost(req)}
h.handler.ServeHTTP(logger, req)
requestDuration := time.Since(t)
logRequest(logger.proxyHost, logger.authInfo, req, url, requestDuration, logger.Status())
}
// logRequest logs information about a request
func logRequest(proxyHost, username string, req *http.Request, url url.URL, requestDuration time.Duration, status int) {
uri := req.Host + url.RequestURI()
Info().
Int("http-status", status).
Str("request-method", req.Method).
Str("request-uri", uri).
Str("proxy-host", proxyHost).
// Str("user-agent", req.Header.Get("User-Agent")).
Str("remote-address", getRemoteAddr(req)).
Dur("duration", requestDuration).
Str("user", username).
Msg("request")
}
// getRemoteAddr returns the client IP address from a request. If present, the
// X-Forwarded-For header is assumed to be set by a load balancer, and its
// rightmost entry (the client IP that connected to the LB) is returned.
func getRemoteAddr(req *http.Request) string {
addr := req.RemoteAddr
forwardedHeader := req.Header.Get("X-Forwarded-For")
if forwardedHeader != "" {
forwardedList := strings.Split(forwardedHeader, ",")
forwardedAddr := strings.TrimSpace(forwardedList[len(forwardedList)-1])
if forwardedAddr != "" {
addr = forwardedAddr
}
}
return addr
}
// getProxyHost attempts to get the proxy host from the redirect_uri parameter
func getProxyHost(req *http.Request) string {
err := req.ParseForm()
if err != nil {
return ""
}
redirect := req.Form.Get("redirect_uri")
redirectURL, err := url.Parse(redirect)
if err != nil {
return ""
}
return redirectURL.Host
}