diff --git a/go.mod b/go.mod index 4874f3313..4118a2283 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ replace ( require ( github.com/ghodss/yaml v1.0.0 + github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 github.com/hashicorp/go-cleanhttp v0.5.1 github.com/hashicorp/go-getter v1.5.1 diff --git a/go.sum b/go.sum index 1875706aa..f60050c74 100644 --- a/go.sum +++ b/go.sum @@ -261,6 +261,8 @@ github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -447,6 +449,8 @@ github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d/go.mod h1:ehWUbL github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v0.0.0-20181024020800-521ea7b17d02/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= diff --git a/pkg/http-server/start.go b/pkg/http-server/start.go index 93e16863b..99362ce7b 100644 --- a/pkg/http-server/start.go +++ b/pkg/http-server/start.go @@ -18,6 +18,8 @@ package httpserver import ( "context" + httputils "github.com/accurics/terrascan/pkg/utils/http" + gorillaHandlers "github.com/gorilla/handlers" "net/http" "os" "os/signal" @@ -54,14 +56,20 @@ func (g *APIServer) start(routes []*Route, port, certFile, privateKeyFile string router = mux.NewRouter() // new router ) + logWriter := logging.GetLogWriter(logger) + logger.Info("registering routes...") // register all routes for _, v := range routes { logger.Info("Route ", v.verb, " - ", v.path) - router.Methods(v.verb).Path(v.path).HandlerFunc(v.fn) + handler := gorillaHandlers.CustomLoggingHandler(logWriter, http.HandlerFunc(v.fn), logging.WriteRequestLog) + router.Methods(v.verb).Path(v.path).Handler(handler) } + router.NotFoundHandler = gorillaHandlers.CustomLoggingHandler(logWriter, http.HandlerFunc(httputils.NotFound), logging.WriteRequestLog) + router.MethodNotAllowedHandler = gorillaHandlers.CustomLoggingHandler(logWriter, http.HandlerFunc(httputils.NotAllowed), logging.WriteRequestLog) + // Add a route for all static templates / assets. Currently used for the Webhook logs views // go/terrascan/asset is the path where the assets files are located inside the docker container router.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", http.FileServer(http.Dir("/go/terrascan/assets")))) @@ -100,3 +108,5 @@ func (g *APIServer) start(routes []*Route, port, certFile, privateKeyFile string } logger.Info("server exiting gracefully") } + + diff --git a/pkg/logging/logwriter.go b/pkg/logging/logwriter.go new file mode 100644 index 000000000..f0caa385a --- /dev/null +++ b/pkg/logging/logwriter.go @@ -0,0 +1,76 @@ +package logging + +import ( + gorillaHandlers "github.com/gorilla/handlers" + "go.uber.org/zap" + "io" + "net" + "net/http" + "net/url" + "strconv" + "time" +) + +type LogWriter struct { + logger *zap.SugaredLogger +} + +func GetLogWriter(logger *zap.SugaredLogger) LogWriter { + return LogWriter{ + logger: logger, + } +} + +func (l LogWriter) Write(p []byte) (n int, err error) { + l.logger.Info(string(p)) + return len(p), nil +} + +// buildCommonLogLine builds a log entry for req in Apache Common Log Format. +// ts is the timestamp with which the entry should be logged. +// status and size are used to provide the response HTTP status and size. +func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte { + username := "-" + if url.User != nil { + if name := url.User.Username(); name != "" { + username = name + } + } + + host, _, err := net.SplitHostPort(req.RemoteAddr) + if err != nil { + host = req.RemoteAddr + } + + uri := req.RequestURI + + // Requests using the CONNECT method over HTTP/2.0 must use + // the authority field (aka r.Host) to identify the target. + // Refer: https://httpwg.github.io/specs/rfc7540.html#CONNECT + if req.ProtoMajor == 2 && req.Method == "CONNECT" { + uri = req.Host + } + if uri == "" { + uri = url.RequestURI() + } + + buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2) + buf = append(buf, host...) + buf = append(buf, " - "...) + buf = append(buf, username...) + buf = append(buf, ` "`...) + buf = append(buf, req.Method...) + buf = append(buf, " "...) + buf = append(buf, uri...) + buf = append(buf, " "...) + buf = append(buf, req.Proto...) + buf = append(buf, `" `...) + buf = append(buf, strconv.Itoa(status)...) + buf = append(buf, " "...) + buf = append(buf, strconv.Itoa(size)...) + return buf +} + +func WriteRequestLog(writer io.Writer, params gorillaHandlers.LogFormatterParams) { + writer.Write(buildCommonLogLine(params.Request, params.URL, params.TimeStamp, params.StatusCode, params.Size)) +} diff --git a/pkg/utils/http/response.go b/pkg/utils/http/response.go new file mode 100644 index 000000000..fdc75bd2d --- /dev/null +++ b/pkg/utils/http/response.go @@ -0,0 +1,11 @@ +package httputils + +import "net/http" + +func NotFound(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) +} + +func NotAllowed(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusMethodNotAllowed) +}