package main import ( "bytes" "fmt" "io" "mime/multipart" "net/http" "net/http/cookiejar" "net/url" "os" "path/filepath" "regexp" "strings" ) func main() { if len(os.Args) < 4 { fmt.Fprintf(os.Stderr, "Usage: %s [domain...]\n", os.Args[0]) os.Exit(1) } username := os.Args[1] password := os.Args[2] domains := os.Args[3:] jar, _ := cookiejar.New(nil) client := &http.Client{Jar: jar} // Step 1: Fetch login page to get CSRF token resp, err := client.Get("http://127.0.0.1:8000/login.html") if err != nil { fmt.Fprintf(os.Stderr, "FAIL: fetching login page: %v\n", err) os.Exit(1) } body, _ := io.ReadAll(resp.Body) resp.Body.Close() csrf := extractCSRF(string(body)) if csrf == "" { fmt.Fprintf(os.Stderr, "FAIL: could not extract CSRF token\n") os.Exit(1) } // Step 2: Login form := url.Values{"username": {username}, "password": {password}} req, _ := http.NewRequest("POST", "http://127.0.0.1:8000/api/auth/login", strings.NewReader(form.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("X-CSRF-Token", csrf) resp, err = client.Do(req) if err != nil { fmt.Fprintf(os.Stderr, "FAIL: login request: %v\n", err) os.Exit(1) } body, _ = io.ReadAll(resp.Body) resp.Body.Close() 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 using multipart/form-data certsDir := "/opt/nextworkspace/config/zoraxy/conf/certs" success := true for _, domain := range domains { // 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 { 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) } // 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 } b, _ := io.ReadAll(resp.Body) resp.Body.Close() csrf = extractCSRF(string(b)) if csrf == "" { fmt.Printf("WARN: %s no CSRF token\n", domain) continue } 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) resp, err = client.Do(req) if err != nil { fmt.Printf("FAIL: %s request failed: %v\n", domain, err) success = false continue } 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(b))) success = false } } if !success { os.Exit(1) } } func extractCSRF(html string) string { idx := strings.Index(html, "zoraxy.csrf.Token") if idx < 0 { return "" } sub := html[idx:] re := regexp.MustCompile(`content="([^"]+)"`) m := re.FindStringSubmatch(sub) if len(m) > 1 { return m[1] } return "" }