package main import ( "crypto/subtle" "encoding/json" "io" "net/http" "strings" "time" ) func tokenOK(got, expected string) bool { if expected == "" || len(got) != len(expected) { return false } return subtle.ConstantTimeCompare([]byte(got), []byte(expected)) == 1 } func handler(cfg config) http.Handler { mux := http.NewServeMux() detector := &ipDetector{} mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } w.Header().Set("Content-Type", "application/json") w.Header().Set("Cache-Control", "no-store") if cfg.Fingerprint != "" { w.Header().Set("X-RustProxy-Agent-Certificate-SHA256", cfg.Fingerprint) } _, _ = io.WriteString(w, `{"status":"ok"}`) }) mux.HandleFunc("/v1/inventory", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } auth := strings.TrimSpace(r.Header.Get("Authorization")) const prefix = "Bearer " if !strings.HasPrefix(auth, prefix) || !tokenOK(strings.TrimSpace(strings.TrimPrefix(auth, prefix)), cfg.Token) { w.Header().Set("WWW-Authenticate", "Bearer") http.Error(w, "unauthorized", http.StatusUnauthorized) return } containers, err := dockerContainers(r.Context(), cfg.DockerSock) if err != nil { http.Error(w, err.Error(), http.StatusBadGateway) return } addresses, primaryIP, primarySource := detectedAddresses(r.Context(), cfg, detector) w.Header().Set("Content-Type", "application/json") w.Header().Set("Cache-Control", "no-store") if cfg.Fingerprint != "" { w.Header().Set("X-RustProxy-Agent-Certificate-SHA256", cfg.Fingerprint) } _ = json.NewEncoder(w).Encode(inventory{ AgentVersion: version, Hostname: cfg.Hostname, PublicIP: primaryIP, PublicIPSource: primarySource, CertificateSHA256: cfg.Fingerprint, Addresses: addresses, ObservedAt: time.Now().UTC().Format(time.RFC3339), Services: servicesFrom(containers), }) }) return mux }