From eb7512cd0ebe740423f44367353f141827256057 Mon Sep 17 00:00:00 2001 From: Javier Marcos Date: Thu, 29 Aug 2019 10:25:10 -0700 Subject: [PATCH] Upload certificate from admin --- cmd/admin/handlers-post.go | 133 ++++++++++++++++-- cmd/admin/handlers.go | 6 +- cmd/admin/main.go | 25 ++-- cmd/admin/static/js/enrolls.js | 22 +++ .../templates/components/page-modals.html | 23 +++ cmd/admin/templates/enroll.html | 20 ++- cmd/admin/types-requests.go | 6 + cmd/tls/handlers-tls.go | 6 +- cmd/tls/main.go | 6 +- pkg/environments/environments.go | 18 ++- pkg/environments/flags.go | 10 ++ 11 files changed, 238 insertions(+), 37 deletions(-) diff --git a/cmd/admin/handlers-post.go b/cmd/admin/handlers-post.go index d58a6168..9361a8c6 100644 --- a/cmd/admin/handlers-post.go +++ b/cmd/admin/handlers-post.go @@ -601,21 +601,29 @@ func confPOSTHandler(w http.ResponseWriter, r *http.Request) { } else { // Check CSRF Token if checkCSRFToken(ctx["csrftoken"], c.CSRFToken) { - configuration, err := base64.StdEncoding.DecodeString(c.ConfigurationB64) - if err != nil { - responseMessage = "error decoding configuration" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - err = envs.UpdateConfiguration(environmentVar, string(configuration)) + if c.ConfigurationB64 != "" { + configuration, err := base64.StdEncoding.DecodeString(c.ConfigurationB64) if err != nil { - responseMessage = "error saving configuration" + responseMessage = "error decoding configuration" responseCode = http.StatusInternalServerError if settingsmgr.DebugService(settings.ServiceAdmin) { log.Printf("DebugService: %s %v", responseMessage, err) } + } else { + err = envs.UpdateConfiguration(environmentVar, string(configuration)) + if err != nil { + responseMessage = "error saving configuration" + responseCode = http.StatusInternalServerError + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: %s %v", responseMessage, err) + } + } + } + } else { + responseMessage = "empty configuration" + responseCode = http.StatusInternalServerError + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: %s %v", responseMessage, err) } } } else { @@ -688,6 +696,24 @@ func intervalsPOSTHandler(w http.ResponseWriter, r *http.Request) { log.Printf("DebugService: %s %v", responseMessage, err) } } + // After updating interval, you need to re-generate flags + flags, err := envs.GenerateFlagsEnv(environmentVar, "", "") + if err == nil { + // Update flags in the newly created environment + if err := envs.UpdateFlags(environmentVar, flags); err != nil { + responseMessage = "error updating flags" + responseCode = http.StatusInternalServerError + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: %s %v", responseMessage, err) + } + } + } else { + responseMessage = "error re-generating flags" + responseCode = http.StatusInternalServerError + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: %s %v", responseMessage, err) + } + } } else { responseMessage = "invalid CSRF token" responseCode = http.StatusInternalServerError @@ -1214,3 +1240,90 @@ func usersPOSTHandler(w http.ResponseWriter, r *http.Request) { log.Println("DebugService: Users response sent") } } + +// Handler POST requests enroll data +func enrollPOSTHandler(w http.ResponseWriter, r *http.Request) { + responseMessage := "Enroll data saved successfully" + responseCode := http.StatusOK + utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) + vars := mux.Vars(r) + // Extract environment and verify + environmentVar, ok := vars["environment"] + if !ok || !envs.Exists(environmentVar) { + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: error getting environment") + } + return + } + // Verify environment + if !envs.Exists(environmentVar) { + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: error unknown environment (%s)", environmentVar) + } + return + } + var e EnrollRequest + // Get context data + ctx := r.Context().Value(contextKey("session")).(contextValue) + // Parse request JSON body + err := json.NewDecoder(r.Body).Decode(&e) + if err != nil { + responseMessage = "error parsing POST body" + responseCode = http.StatusInternalServerError + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: %s %v", responseMessage, err) + } + } else { + // Check CSRF Token + if checkCSRFToken(ctx["csrftoken"], e.CSRFToken) { + if e.CertificateB64 != "" { + certificate, err := base64.StdEncoding.DecodeString(e.CertificateB64) + if err != nil { + responseMessage = "error decoding certificate" + responseCode = http.StatusInternalServerError + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: %s %v", responseMessage, err) + } + } else { + err = envs.UpdateCertificate(environmentVar, string(certificate)) + if err != nil { + responseMessage = "error saving certificate" + responseCode = http.StatusInternalServerError + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: %s %v", responseMessage, err) + } + } + } + } else { + responseMessage = "empty certificate" + responseCode = http.StatusInternalServerError + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: %s %v", responseMessage, err) + } + } + } else { + responseMessage = "invalid CSRF token" + responseCode = http.StatusInternalServerError + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: %s %v", responseMessage, err) + } + } + } + // 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 + w.Header().Set("Content-Type", JSONApplicationUTF8) + w.WriteHeader(responseCode) + _, _ = w.Write(response) + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Configuration response sent") + } +} diff --git a/cmd/admin/handlers.go b/cmd/admin/handlers.go index 9f8de825..f9a07210 100644 --- a/cmd/admin/handlers.go +++ b/cmd/admin/handlers.go @@ -22,13 +22,13 @@ const JSONApplicationUTF8 string = JSONApplication + "; charset=UTF-8" // Empty default osquery configuration const emptyConfiguration string = "data/osquery-empty.json" -// Handle testing requests -func testingHTTPHandler(w http.ResponseWriter, r *http.Request) { +// Handle health requests +func healthHTTPHandler(w http.ResponseWriter, r *http.Request) { utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) // Send response w.Header().Set("Content-Type", JSONApplicationUTF8) w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("test")) + _, _ = w.Write([]byte("✅")) } // Handle error requests diff --git a/cmd/admin/main.go b/cmd/admin/main.go index eadf19fc..9d6ab678 100644 --- a/cmd/admin/main.go +++ b/cmd/admin/main.go @@ -39,8 +39,8 @@ const ( serviceDescription string = "Admin service for osctrl" // Application description appDescription string = serviceDescription + ", a fast and efficient osquery management" - // Default endpoint to handle HTTP testing - testingPath string = "/testing" + // Default endpoint to handle HTTP health + healthPath string = "/health" // Default endpoint to handle HTTP errors errorPath string = "/error" // Default service configuration file @@ -95,7 +95,7 @@ var validAuth = map[string]bool{ settings.AuthJSON: true, } var validLogging = map[string]bool{ - settings.LoggingDB: true, + settings.LoggingDB: true, } // Function to load the configuration file @@ -256,8 +256,8 @@ func main() { routerAdmin.HandleFunc("/login", loginGETHandler).Methods("GET") routerAdmin.HandleFunc("/login", loginPOSTHandler).Methods("POST") } - // Admin: testing - routerAdmin.HandleFunc(testingPath, testingHTTPHandler).Methods("GET") + // Admin: health of service + routerAdmin.HandleFunc(healthPath, healthHTTPHandler).Methods("GET") // Admin: error routerAdmin.HandleFunc(errorPath, errorHTTPHandler).Methods("GET") // Admin: favicon @@ -324,6 +324,7 @@ func main() { routerAdmin.Handle("/intervals/{environment}", handlerAuthCheck(http.HandlerFunc(intervalsPOSTHandler))).Methods("POST") // Admin: nodes enroll routerAdmin.Handle("/enroll/{environment}", handlerAuthCheck(http.HandlerFunc(enrollGETHandler))).Methods("GET") + routerAdmin.Handle("/enroll/{environment}", handlerAuthCheck(http.HandlerFunc(enrollPOSTHandler))).Methods("POST") routerAdmin.Handle("/expiration/{environment}", handlerAuthCheck(http.HandlerFunc(expirationPOSTHandler))).Methods("POST") // Admin: server settings routerAdmin.Handle("/settings/{service}", handlerAuthCheck(http.HandlerFunc(settingsGETHandler))).Methods("GET") @@ -331,16 +332,14 @@ func main() { // Admin: manage environments routerAdmin.Handle("/environments", handlerAuthCheck(http.HandlerFunc(envsGETHandler))).Methods("GET") routerAdmin.Handle("/environments", handlerAuthCheck(http.HandlerFunc(envsPOSTHandler))).Methods("POST") - // Admin: manage users if authentication is enabled - if adminConfig.Auth != settings.AuthNone { - routerAdmin.Handle("/users", handlerAuthCheck(http.HandlerFunc(usersGETHandler))).Methods("GET") - routerAdmin.Handle("/users", handlerAuthCheck(http.HandlerFunc(usersPOSTHandler))).Methods("POST") - // logout - routerAdmin.Handle("/logout", handlerAuthCheck(http.HandlerFunc(logoutHandler))).Methods("POST") - } + // Admin: manage users + routerAdmin.Handle("/users", handlerAuthCheck(http.HandlerFunc(usersGETHandler))).Methods("GET") + routerAdmin.Handle("/users", handlerAuthCheck(http.HandlerFunc(usersPOSTHandler))).Methods("POST") + // logout + routerAdmin.Handle("/logout", handlerAuthCheck(http.HandlerFunc(logoutHandler))).Methods("POST") // SAML ACS - if adminConfig.Auth == settings.AuthNone { + if adminConfig.Auth == settings.AuthSAML { routerAdmin.PathPrefix("/saml/").Handler(samlMiddleware) } diff --git a/cmd/admin/static/js/enrolls.js b/cmd/admin/static/js/enrolls.js index f4d22c9d..0415d1c1 100644 --- a/cmd/admin/static/js/enrolls.js +++ b/cmd/admin/static/js/enrolls.js @@ -24,3 +24,25 @@ function extendRemoveLink() { function expireRemoveLink() { genericLinkAction('remove', 'expire'); } + +function confirmUploadCertificate() { + $('#certificate_action').click(function () { + $('#certificateModal').modal('hide'); + uploadCertificate(); + }); + $("#certificateModal").modal(); +} + +function uploadCertificate() { + var _csrftoken = $("#csrftoken").val(); + var _blob = $('#certificate').data('CodeMirrorInstance'); + var _certificate = _blob.getValue(); + + var _url = window.location.pathname; + + var data = { + csrftoken: _csrftoken, + certificate: btoa(_certificate), + }; + sendPostRequest(data, _url, window.location.pathname, false); +} diff --git a/cmd/admin/templates/components/page-modals.html b/cmd/admin/templates/components/page-modals.html index 732d80fa..c35dd078 100644 --- a/cmd/admin/templates/components/page-modals.html +++ b/cmd/admin/templates/components/page-modals.html @@ -136,4 +136,27 @@ + + + {{ end }} diff --git a/cmd/admin/templates/enroll.html b/cmd/admin/templates/enroll.html index 38f2791b..def06ac6 100644 --- a/cmd/admin/templates/enroll.html +++ b/cmd/admin/templates/enroll.html @@ -224,8 +224,16 @@
-
- Enrollment certificate: +
+
+ Enrollment certificate: +
+
+ +
@@ -262,6 +270,14 @@ hljs.initHighlightingOnLoad(); $(document).ready(function() { + // Codemirror editor for configuration + var certificateBlob = CodeMirror.fromTextArea(document.getElementById("certificate"), { + mode: 'text/plain', + lineNumbers: true + }); + $('#certificate').data('CodeMirrorInstance', certificateBlob); + certificateBlob.setSize("100%", "100%"); + // Highlight.js code element initialization $('code').each(function(i, block) { hljs.highlightBlock(block); diff --git a/cmd/admin/types-requests.go b/cmd/admin/types-requests.go index 788959ba..130dda60 100644 --- a/cmd/admin/types-requests.go +++ b/cmd/admin/types-requests.go @@ -70,6 +70,12 @@ type ConfigurationRequest struct { ConfigurationB64 string `json:"configuration"` } +// EnrollRequest to receive changes to enroll certificates +type EnrollRequest struct { + CSRFToken string `json:"csrftoken"` + CertificateB64 string `json:"certificate"` +} + // IntervalsRequest to receive changes to intervals type IntervalsRequest struct { CSRFToken string `json:"csrftoken"` diff --git a/cmd/tls/handlers-tls.go b/cmd/tls/handlers-tls.go index 4cfbe585..4ccbc106 100644 --- a/cmd/tls/handlers-tls.go +++ b/cmd/tls/handlers-tls.go @@ -59,12 +59,12 @@ func okHTTPHandler(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("💥")) } -// Handle testing requests -func testingHTTPHandler(w http.ResponseWriter, r *http.Request) { +// Handle health requests +func healthHTTPHandler(w http.ResponseWriter, r *http.Request) { // Send response w.Header().Set("Content-Type", JSONApplicationUTF8) w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("test")) + _, _ = w.Write([]byte("✅")) } // Handle error requests diff --git a/cmd/tls/main.go b/cmd/tls/main.go index 6387ff38..ed64e46b 100644 --- a/cmd/tls/main.go +++ b/cmd/tls/main.go @@ -32,8 +32,8 @@ const ( serviceDescription string = "TLS service for osctrl" // Application description appDescription string = serviceDescription + ", a fast and efficient osquery management" - // Default endpoint to handle HTTP testing - testingPath string = "/testing" + // Default endpoint to handle HTTP health + healthPath string = "/health" // Default endpoint to handle HTTP errors errorPath string = "/error" // Default service configuration file @@ -168,7 +168,7 @@ func main() { // TLS: root routerTLS.HandleFunc("/", okHTTPHandler) // TLS: testing - routerTLS.HandleFunc(testingPath, testingHTTPHandler).Methods("GET") + routerTLS.HandleFunc(healthPath, healthHTTPHandler).Methods("GET") // TLS: error routerTLS.HandleFunc(errorPath, errorHTTPHandler).Methods("GET") // TLS: Specific routes for osquery nodes diff --git a/pkg/environments/environments.go b/pkg/environments/environments.go index 5c600010..bd2980d3 100644 --- a/pkg/environments/environments.go +++ b/pkg/environments/environments.go @@ -14,17 +14,17 @@ const ( // DefaultLogPath as default value for logging data from nodes DefaultLogPath string = "log" // DefaultLogInterval as default interval for logging data from nodes - DefaultLogInterval int = 10 + DefaultLogInterval int = 600 // DefaultConfigPath as default value for configuring nodes DefaultConfigPath string = "config" // DefaultConfigInterval as default interval for configuring nodes - DefaultConfigInterval int = 10 + DefaultConfigInterval int = 300 // DefaultQueryReadPath as default value for distributing on-demand queries to nodes DefaultQueryReadPath string = "read" // DefaultQueryWritePath as default value for collecting results from on-demand queries DefaultQueryWritePath string = "write" // DefaultQueryInterval as default interval for distributing on-demand queries to nodes - DefaultQueryInterval int = 10 + DefaultQueryInterval int = 60 // DefaultCarverInitPath as default init endpoint for the carver DefaultCarverInitPath string = "init" // DefaultCarverBlockPath as default block endpoint for the carver @@ -209,6 +209,18 @@ func (environment *Environment) UpdateConfiguration(name, configuration string) return nil } +// UpdateCertificate to update certificate for an environment +func (environment *Environment) UpdateCertificate(name, certificate string) error { + env, err := environment.Get(name) + if err != nil { + return fmt.Errorf("error getting environment %v", err) + } + if err := environment.DB.Model(&env).Update("certificate", certificate).Error; err != nil { + return fmt.Errorf("Update %v", err) + } + return nil +} + // UpdateFlags to update flags for an environment func (environment *Environment) UpdateFlags(name, flags string) error { env, err := environment.Get(name) diff --git a/pkg/environments/flags.go b/pkg/environments/flags.go index 937cdd2a..320310b8 100644 --- a/pkg/environments/flags.go +++ b/pkg/environments/flags.go @@ -2,6 +2,7 @@ package environments import ( "bytes" + "fmt" "text/template" ) @@ -72,3 +73,12 @@ func GenerateFlags(env TLSEnvironment, secret, certificate string) (string, erro } return tpl.String(), nil } + +// GenerateFlagsEnv to generate flags by environment name +func (environment *Environment) GenerateFlagsEnv(name string, secret, certificate string) (string, error) { + env, err := environment.Get(name) + if err != nil { + return "", fmt.Errorf("error getting environment %v", err) + } + return GenerateFlags(env, secret, certificate) +}