From 4a1cc40fdcd8c3d8596be419640f2a335451c47f Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 2 Oct 2024 10:02:27 +0200 Subject: [PATCH 1/2] Add proxy for network and resolution endpoints Forward the requests to the network and resolution endpoints to the Supervisor. This will allow the landing page to get insights when the Supervisor detects issues on first startup (e.g. problematic DNS servers). The network endpoint will allow to resolve these issues. Note: This makes the two mentioned endpoints available unauthenticated. However, the landing page will be replaced with Core as soon as it has been downloaded, so this unauthenticated forwarding will only be during a brief phase on initial setup. A bad actor could also just setup a new user from the onboarding page at this point in time. So this effectly does not change the security posture. --- http.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 2 ++ 2 files changed, 58 insertions(+) diff --git a/http.go b/http.go index e3bd110..3148a6d 100644 --- a/http.go +++ b/http.go @@ -4,8 +4,12 @@ import ( "io" "log" "net/http" + "net/http/httputil" + "net/url" "os" + "path" "regexp" + "strings" ) var regexASCII = regexp.MustCompile(`\[\d+m`) @@ -48,3 +52,55 @@ func httpLogs(w http.ResponseWriter, r *http.Request) { logs := regexASCII.ReplaceAllLiteralString(string(data), "") w.Write([]byte(logs)) } + +func httpSupervisorProxy(w http.ResponseWriter, r *http.Request) { + log.Printf("Proxy request: %s", r.URL.Path) + + // Base Supervisor URL + u, err := url.Parse("http://supervisor/") + if err != nil { + // Handle error in parsing URL + w.Write([]byte(err.Error())) + return + } + + // Strip "/supervisor/" from the path + trimmedPath := strings.TrimPrefix(r.URL.Path, "/supervisor/") + + // Split the path into parts, the first part is the subpath (e.g., resolution or network) + parts := strings.SplitN(trimmedPath, "/", 2) + if len(parts) < 2 { + http.Error(w, "Bad request: missing path", http.StatusBadRequest) + log.Printf("Invalid path: %s", r.URL.Path) + return + } + + // Extract subpath (e.g., resolution or network) + subPath := parts[0] + + // The remainder path (after the subpath) to be sanitized + remainderPath := "/" + parts[1] + + // Clean the remainder path to avoid path traversal attacks + cleanPath := path.Clean(remainderPath) + + // Ensure it's under the intended subpath (e.g., /resolution or /network) + if cleanPath != remainderPath { + http.Error(w, "Forbidden: Invalid path", http.StatusForbidden) + log.Printf("Blocked path traversal attempt: %s", cleanPath) + return + } + + // Update the request path to be forwarded + r.URL.Path = "/" + subPath + cleanPath + log.Printf("Sanitized and remapped path: %s", r.URL.Path) + + // Create the reverse proxy + proxy := httputil.NewSingleHostReverseProxy(u) + + // Add authorization header + r.Header.Add("Authorization", "Bearer "+os.Getenv("SUPERVISOR_TOKEN")) + + // Forward the request + proxy.ServeHTTP(w, r) +} diff --git a/main.go b/main.go index cc284ab..bc0da19 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,8 @@ func main() { http.HandleFunc("/api/", httpUnauthorized) http.HandleFunc("/auth/token", httpBad) http.HandleFunc("/observer/logs", httpLogs) + http.HandleFunc("/supervisor/resolution/", httpSupervisorProxy) + http.HandleFunc("/supervisor/network/", httpSupervisorProxy) // Serve static help files staticFiles := http.FileServer(http.Dir(wwwRoot)) From 9856f534f0d571a926d45605e742a6d620924243 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 2 Oct 2024 10:58:22 +0200 Subject: [PATCH 2/2] Be less verbose in proxying --- http.go | 1 - 1 file changed, 1 deletion(-) diff --git a/http.go b/http.go index 3148a6d..5f93025 100644 --- a/http.go +++ b/http.go @@ -93,7 +93,6 @@ func httpSupervisorProxy(w http.ResponseWriter, r *http.Request) { // Update the request path to be forwarded r.URL.Path = "/" + subPath + cleanPath - log.Printf("Sanitized and remapped path: %s", r.URL.Path) // Create the reverse proxy proxy := httputil.NewSingleHostReverseProxy(u)