diff --git a/tools/register-certs/main.go b/tools/register-certs/main.go index 00f9ff8..766e142 100644 --- a/tools/register-certs/main.go +++ b/tools/register-certs/main.go @@ -1,12 +1,15 @@ package main import ( + "bytes" "fmt" "io" + "mime/multipart" "net/http" "net/http/cookiejar" "net/url" "os" + "path/filepath" "regexp" "strings" ) @@ -33,14 +36,7 @@ func main() { resp.Body.Close() re := regexp.MustCompile(`content="([^"]+)"`) - match := re.FindStringSubmatch(string(body)) - var csrf string - for _, m := range match { - if len(m) > 20 { - csrf = m - break - } - } + csrf := extractCSRF(string(body), re) if csrf == "" { fmt.Fprintf(os.Stderr, "FAIL: could not extract CSRF token\n") os.Exit(1) @@ -60,66 +56,79 @@ func main() { body, _ = io.ReadAll(resp.Body) resp.Body.Close() - if resp.StatusCode != 200 || !strings.Contains(string(body), `"ok"`) { + if resp.StatusCode != 200 || !strings.Contains(strings.ToLower(string(body)), `"ok"`) { fmt.Fprintf(os.Stderr, "FAIL: login failed (status=%d): %s\n", resp.StatusCode, strings.TrimSpace(string(body))) os.Exit(1) } fmt.Printf("OK: Logged in as %s\n", username) - // Step 3: Upload cert for each domain + // Step 3: Upload cert for each domain using multipart/form-data certsDir := "/opt/nextworkspace/config/zoraxy/conf/certs" success := true for _, domain := range domains { - // Try .pem first, then .crt for backward compatibility - certFile := certsDir + "/" + domain + ".pem" - if _, err := os.Stat(certFile); os.IsNotExist(err) { - certFile = certsDir + "/" + domain + ".crt" - } - certData, err := os.ReadFile(certFile) + // Try .pem first, then .crt + pemPath := filepath.Join(certsDir, domain+".pem") + crtPath := filepath.Join(certsDir, domain+".crt") + keyPath := filepath.Join(certsDir, domain+".key") + + certData, err := os.ReadFile(pemPath) if err != nil { - fmt.Printf("SKIP: %s (no cert file)\n", domain) - continue + certData, err = os.ReadFile(crtPath) + if err != nil { + fmt.Printf("SKIP: %s (no cert file)\n", domain) + continue + } + } + keyData, err := os.ReadFile(keyPath) + if err != nil { + fmt.Printf("WARN: %s (no key file), uploading cert only\n", domain) } - // Get fresh CSRF for each upload + // Build multipart form + var buf bytes.Buffer + w := multipart.NewWriter(&buf) + w.WriteField("domain", domain) + + certWriter, _ := w.CreateFormFile("cert", domain+".pem") + certWriter.Write(certData) + + if keyData != nil { + keyWriter, _ := w.CreateFormFile("key", domain+".key") + keyWriter.Write(keyData) + } + w.Close() + + // Get fresh CSRF resp, err := client.Get("http://127.0.0.1:8000/login.html") if err != nil { fmt.Printf("WARN: %s csrf fetch failed: %v\n", domain, err) continue } - body, _ := io.ReadAll(resp.Body) + b, _ := io.ReadAll(resp.Body) resp.Body.Close() - match = re.FindStringSubmatch(string(body)) - csrf = "" - for _, m := range match { - if len(m) > 20 { - csrf = m - break - } - } + csrf = extractCSRF(string(b), re) if csrf == "" { fmt.Printf("WARN: %s no CSRF token\n", domain) continue } - uploadURL := fmt.Sprintf("http://127.0.0.1:8000/api/cert/upload?ktype=pub&domain=%s", domain) - req, _ := http.NewRequest("POST", uploadURL, strings.NewReader(string(certData))) + req, _ := http.NewRequest("POST", "http://127.0.0.1:8000/api/cert/upload", &buf) + req.Header.Set("Content-Type", w.FormDataContentType()) req.Header.Set("X-CSRF-Token", csrf) - req.Header.Set("Content-Type", "application/x-pem-file") resp, err = client.Do(req) if err != nil { - fmt.Printf("WARN: %s upload failed: %v\n", domain, err) + fmt.Printf("FAIL: %s request failed: %v\n", domain, err) success = false continue } - body, _ = io.ReadAll(resp.Body) + b, _ = io.ReadAll(resp.Body) resp.Body.Close() if resp.StatusCode == 200 { fmt.Printf("OK: %s cert uploaded\n", domain) } else { - fmt.Printf("FAIL: %s (status=%d): %s\n", domain, resp.StatusCode, strings.TrimSpace(string(body))) + fmt.Printf("FAIL: %s (status=%d): %s\n", domain, resp.StatusCode, strings.TrimSpace(string(b))) success = false } } @@ -128,3 +137,22 @@ func main() { os.Exit(1) } } + +func extractCSRF(html string, re *regexp.Regexp) string { + match := re.FindAllStringSubmatch(html, -1) + for _, m := range match { + if len(m) > 1 && len(m[1]) > 20 { + return m[1] + } + } + // Try looking for the specific zoraxy.csrf.Token pattern + idx := strings.Index(html, "zoraxy.csrf.Token") + if idx >= 0 { + sub := html[idx:] + m := re.FindStringSubmatch(sub) + if len(m) > 1 { + return m[1] + } + } + return "" +}