From feb4977b17ef5e1ebad4409b2fdd48bb9b58a9da Mon Sep 17 00:00:00 2001 From: Javier Marcos Date: Wed, 4 Mar 2020 17:45:20 -0800 Subject: [PATCH 1/3] Refactor http responses --- admin/handlers-post.go | 91 +++++----- admin/handlers-tokens.go | 8 +- admin/handlers.go | 20 +-- admin/json-carves.go | 17 +- admin/json-logs.go | 10 +- admin/json-nodes.go | 8 +- admin/json-queries.go | 4 +- admin/json-stats.go | 6 +- admin/utils.go | 17 +- api/handlers-environments.go | 11 +- api/handlers-nodes.go | 10 +- api/handlers-platforms.go | 4 +- api/handlers-queries.go | 11 +- api/handlers.go | 17 +- api/utils.go | 19 +-- tls/carving.go | 76 +++++++++ tls/handlers-tls.go | 312 ++++++----------------------------- tls/logs.go | 137 +++++++++++++++ utils/utils.go | 22 +++ 19 files changed, 384 insertions(+), 416 deletions(-) create mode 100644 tls/carving.go create mode 100644 tls/logs.go diff --git a/admin/handlers-post.go b/admin/handlers-post.go index 955098d7..798ff5f6 100644 --- a/admin/handlers-post.go +++ b/admin/handlers-post.go @@ -15,8 +15,15 @@ import ( "github.com/gorilla/mux" ) +const ( + metricAdminReq = "admin-req" + metricAdminErr = "admin-err" + metricAdminOK = "admin-ok" +) + // Handler for login page for POST requests func loginPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), false) responseMessage := "OK" responseCode := http.StatusOK @@ -59,16 +66,16 @@ func loginPOSTHandler(w http.ResponseWriter, r *http.Request) { response = []byte(responseMessage) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Login response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } // Handle POST requests to logout func logoutHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) responseMessage := "OK" responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), false) @@ -78,6 +85,7 @@ func logoutHandler(w http.ResponseWriter, r *http.Request) { // Parse request JSON body err := json.NewDecoder(r.Body).Decode(&l) if err != nil { + incMetric(metricAdminErr) responseMessage = "error parsing POST body" responseCode = http.StatusInternalServerError if settingsmgr.DebugService(settings.ServiceAdmin) { @@ -112,16 +120,16 @@ func logoutHandler(w http.ResponseWriter, r *http.Request) { response = []byte(responseMessage) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Logout response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } // Handler for POST requests to run queries func queryRunPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) responseMessage := "The query was created successfully" responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) @@ -259,16 +267,16 @@ send_response: response = []byte("error formating response") } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Query run response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } // Handler for POST requests to run file carves func carvesRunPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) responseMessage := "The carve was created successfully" responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) @@ -418,16 +426,16 @@ send_response: response = []byte("error formating response") } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Carve run response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } // Handler for POST requests to queries func queryActionsPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) responseMessage := "OK" responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) @@ -504,16 +512,16 @@ send_response: response = []byte(responseMessage) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Query run response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } // Handler for POST requests to carves func carvesActionsPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) responseMessage := "OK" responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) @@ -572,16 +580,16 @@ send_response: response = []byte(responseMessage) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Carves action response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } // Handler POST requests for saving configuration func confPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) responseMessage := "Configuration saved successfully" responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) @@ -666,16 +674,16 @@ send_response: response = []byte(responseMessage) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Configuration response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } // Handler POST requests for saving intervals func intervalsPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) responseMessage := "Intervals updated successfully" responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) @@ -754,16 +762,16 @@ func intervalsPOSTHandler(w http.ResponseWriter, r *http.Request) { response = []byte(responseMessage) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Intervals response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } // Handler POST requests for expiring enroll links func expirationPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) responseMessage := "OK" responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) @@ -860,16 +868,16 @@ func expirationPOSTHandler(w http.ResponseWriter, r *http.Request) { response = []byte(responseMessage) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Expiration response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } // Handler POST requests for multi node action func nodeActionsPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) responseMessage := "OK" responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) @@ -930,16 +938,16 @@ func nodeActionsPOSTHandler(w http.ResponseWriter, r *http.Request) { response = []byte(responseMessage) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Multi-node action response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } // Handler for POST request for /environments func envsPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) responseMessage := "OK" responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) @@ -1047,16 +1055,16 @@ send_response: } } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Environments response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } // Handler for POST request for /settings func settingsPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) responseMessage := "OK" responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) @@ -1170,16 +1178,16 @@ send_response: response = []byte(responseMessage) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Settings response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } // Handler for POST request for /users func usersPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) responseMessage := "OK" responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) @@ -1328,16 +1336,16 @@ send_response: response = []byte(responseMessage) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Users response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } // Handler POST requests enroll data func enrollPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) responseMessage := "Enroll data saved successfully" responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) @@ -1422,10 +1430,9 @@ send_response: response = []byte(responseMessage) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(responseCode) - _, _ = w.Write(response) if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Configuration response sent") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + incMetric(metricAdminOK) } diff --git a/admin/handlers-tokens.go b/admin/handlers-tokens.go index 9807bd91..26cdd29a 100644 --- a/admin/handlers-tokens.go +++ b/admin/handlers-tokens.go @@ -10,6 +10,12 @@ import ( "github.com/jmpsec/osctrl/utils" ) +const ( + metricTokenReq = "admin-token-req" + metricTokenErr = "admin-token-err" + metricTokenOK = "admin-token-ok" +) + // TokenJSON to be used to populate a JSON token type TokenJSON struct { Token string `json:"token"` @@ -131,6 +137,6 @@ func tokensPOSTHandler(w http.ResponseWriter, r *http.Request) { return } // Serialize and serve JSON - apiHTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, response) incMetric(metricTokenOK) } diff --git a/admin/handlers.go b/admin/handlers.go index e1f07fe2..0f814a84 100644 --- a/admin/handlers.go +++ b/admin/handlers.go @@ -8,15 +8,9 @@ import ( ) const ( - metricAdminReq = "admin-req" - metricAdminErr = "admin-err" - metricAdminOK = "admin-ok" metricJSONReq = "admin-json-req" metricJSONErr = "admin-json-err" metricJSONOK = "admin-json-ok" - metricTokenReq = "admin-token-req" - metricTokenErr = "admin-token-err" - metricTokenOK = "admin-token-ok" metricHealthReq = "health-req" metricHealthOK = "health-ok" ) @@ -24,14 +18,15 @@ const ( // Empty default osquery configuration const emptyConfiguration string = "data/osquery-empty.json" +var errorContent = []byte("❌") +var okContent = []byte("✅") + // Handle health requests func healthHTTPHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricHealthReq) utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("✅")) + utils.HTTPResponse(w, "", http.StatusOK, okContent) incMetric(metricHealthOK) } @@ -39,17 +34,14 @@ func healthHTTPHandler(w http.ResponseWriter, r *http.Request) { func errorHTTPHandler(w http.ResponseWriter, r *http.Request) { utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusInternalServerError) - _, _ = w.Write([]byte("oh no...")) + utils.HTTPResponse(w, "", http.StatusInternalServerError, []byte("oh no...")) } // Handle forbidden error requests func forbiddenHTTPHandler(w http.ResponseWriter, r *http.Request) { utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) // Send response - w.WriteHeader(http.StatusForbidden) - _, _ = w.Write([]byte("❌")) + utils.HTTPResponse(w, "", http.StatusForbidden, errorContent) } // Handler for the favicon diff --git a/admin/json-carves.go b/admin/json-carves.go index a344e47e..03cecbe9 100644 --- a/admin/json-carves.go +++ b/admin/json-carves.go @@ -66,24 +66,21 @@ func jsonCarvesHandler(w http.ResponseWriter, r *http.Request) { // Extract target target, ok := vars["target"] if !ok { - incMetric(metricAdminErr) - log.Println("error getting target") incMetric(metricJSONErr) + log.Println("error getting target") return } // Verify target if !CarvesTargets[target] { - incMetric(metricAdminErr) - log.Printf("invalid target %s", target) incMetric(metricJSONErr) + log.Printf("invalid target %s", target) return } // Retrieve carves for that target qs, err := queriesmgr.GetCarves(target) if err != nil { - incMetric(metricAdminErr) - log.Printf("error getting query carves %v", err) incMetric(metricJSONErr) + log.Printf("error getting query carves %v", err) return } // Prepare data to be returned @@ -140,15 +137,11 @@ func jsonCarvesHandler(w http.ResponseWriter, r *http.Request) { // Serialize JSON returnedJSON, err := json.Marshal(returned) if err != nil { - incMetric(metricAdminErr) - log.Printf("error serializing JSON %v", err) incMetric(metricJSONErr) + log.Printf("error serializing JSON %v", err) return } - incMetric(metricAdminOK) // Header to serve JSON - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(returnedJSON) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) incMetric(metricJSONOK) } diff --git a/admin/json-logs.go b/admin/json-logs.go index 61e8c10b..5b971eae 100644 --- a/admin/json-logs.go +++ b/admin/json-logs.go @@ -149,11 +149,8 @@ func jsonLogsHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricJSONErr) return } - incMetric(metricAdminOK) // Header to serve JSON - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(returnedJSON) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) incMetric(metricJSONOK) } @@ -200,10 +197,7 @@ func jsonQueryLogsHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricJSONErr) return } - incMetric(metricAdminOK) // Header to serve JSON - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(returnedJSON) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) incMetric(metricJSONOK) } diff --git a/admin/json-nodes.go b/admin/json-nodes.go index c353c9a4..292eab6c 100644 --- a/admin/json-nodes.go +++ b/admin/json-nodes.go @@ -103,9 +103,7 @@ func jsonEnvironmentHandler(w http.ResponseWriter, r *http.Request) { return } // Header to serve JSON - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(returnedJSON) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) incMetric(metricJSONOK) } @@ -169,8 +167,6 @@ func jsonPlatformHandler(w http.ResponseWriter, r *http.Request) { return } // Header to serve JSON - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(returnedJSON) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) incMetric(metricJSONOK) } diff --git a/admin/json-queries.go b/admin/json-queries.go index 5105e4ec..fc4db2bf 100644 --- a/admin/json-queries.go +++ b/admin/json-queries.go @@ -134,8 +134,6 @@ func jsonQueryHandler(w http.ResponseWriter, r *http.Request) { return } // Header to serve JSON - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(returnedJSON) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) incMetric(metricJSONOK) } diff --git a/admin/json-stats.go b/admin/json-stats.go index a0f069ea..c3d4be96 100644 --- a/admin/json-stats.go +++ b/admin/json-stats.go @@ -69,9 +69,7 @@ func jsonStatsHandler(w http.ResponseWriter, r *http.Request) { log.Printf("error serializing JSON %v", err) return } - incMetric(metricAdminOK) // Header to serve JSON - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(returnedJSON) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) + incMetric(metricJSONOK) } diff --git a/admin/utils.go b/admin/utils.go index c6450b1e..eff1345a 100644 --- a/admin/utils.go +++ b/admin/utils.go @@ -398,23 +398,8 @@ func templateMetadata(ctx contextValue, service, version string) TemplateMetadat } } -// Helper to send HTTP response -func apiHTTPResponse(w http.ResponseWriter, cType string, code int, data interface{}) { - if cType != "" { - w.Header().Set(utils.ContentType, cType) - } - content, err := json.Marshal(data) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - log.Printf("error serializing response: %v", err) - content = []byte("error serializing response") - } - w.WriteHeader(code) - _, _ = w.Write(content) -} - // Helper to handle admin error responses func adminErrorResponse(w http.ResponseWriter, msg string, code int, err error) { log.Printf("%s: %v", msg, err) - apiHTTPResponse(w, utils.JSONApplicationUTF8, code, AdminResponse{Message: msg}) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, code, AdminResponse{Message: msg}) } diff --git a/api/handlers-environments.go b/api/handlers-environments.go index 12ef112f..770f3624 100644 --- a/api/handlers-environments.go +++ b/api/handlers-environments.go @@ -25,11 +25,13 @@ func apiEnvironmentHandler(w http.ResponseWriter, r *http.Request) { if !ok { incMetric(metricAPIEnvsErr) apiErrorResponse(w, "error getting name", http.StatusInternalServerError, nil) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusInternalServerError, errorContent) return } // Get context data and check access ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) if !apiUsers.IsAdmin(ctx["user"]) { + incMetric(metricAPIEnvsErr) log.Printf("attempt to use API by user %s", ctx["user"]) apiErrorResponse(w, "no access", http.StatusForbidden, nil) return @@ -47,11 +49,11 @@ func apiEnvironmentHandler(w http.ResponseWriter, r *http.Request) { return } // Header to serve JSON - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, env) - incMetric(metricAPIEnvsOK) if settingsmgr.DebugService(settings.ServiceAPI) { log.Printf("DebugService: Returned environment %s", name) } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, env) + incMetric(metricAPIEnvsOK) } // GET Handler to return all environments as JSON @@ -61,6 +63,7 @@ func apiEnvironmentsHandler(w http.ResponseWriter, r *http.Request) { // Get context data and check access ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) if !apiUsers.IsAdmin(ctx["user"]) { + incMetric(metricAPIEnvsErr) log.Printf("attempt to use API by user %s", ctx["user"]) apiErrorResponse(w, "no access", http.StatusForbidden, nil) return @@ -73,9 +76,9 @@ func apiEnvironmentsHandler(w http.ResponseWriter, r *http.Request) { return } // Header to serve JSON - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, envAll) - incMetric(metricAPIEnvsOK) if settingsmgr.DebugService(settings.ServiceAPI) { log.Println("DebugService: Returned environments") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, envAll) + incMetric(metricAPIEnvsOK) } diff --git a/api/handlers-nodes.go b/api/handlers-nodes.go index e1cbb5da..2364e067 100644 --- a/api/handlers-nodes.go +++ b/api/handlers-nodes.go @@ -30,6 +30,7 @@ func apiNodeHandler(w http.ResponseWriter, r *http.Request) { // Get context data and check access ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) if !apiUsers.IsAdmin(ctx["user"]) { + incMetric(metricAPINodesErr) log.Printf("attempt to use API by user %s", ctx["user"]) apiErrorResponse(w, "no access", http.StatusForbidden, nil) return @@ -47,11 +48,11 @@ func apiNodeHandler(w http.ResponseWriter, r *http.Request) { return } // Serialize and serve JSON - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, node) - incMetric(metricAPINodesOK) if settingsmgr.DebugService(settings.ServiceAPI) { log.Printf("DebugService: Returned node %s", uuid) } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, node) + incMetric(metricAPINodesOK) } // GET Handler for multiple JSON nodes @@ -61,6 +62,7 @@ func apiNodesHandler(w http.ResponseWriter, r *http.Request) { // Get context data and check access ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) if !apiUsers.IsAdmin(ctx["user"]) { + incMetric(metricAPINodesErr) log.Printf("attempt to use API by user %s", ctx["user"]) apiErrorResponse(w, "no access", http.StatusForbidden, nil) return @@ -79,9 +81,9 @@ func apiNodesHandler(w http.ResponseWriter, r *http.Request) { return } // Serialize and serve JSON - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, nodes) - incMetric(metricAPINodesOK) if settingsmgr.DebugService(settings.ServiceAPI) { log.Println("DebugService: Returned nodes") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, nodes) + incMetric(metricAPINodesOK) } diff --git a/api/handlers-platforms.go b/api/handlers-platforms.go index 54c0731f..98d5620f 100644 --- a/api/handlers-platforms.go +++ b/api/handlers-platforms.go @@ -33,9 +33,9 @@ func apiPlatformsHandler(w http.ResponseWriter, r *http.Request) { return } // Serialize and serve JSON - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, platforms) - incMetric(metricAPIPlatformsOK) if settingsmgr.DebugService(settings.ServiceAPI) { log.Println("DebugService: Returned platforms") } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, platforms) + incMetric(metricAPIPlatformsOK) } diff --git a/api/handlers-queries.go b/api/handlers-queries.go index e167a0cf..349489b4 100644 --- a/api/handlers-queries.go +++ b/api/handlers-queries.go @@ -49,10 +49,10 @@ func apiQueryShowHandler(w http.ResponseWriter, r *http.Request) { return } // Serialize and serve JSON - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, query) if settingsmgr.DebugService(settings.ServiceAPI) { log.Printf("DebugService: Returned query %s", name) } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, query) incMetric(metricAPIQueriesOK) } @@ -170,7 +170,7 @@ func apiQueriesRunHandler(w http.ResponseWriter, r *http.Request) { return } // Return query name as serialized response - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, ApiQueriesResponse{Name: newQuery.Name}) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, ApiQueriesResponse{Name: newQuery.Name}) incMetric(metricAPIQueriesOK) } @@ -192,7 +192,7 @@ func apiAllQueriesShowHandler(w http.ResponseWriter, r *http.Request) { return } // Serialize and serve JSON - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, queries) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, queries) incMetric(metricAPIQueriesOK) } @@ -214,7 +214,7 @@ func apiHiddenQueriesShowHandler(w http.ResponseWriter, r *http.Request) { return } // Serialize and serve JSON - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, queries) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, queries) incMetric(metricAPIQueriesOK) } @@ -233,6 +233,7 @@ func apiQueryResultsHandler(w http.ResponseWriter, r *http.Request) { // Get context data and check access ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) if !apiUsers.IsAdmin(ctx["user"]) { + incMetric(metricAPIQueriesErr) log.Printf("attempt to use API by user %s", ctx["user"]) apiErrorResponse(w, "no access", http.StatusForbidden, nil) return @@ -250,6 +251,6 @@ func apiQueryResultsHandler(w http.ResponseWriter, r *http.Request) { return } // Serialize and serve JSON - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, queryLogs) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, queryLogs) incMetric(metricAPIQueriesOK) } diff --git a/api/handlers.go b/api/handlers.go index 06af6852..3cf126ac 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -15,15 +15,6 @@ const ( metricHealthOK = "health-ok" ) -// JSONApplication for Content-Type headers -const JSONApplication string = "application/json" - -// ContentType for header key -const contentType string = "Content-Type" - -// JSONApplicationUTF8 for Content-Type headers, UTF charset -const JSONApplicationUTF8 string = JSONApplication + "; charset=UTF-8" - var errorContent = []byte("❌") var okContent = []byte("✅") @@ -32,7 +23,7 @@ func healthHTTPHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricHealthReq) utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), true) // Send response - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, okContent) + utils.HTTPResponse(w, "", http.StatusOK, okContent) incMetric(metricHealthOK) } @@ -41,7 +32,7 @@ func rootHTTPHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricHealthReq) utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), true) // Send response - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, okContent) + utils.HTTPResponse(w, "", http.StatusOK, okContent) incMetric(metricHealthOK) } @@ -50,7 +41,7 @@ func errorHTTPHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAPIReq) utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), true) // Send response - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusInternalServerError, errorContent) + utils.HTTPResponse(w, "", http.StatusInternalServerError, errorContent) incMetric(metricAPIErr) } @@ -59,6 +50,6 @@ func forbiddenHTTPHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAPIReq) utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) // Send response - apiHTTPResponse(w, JSONApplicationUTF8, http.StatusForbidden, errorContent) + utils.HTTPResponse(w, "", http.StatusForbidden, errorContent) incMetric(metricAPIErr) } diff --git a/api/utils.go b/api/utils.go index df753df1..9f07b0c6 100644 --- a/api/utils.go +++ b/api/utils.go @@ -4,7 +4,6 @@ import ( "crypto/md5" "crypto/rand" "encoding/hex" - "encoding/json" "flag" "fmt" "log" @@ -12,6 +11,7 @@ import ( "os" "github.com/jmpsec/osctrl/settings" + "github.com/jmpsec/osctrl/utils" ) // Helper to send metrics if it is enabled @@ -92,25 +92,10 @@ func removeStringDuplicates(s []string) []string { return s[:i] } -// Helper to send HTTP response -func apiHTTPResponse(w http.ResponseWriter, cType string, code int, data interface{}) { - if cType != "" { - w.Header().Set(contentType, cType) - } - content, err := json.Marshal(data) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - log.Printf("error serializing response: %v", err) - content = []byte("error serializing response") - } - w.WriteHeader(code) - _, _ = w.Write(content) -} - // Helper to handle API error responses func apiErrorResponse(w http.ResponseWriter, msg string, code int, err error) { log.Printf("%s: %v", msg, err) - apiHTTPResponse(w, JSONApplicationUTF8, code, ApiErrorResponse{Error: msg}) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, code, ApiErrorResponse{Error: msg}) } // Helper to generate a random query name diff --git a/tls/carving.go b/tls/carving.go new file mode 100644 index 00000000..117b1e14 --- /dev/null +++ b/tls/carving.go @@ -0,0 +1,76 @@ +package main + +import ( + "log" + + "github.com/jmpsec/osctrl/carves" + "github.com/jmpsec/osctrl/types" +) + +// Function to initialize a file carve from a node +func processCarveInit(req types.CarveInitRequest, sessionid, environment string) error { + // Retrieve node + node, err := nodesmgr.GetByKey(req.NodeKey) + if err != nil { + incMetric(metricInitErr) + log.Printf("error retrieving node %s", err) + return err + } + // Prepare carve to initialize + carve := carves.CarvedFile{ + CarveID: req.CarveID, + RequestID: req.RequestID, + SessionID: sessionid, + UUID: node.UUID, + Environment: environment, + CarveSize: req.CarveSize, + BlockSize: req.BlockSize, + TotalBlocks: req.BlockCount, + CompletedBlocks: 0, + Status: carves.StatusInitialized, + } + // Create File Carve + err = filecarves.CreateCarve(carve) + if err != nil { + incMetric(metricInitErr) + log.Printf("error creating CarvedFile %v", err) + return err + } + return nil +} + +// Function to process one block from a file carve +// FIXME it can be more efficient on db access +func processCarveBlock(req types.CarveBlockRequest, environment string) { + // Prepare carve block + block := carves.CarvedBlock{ + RequestID: req.RequestID, + SessionID: req.SessionID, + Environment: environment, + BlockID: req.BlockID, + Data: req.Data, + Size: len(req.Data), + } + // Create Block + if err := filecarves.CreateBlock(block); err != nil { + incMetric(metricBlockErr) + log.Printf("error creating CarvedBlock %v", err) + } + // Bump block completion + if err := filecarves.CompleteBlock(req.SessionID); err != nil { + incMetric(metricBlockErr) + log.Printf("error completing block %v", err) + } + // If it is completed, set status + if filecarves.Completed(req.SessionID) { + if err := filecarves.ChangeStatus(carves.StatusCompleted, req.SessionID); err != nil { + incMetric(metricBlockErr) + log.Printf("error completing carve %v", err) + } + } else { + if err := filecarves.ChangeStatus(carves.StatusInProgress, req.SessionID); err != nil { + incMetric(metricBlockErr) + log.Printf("error progressing carve %v", err) + } + } +} diff --git a/tls/handlers-tls.go b/tls/handlers-tls.go index 3fa73485..d39ad0d1 100644 --- a/tls/handlers-tls.go +++ b/tls/handlers-tls.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/gorilla/mux" - "github.com/jmpsec/osctrl/carves" "github.com/jmpsec/osctrl/environments" "github.com/jmpsec/osctrl/nodes" "github.com/jmpsec/osctrl/queries" @@ -18,66 +17,52 @@ import ( ) const ( - metricEnrollReq = "enroll-req" - metricEnrollErr = "enroll-err" - metricEnrollOK = "enroll-ok" - metricLogReq = "log-req" - metricLogErr = "log-err" - metricLogOK = "log-ok" - metricConfigReq = "config-req" - metricConfigErr = "config-err" - metricConfigOK = "config-ok" - metricReadReq = "read-req" - metricReadErr = "read-err" - metricReadOK = "read-ok" - metricWriteReq = "write-req" - metricWriteErr = "write-err" - metricWriteOK = "write-ok" - metricInitReq = "init-req" - metricInitErr = "init-err" - metricInitOK = "init-ok" - metricBlockReq = "block-req" - metricBlockErr = "block-err" - metricBlockOK = "block-ok" - metricHealthReq = "health-req" - metricHealthOK = "health-ok" + metricEnrollReq = "enroll-req" + metricEnrollErr = "enroll-err" + metricEnrollOK = "enroll-ok" + metricLogReq = "log-req" + metricLogErr = "log-err" + metricLogOK = "log-ok" + metricConfigReq = "config-req" + metricConfigErr = "config-err" + metricConfigOK = "config-ok" + metricReadReq = "read-req" + metricReadErr = "read-err" + metricReadOK = "read-ok" + metricWriteReq = "write-req" + metricWriteErr = "write-err" + metricWriteOK = "write-ok" + metricInitReq = "init-req" + metricInitErr = "init-err" + metricInitOK = "init-ok" + metricBlockReq = "block-req" + metricBlockErr = "block-err" + metricBlockOK = "block-ok" + metricHealthReq = "health-req" + metricHealthOK = "health-ok" + metricOnelinerReq = "oneliner-req" + metricOnelinerErr = "oneliner-err" + metricOnelinerOk = "oneliner-ok" ) -// JSONApplication for Content-Type headers -const JSONApplication string = "application/json" - -// TextPlain for Content-Type headers -const TextPlain string = "text/plain" - -// JSONApplicationUTF8 for Content-Type headers, UTF charset -const JSONApplicationUTF8 string = JSONApplication + "; charset=UTF-8" - -// TextPlainUTF8 for Content-Type headers, UTF charset -const TextPlainUTF8 string = TextPlain + "; charset=UTF-8" - // Handler to be used as health check func okHTTPHandler(w http.ResponseWriter, r *http.Request) { // Send response - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("💥")) + utils.HTTPResponse(w, "", http.StatusOK, []byte("💥")) } // Handle health requests func healthHTTPHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricHealthReq) // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("✅")) + utils.HTTPResponse(w, "", http.StatusOK, []byte("✅")) incMetric(metricHealthOK) } // Handle error requests func errorHTTPHandler(w http.ResponseWriter, r *http.Request) { // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusInternalServerError) - _, _ = w.Write([]byte("oh no...")) + utils.HTTPResponse(w, "", http.StatusInternalServerError, []byte("uh oh...")) } // Function to handle the enroll requests from osquery nodes @@ -155,9 +140,7 @@ func enrollHandler(w http.ResponseWriter, r *http.Request) { log.Printf("Response: %s", string(response)) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, response) incMetric(metricEnrollOK) } @@ -223,9 +206,7 @@ func configHandler(w http.ResponseWriter, r *http.Request) { log.Printf("Configuration: %s", string(response)) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, response) incMetric(metricConfigOK) } @@ -303,103 +284,10 @@ func logHandler(w http.ResponseWriter, r *http.Request) { log.Printf("Response: %s", string(response)) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, response) incMetric(metricLogOK) } -// Helper to process logs -func processLogs(data json.RawMessage, logType, environment, ipaddress string) { - // Parse log to extract metadata - var logs []types.LogGenericData - err := json.Unmarshal(data, &logs) - if err != nil { - // FIXME metrics for this - log.Printf("error parsing log %s %v", string(data), err) - } - // Iterate through received messages to extract metadata - var uuids, hosts, names, users, osqueryusers, hashes, dhashes, osqueryversions []string - for _, l := range logs { - uuids = append(uuids, l.HostIdentifier) - hosts = append(hosts, l.Decorations.Hostname) - names = append(names, l.Decorations.LocalHostname) - users = append(users, l.Decorations.Username) - osqueryusers = append(osqueryusers, l.Decorations.OsqueryUser) - hashes = append(hashes, l.Decorations.ConfigHash) - dhashes = append(dhashes, l.Decorations.DaemonHash) - osqueryversions = append(osqueryversions, l.Version) - } - // FIXME it only uses the first element from the []string that uniq returns - uuid := uniq(uuids)[0] - user := uniq(users)[0] - osqueryuser := uniq(osqueryusers)[0] - host := uniq(hosts)[0] - name := uniq(names)[0] - hash := uniq(hashes)[0] - dhash := uniq(dhashes)[0] - osqueryversion := uniq(osqueryversions)[0] - // Dispatch logs and update metadata - dispatchLogs(data, uuid, ipaddress, user, osqueryuser, host, name, hash, dhash, osqueryversion, logType, environment) -} - -// Helper to dispatch logs -func dispatchLogs(data []byte, uuid, ipaddress, user, osqueryuser, hostname, localname, hash, dhash, osqueryversion, logType, environment string) { - // Use metadata to update record - if err := nodesmgr.UpdateMetadataByUUID(user, osqueryuser, hostname, localname, ipaddress, hash, dhash, osqueryversion, uuid); err != nil { - log.Printf("error updating metadata %s", err) - } - // Send data to storage - // FIXME allow multiple types of logging - if envsmap[environment].DebugHTTP { - log.Printf("dispatching logs to %s", tlsConfig.Logging) - } - loggerTLS.Log( - logType, - data, - environment, - uuid, - envsmap[environment].DebugHTTP) - // Refresh last logging request - if logType == types.StatusLog { - err := nodesmgr.RefreshLastStatus(uuid) - if err != nil { - log.Printf("error refreshing last status %v", err) - } - } - if logType == types.ResultLog { - if err := nodesmgr.RefreshLastResult(uuid); err != nil { - log.Printf("error refreshing last result %v", err) - } - } -} - -// Helper to dispatch queries -func dispatchQueries(queryData types.QueryWriteData, node nodes.OsqueryNode) { - // Prepare data to send - data, err := json.Marshal(queryData) - if err != nil { - log.Printf("error preparing data %v", err) - } - // Refresh last query write request - if err := nodesmgr.RefreshLastQueryWrite(node.UUID); err != nil { - log.Printf("error refreshing last query write %v", err) - } - // Send data to storage - // FIXME allow multiple types of logging - if envsmap[node.Environment].DebugHTTP { - log.Printf("dispatching queries to %s", tlsConfig.Logging) - } - loggerTLS.QueryLog( - types.QueryLog, - data, - node.Environment, - node.UUID, - queryData.Name, - queryData.Status, - envsmap[node.Environment].DebugHTTP) -} - // Function to handle on-demand queries to osquery nodes func queryReadHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricReadReq) @@ -471,9 +359,7 @@ func queryReadHandler(w http.ResponseWriter, r *http.Request) { log.Printf("Response: %s", string(response)) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, response) incMetric(metricReadOK) } @@ -531,61 +417,24 @@ func queryWriteHandler(w http.ResponseWriter, r *http.Request) { log.Printf("Response: %s", string(response)) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, response) incMetric(metricWriteOK) } -// Helper to process on-demand query result logs -func processLogQueryResult(queries types.QueryWriteQueries, statuses types.QueryWriteStatuses, nodeKey string, environment string) { - // Retrieve node - node, err := nodesmgr.GetByKey(nodeKey) - if err != nil { - log.Printf("error retrieving node %s", err) - } - // Tap into results so we can update internal metrics - for q, r := range queries { - // Dispatch query name, result and status - d := types.QueryWriteData{ - Name: q, - Result: r, - Status: statuses[q], - } - go dispatchQueries(d, node) - // Update internal metrics per query - var err error - if statuses[q] != 0 { - err = queriesmgr.IncError(q) - } else { - err = queriesmgr.IncExecution(q) - } - if err != nil { - log.Printf("error updating query %s", err) - } - // Add a record for this query - if err := queriesmgr.TrackExecution(q, node.UUID, statuses[q]); err != nil { - log.Printf("error adding query execution %s", err) - } - // Check if query is completed - if err := queriesmgr.VerifyComplete(q); err != nil { - log.Printf("error verifying and completing query %s", err) - } - } -} - // Function to handle the endpoint for quick enrollment script distribution func quickEnrollHandler(w http.ResponseWriter, r *http.Request) { - // FIXME metrics + incMetric(metricOnelinerReq) // Retrieve environment variable vars := mux.Vars(r) env, ok := vars["environment"] if !ok { + incMetric(metricOnelinerErr) log.Println("Environment is missing") return } // Check if environment is valid if !envs.Exists(env) { + incMetric(metricOnelinerErr) log.Printf("error unknown environment (%s)", env) return } @@ -593,29 +442,34 @@ func quickEnrollHandler(w http.ResponseWriter, r *http.Request) { utils.DebugHTTPDump(r, envsmap[env].DebugHTTP, true) e, err := envs.Get(env) if err != nil { + incMetric(metricOnelinerErr) log.Printf("error getting environment %v", err) return } // Retrieve type of script script, ok := vars["script"] if !ok { + incMetric(metricOnelinerErr) log.Println("Script is missing") return } // Retrieve SecretPath variable secretPath, ok := vars["secretpath"] if !ok { + incMetric(metricOnelinerErr) log.Println("Path is missing") return } // Check if provided SecretPath is valid and is not expired if strings.HasPrefix(script, "enroll") { if !checkValidEnrollSecretPath(env, secretPath) { + incMetric(metricOnelinerErr) log.Println("Invalid Path") return } } else if strings.HasPrefix(script, "remove") { if !checkValidRemoveSecretPath(env, secretPath) { + incMetric(metricOnelinerErr) log.Println("Invalid Path") return } @@ -623,81 +477,13 @@ func quickEnrollHandler(w http.ResponseWriter, r *http.Request) { // Prepare response with the script quickScript, err := environments.QuickAddScript(projectName, script, e) if err != nil { + incMetric(metricOnelinerErr) log.Printf("error getting script %v", err) return } // Send response - w.Header().Set("Content-Type", TextPlainUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(quickScript)) -} - -// Function to initialize a file carve from a node -func processCarveInit(req types.CarveInitRequest, sessionid, environment string) error { - // Retrieve node - node, err := nodesmgr.GetByKey(req.NodeKey) - if err != nil { - incMetric(metricInitErr) - log.Printf("error retrieving node %s", err) - return err - } - // Prepare carve to initialize - carve := carves.CarvedFile{ - CarveID: req.CarveID, - RequestID: req.RequestID, - SessionID: sessionid, - UUID: node.UUID, - Environment: environment, - CarveSize: req.CarveSize, - BlockSize: req.BlockSize, - TotalBlocks: req.BlockCount, - CompletedBlocks: 0, - Status: carves.StatusInitialized, - } - // Create File Carve - err = filecarves.CreateCarve(carve) - if err != nil { - incMetric(metricInitErr) - log.Printf("error creating CarvedFile %v", err) - return err - } - return nil -} - -// Function to process one block from a file carve -// FIXME it can be more efficient on db access -func processCarveBlock(req types.CarveBlockRequest, environment string) { - // Prepare carve block - block := carves.CarvedBlock{ - RequestID: req.RequestID, - SessionID: req.SessionID, - Environment: environment, - BlockID: req.BlockID, - Data: req.Data, - Size: len(req.Data), - } - // Create Block - if err := filecarves.CreateBlock(block); err != nil { - incMetric(metricBlockErr) - log.Printf("error creating CarvedBlock %v", err) - } - // Bump block completion - if err := filecarves.CompleteBlock(req.SessionID); err != nil { - incMetric(metricBlockErr) - log.Printf("error completing block %v", err) - } - // If it is completed, set status - if filecarves.Completed(req.SessionID) { - if err := filecarves.ChangeStatus(carves.StatusCompleted, req.SessionID); err != nil { - incMetric(metricBlockErr) - log.Printf("error completing carve %v", err) - } - } else { - if err := filecarves.ChangeStatus(carves.StatusInProgress, req.SessionID); err != nil { - incMetric(metricBlockErr) - log.Printf("error progressing carve %v", err) - } - } + utils.HTTPResponse(w, utils.TextPlainUTF8, http.StatusOK, []byte(quickScript)) + incMetric(metricOnelinerOk) } // Function to handle the initialization of the file carver @@ -760,9 +546,7 @@ func carveInitHandler(w http.ResponseWriter, r *http.Request) { log.Printf("Response: %s", string(response)) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, response) incMetric(metricInitOK) } @@ -813,8 +597,6 @@ func carveBlockHandler(w http.ResponseWriter, r *http.Request) { log.Printf("Response: %s", string(response)) } // Send response - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, response) incMetric(metricBlockOK) } diff --git a/tls/logs.go b/tls/logs.go new file mode 100644 index 00000000..43b9bd70 --- /dev/null +++ b/tls/logs.go @@ -0,0 +1,137 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/jmpsec/osctrl/nodes" + "github.com/jmpsec/osctrl/types" +) + +// Helper to process logs +func processLogs(data json.RawMessage, logType, environment, ipaddress string) { + // Parse log to extract metadata + var logs []types.LogGenericData + err := json.Unmarshal(data, &logs) + if err != nil { + // FIXME metrics for this + log.Printf("error parsing log %s %v", string(data), err) + } + // Iterate through received messages to extract metadata + var uuids, hosts, names, users, osqueryusers, hashes, dhashes, osqueryversions []string + for _, l := range logs { + uuids = append(uuids, l.HostIdentifier) + hosts = append(hosts, l.Decorations.Hostname) + names = append(names, l.Decorations.LocalHostname) + users = append(users, l.Decorations.Username) + osqueryusers = append(osqueryusers, l.Decorations.OsqueryUser) + hashes = append(hashes, l.Decorations.ConfigHash) + dhashes = append(dhashes, l.Decorations.DaemonHash) + osqueryversions = append(osqueryversions, l.Version) + } + // FIXME it only uses the first element from the []string that uniq returns + uuid := uniq(uuids)[0] + user := uniq(users)[0] + osqueryuser := uniq(osqueryusers)[0] + host := uniq(hosts)[0] + name := uniq(names)[0] + hash := uniq(hashes)[0] + dhash := uniq(dhashes)[0] + osqueryversion := uniq(osqueryversions)[0] + // Dispatch logs and update metadata + dispatchLogs(data, uuid, ipaddress, user, osqueryuser, host, name, hash, dhash, osqueryversion, logType, environment) +} + +// Helper to dispatch logs +func dispatchLogs(data []byte, uuid, ipaddress, user, osqueryuser, hostname, localname, hash, dhash, osqueryversion, logType, environment string) { + // Use metadata to update record + if err := nodesmgr.UpdateMetadataByUUID(user, osqueryuser, hostname, localname, ipaddress, hash, dhash, osqueryversion, uuid); err != nil { + log.Printf("error updating metadata %s", err) + } + // Send data to storage + // FIXME allow multiple types of logging + if envsmap[environment].DebugHTTP { + log.Printf("dispatching logs to %s", tlsConfig.Logging) + } + loggerTLS.Log( + logType, + data, + environment, + uuid, + envsmap[environment].DebugHTTP) + // Refresh last logging request + if logType == types.StatusLog { + err := nodesmgr.RefreshLastStatus(uuid) + if err != nil { + log.Printf("error refreshing last status %v", err) + } + } + if logType == types.ResultLog { + if err := nodesmgr.RefreshLastResult(uuid); err != nil { + log.Printf("error refreshing last result %v", err) + } + } +} + +// Helper to dispatch queries +func dispatchQueries(queryData types.QueryWriteData, node nodes.OsqueryNode) { + // Prepare data to send + data, err := json.Marshal(queryData) + if err != nil { + log.Printf("error preparing data %v", err) + } + // Refresh last query write request + if err := nodesmgr.RefreshLastQueryWrite(node.UUID); err != nil { + log.Printf("error refreshing last query write %v", err) + } + // Send data to storage + // FIXME allow multiple types of logging + if envsmap[node.Environment].DebugHTTP { + log.Printf("dispatching queries to %s", tlsConfig.Logging) + } + loggerTLS.QueryLog( + types.QueryLog, + data, + node.Environment, + node.UUID, + queryData.Name, + queryData.Status, + envsmap[node.Environment].DebugHTTP) +} + +// Helper to process on-demand query result logs +func processLogQueryResult(queries types.QueryWriteQueries, statuses types.QueryWriteStatuses, nodeKey string, environment string) { + // Retrieve node + node, err := nodesmgr.GetByKey(nodeKey) + if err != nil { + log.Printf("error retrieving node %s", err) + } + // Tap into results so we can update internal metrics + for q, r := range queries { + // Dispatch query name, result and status + d := types.QueryWriteData{ + Name: q, + Result: r, + Status: statuses[q], + } + go dispatchQueries(d, node) + // Update internal metrics per query + var err error + if statuses[q] != 0 { + err = queriesmgr.IncError(q) + } else { + err = queriesmgr.IncExecution(q) + } + if err != nil { + log.Printf("error updating query %s", err) + } + // Add a record for this query + if err := queriesmgr.TrackExecution(q, node.UUID, statuses[q]); err != nil { + log.Printf("error adding query execution %s", err) + } + // Check if query is completed + if err := queriesmgr.VerifyComplete(q); err != nil { + log.Printf("error verifying and completing query %s", err) + } + } +} diff --git a/utils/utils.go b/utils/utils.go index f688e2b8..5f06f828 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -2,6 +2,7 @@ package utils import ( "crypto/tls" + "encoding/json" "io" "io/ioutil" "log" @@ -16,6 +17,12 @@ const JSONApplication string = "application/json" // JSONApplicationUTF8 for Content-Type headers, UTF charset const JSONApplicationUTF8 string = JSONApplication + "; charset=UTF-8" +// TextPlain for Content-Type headers +const TextPlain string = "text/plain" + +// TextPlainUTF8 for Content-Type headers, UTF charset +const TextPlainUTF8 string = TextPlain + "; charset=UTF-8" + // ContentType for header key const ContentType string = "Content-Type" @@ -82,3 +89,18 @@ func DebugHTTPDump(r *http.Request, debugCheck bool, showBody bool) { log.Println("-------------------------------------------------------end") } } + +// HTTPResponse - Helper to send HTTP response +func HTTPResponse(w http.ResponseWriter, cType string, code int, data interface{}) { + if cType != "" { + w.Header().Set(ContentType, cType) + } + content, err := json.Marshal(data) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + log.Printf("error serializing response: %v", err) + content = []byte("error serializing response") + } + w.WriteHeader(code) + _, _ = w.Write(content) +} From d88f372d8d8af50eb3655bb7370961b3968dce5e Mon Sep 17 00:00:00 2001 From: Javier Marcos Date: Sat, 7 Mar 2020 18:37:27 -0800 Subject: [PATCH 2/3] Refactor HTTP response --- admin/handlers-post.go | 190 +++--------------- admin/handlers-tokens.go | 13 +- admin/handlers.go | 4 +- admin/json-carves.go | 12 +- admin/json-logs.go | 23 +-- admin/json-nodes.go | 23 +-- admin/json-queries.go | 12 +- admin/json-stats.go | 12 +- admin/templates/carves.html | 2 +- .../templates/components/page-aside-left.html | 4 +- admin/templates/node.html | 2 +- admin/templates/queries-run.html | 4 +- admin/templates/queries.html | 2 +- admin/templates/table.html | 2 +- api/handlers-environments.go | 4 +- api/handlers.go | 4 +- deploy/osquery/osquery-dev.json | 5 +- tls/handlers-tls.go | 89 +++----- utils/utils.go | 17 +- 19 files changed, 99 insertions(+), 325 deletions(-) diff --git a/admin/handlers-post.go b/admin/handlers-post.go index 798ff5f6..2856e962 100644 --- a/admin/handlers-post.go +++ b/admin/handlers-post.go @@ -55,21 +55,11 @@ func loginPOSTHandler(w http.ResponseWriter, r *http.Request) { } } } - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - responseMessage = "error formating response" - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - responseCode = http.StatusInternalServerError - response = []byte(responseMessage) - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Login response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } @@ -109,21 +99,11 @@ func logoutHandler(w http.ResponseWriter, r *http.Request) { } } } - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - responseMessage = "error formating response" - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - responseCode = http.StatusInternalServerError - response = []byte(responseMessage) - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Logout response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } @@ -259,18 +239,11 @@ func queryRunPOSTHandler(w http.ResponseWriter, r *http.Request) { log.Printf("%s", responseMessage) } send_response: - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - log.Printf("error formating response [ %v ]", err) - responseCode = http.StatusInternalServerError - response = []byte("error formating response") - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Query run response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } @@ -418,18 +391,11 @@ func carvesRunPOSTHandler(w http.ResponseWriter, r *http.Request) { log.Printf("%s", responseMessage) } send_response: - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - log.Printf("error formating response [ %v ]", err) - responseCode = http.StatusInternalServerError - response = []byte("error formating response") - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Carve run response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } @@ -501,21 +467,11 @@ func queryActionsPOSTHandler(w http.ResponseWriter, r *http.Request) { } } send_response: - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - responseMessage = "error formating response" - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - responseCode = http.StatusInternalServerError - response = []byte(responseMessage) - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Query run response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } @@ -569,21 +525,11 @@ func carvesActionsPOSTHandler(w http.ResponseWriter, r *http.Request) { } } send_response: - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - responseMessage = "error formating response" - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - responseCode = http.StatusInternalServerError - response = []byte(responseMessage) - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Carves action response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } @@ -663,21 +609,11 @@ func confPOSTHandler(w http.ResponseWriter, r *http.Request) { } } send_response: - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - responseMessage = "error formating response" - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - responseCode = http.StatusInternalServerError - response = []byte(responseMessage) - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Configuration response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } @@ -751,21 +687,11 @@ func intervalsPOSTHandler(w http.ResponseWriter, r *http.Request) { } } } - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - responseMessage = "error formating response" - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - responseCode = http.StatusInternalServerError - response = []byte(responseMessage) - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Intervals response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } @@ -857,21 +783,11 @@ func expirationPOSTHandler(w http.ResponseWriter, r *http.Request) { } } } - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - responseMessage = "error formating response" - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - responseCode = http.StatusInternalServerError - response = []byte(responseMessage) - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Expiration response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } @@ -927,21 +843,11 @@ func nodeActionsPOSTHandler(w http.ResponseWriter, r *http.Request) { } } } - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - responseMessage = "error formating response" - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s", responseMessage) - } - responseCode = http.StatusInternalServerError - response = []byte(responseMessage) - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Multi-node action response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } @@ -1044,21 +950,11 @@ func envsPOSTHandler(w http.ResponseWriter, r *http.Request) { } } send_response: - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - responseMessage = "error formating response" - responseCode = http.StatusInternalServerError - response = []byte(responseMessage) - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Environments response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } @@ -1167,21 +1063,11 @@ func settingsPOSTHandler(w http.ResponseWriter, r *http.Request) { } } send_response: - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - responseMessage = "error formating response" - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - responseCode = http.StatusInternalServerError - response = []byte(responseMessage) - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Settings response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } @@ -1325,21 +1211,11 @@ func usersPOSTHandler(w http.ResponseWriter, r *http.Request) { } } send_response: - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - responseMessage = "error formating response" - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - responseCode = http.StatusInternalServerError - response = []byte(responseMessage) - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Users response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } @@ -1419,20 +1295,10 @@ func enrollPOSTHandler(w http.ResponseWriter, r *http.Request) { } } send_response: - // Prepare response - response, err := json.Marshal(AdminResponse{Message: responseMessage}) - if err != nil { - responseMessage = "error formating response" - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - responseCode = http.StatusInternalServerError - response = []byte(responseMessage) - } - // Send response + // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Configuration response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, response) + utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } diff --git a/admin/handlers-tokens.go b/admin/handlers-tokens.go index 26cdd29a..3723acaf 100644 --- a/admin/handlers-tokens.go +++ b/admin/handlers-tokens.go @@ -58,17 +58,8 @@ func tokensGETHandler(w http.ResponseWriter, r *http.Request) { ExpiresTS: user.TokenExpire.String(), } } - // Serialize JSON - returnedJSON, err := json.Marshal(returned) - if err != nil { - log.Printf("error serializing JSON %v", err) - incMetric(metricTokenErr) - return - } - // Header to serve JSON - w.Header().Set(utils.ContentType, utils.JSONApplicationUTF8) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(returnedJSON) + // Serve JSON + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returned) incMetric(metricTokenOK) } diff --git a/admin/handlers.go b/admin/handlers.go index 0f814a84..caa751e4 100644 --- a/admin/handlers.go +++ b/admin/handlers.go @@ -18,8 +18,8 @@ const ( // Empty default osquery configuration const emptyConfiguration string = "data/osquery-empty.json" -var errorContent = []byte("❌") -var okContent = []byte("✅") +const errorContent = "❌" +const okContent = "✅" // Handle health requests func healthHTTPHandler(w http.ResponseWriter, r *http.Request) { diff --git a/admin/json-carves.go b/admin/json-carves.go index 03cecbe9..e22c56e0 100644 --- a/admin/json-carves.go +++ b/admin/json-carves.go @@ -1,7 +1,6 @@ package main import ( - "encoding/json" "log" "net/http" @@ -134,14 +133,7 @@ func jsonCarvesHandler(w http.ResponseWriter, r *http.Request) { returned := ReturnedCarves{ Data: cJSON, } - // Serialize JSON - returnedJSON, err := json.Marshal(returned) - if err != nil { - incMetric(metricJSONErr) - log.Printf("error serializing JSON %v", err) - return - } - // Header to serve JSON - utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) + // Serve JSON + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returned) incMetric(metricJSONOK) } diff --git a/admin/json-logs.go b/admin/json-logs.go index 5b971eae..b9bea584 100644 --- a/admin/json-logs.go +++ b/admin/json-logs.go @@ -1,7 +1,6 @@ package main import ( - "encoding/json" "log" "net/http" "strconv" @@ -142,15 +141,8 @@ func jsonLogsHandler(w http.ResponseWriter, r *http.Request) { returned := ReturnedLogs{ Data: logJSON, } - // Serialize JSON - returnedJSON, err := json.Marshal(returned) - if err != nil { - log.Printf("error serializing JSON %v", err) - incMetric(metricJSONErr) - return - } - // Header to serve JSON - utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) + // Serialize and serve JSON + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returned) incMetric(metricJSONOK) } @@ -190,14 +182,7 @@ func jsonQueryLogsHandler(w http.ResponseWriter, r *http.Request) { returned := ReturnedQueryLogs{ Data: queryLogJSON, } - // Serialize JSON - returnedJSON, err := json.Marshal(returned) - if err != nil { - log.Printf("error serializing JSON %v", err) - incMetric(metricJSONErr) - return - } - // Header to serve JSON - utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) + // Serialize and serve JSON + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returned) incMetric(metricJSONOK) } diff --git a/admin/json-nodes.go b/admin/json-nodes.go index 292eab6c..121acec1 100644 --- a/admin/json-nodes.go +++ b/admin/json-nodes.go @@ -1,7 +1,6 @@ package main import ( - "encoding/json" "log" "net/http" @@ -95,15 +94,8 @@ func jsonEnvironmentHandler(w http.ResponseWriter, r *http.Request) { returned := ReturnedNodes{ Data: nJSON, } - // Serialize JSON - returnedJSON, err := json.Marshal(returned) - if err != nil { - log.Printf("error serializing JSON %v", err) - incMetric(metricJSONErr) - return - } - // Header to serve JSON - utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) + // Serve JSON + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returned) incMetric(metricJSONOK) } @@ -159,14 +151,7 @@ func jsonPlatformHandler(w http.ResponseWriter, r *http.Request) { returned := ReturnedNodes{ Data: nJSON, } - // Serialize JSON - returnedJSON, err := json.Marshal(returned) - if err != nil { - log.Printf("error serializing JSON %v", err) - incMetric(metricJSONErr) - return - } - // Header to serve JSON - utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) + // Serve JSON + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returned) incMetric(metricJSONOK) } diff --git a/admin/json-queries.go b/admin/json-queries.go index fc4db2bf..7afbda41 100644 --- a/admin/json-queries.go +++ b/admin/json-queries.go @@ -1,7 +1,6 @@ package main import ( - "encoding/json" "log" "net/http" @@ -126,14 +125,7 @@ func jsonQueryHandler(w http.ResponseWriter, r *http.Request) { returned := ReturnedQueries{ Data: qJSON, } - // Serialize JSON - returnedJSON, err := json.Marshal(returned) - if err != nil { - log.Printf("error serializing JSON %v", err) - incMetric(metricJSONErr) - return - } - // Header to serve JSON - utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) + // Serve JSON + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returned) incMetric(metricJSONOK) } diff --git a/admin/json-stats.go b/admin/json-stats.go index c3d4be96..736f3781 100644 --- a/admin/json-stats.go +++ b/admin/json-stats.go @@ -1,7 +1,6 @@ package main import ( - "encoding/json" "log" "net/http" @@ -62,14 +61,7 @@ func jsonStatsHandler(w http.ResponseWriter, r *http.Request) { return } } - // Serialize JSON - returnedJSON, err := json.Marshal(stats) - if err != nil { - incMetric(metricAdminErr) - log.Printf("error serializing JSON %v", err) - return - } - // Header to serve JSON - utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returnedJSON) + // Serve JSON + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, stats) incMetric(metricJSONOK) } diff --git a/admin/templates/carves.html b/admin/templates/carves.html index 21a4a97b..cd23a63f 100644 --- a/admin/templates/carves.html +++ b/admin/templates/carves.html @@ -21,7 +21,7 @@
- {{ .Title }} + {{ .Title }}
diff --git a/admin/templates/queries.html b/admin/templates/queries.html index 5b09309d..41328980 100644 --- a/admin/templates/queries.html +++ b/admin/templates/queries.html @@ -21,7 +21,7 @@
- {{ .Title }} + {{ .Title }}