diff --git a/deploy.sh b/deploy.sh index 58256ea..b6bda8c 100755 --- a/deploy.sh +++ b/deploy.sh @@ -149,10 +149,6 @@ if [ "$GREENFIELD" = true ]; then echo "[INFO] Version: $(cat $TARGET_DIR/VERSION)" fi - # Generate stateless API token for Zoraxy - DEPLOY_TOKEN=$(openssl rand -hex 32 2>/dev/null || date +%s | sha256sum | head -c 64) - echo "[*] Stateless API token generated" - # Write ACME config + BoltDB (before Zoraxy starts, db not locked) mkdir -p "$TARGET_DIR/config/zoraxy/conf" cat > "$TARGET_DIR/config/zoraxy/conf/acme_conf.json" <&1 || true "$TOOL_BIN" db --db "$TARGET_DIR/data/zoraxy/sys.db" \ --set "system:admin:{\"username\":\"${ADMIN_USERNAME:-master}\"}" 2>&1 || true "$TOOL_BIN" db --db "$TARGET_DIR/data/zoraxy/sys.db" \ --set "zorxauth:options:{\"enable_auth_gateway\":true,\"sso_redirect_url\":\"https://${APP_SUB}.${DOMAIN}/\"}" 2>&1 || true - echo " [OK] API token + admin seeded into BoltDB" + echo " [OK] Admin seeded into BoltDB" fi # Start Zoraxy @@ -189,23 +183,19 @@ EOF sleep 2 done - # Register certificates using stateless API token - echo "[*] Registering certificates via API token..." - CERTS_DIR="$TARGET_DIR/config/zoraxy/conf/certs" - AUTH_HEADER="Authorization: Bearer ${DEPLOY_TOKEN}" - for SUB in $APP_SUB $DNS_SUB $WWW_SUB; do - FQDN="${SUB}.${DOMAIN}" - CRT_FILE="$CERTS_DIR/${FQDN}.pem" - if [ ! -f "$CRT_FILE" ]; then echo " SKIP $FQDN (no .pem file)"; continue; fi - R=$(curl -s --max-time 10 -X POST "http://127.0.0.1:8000/api/cert/upload?ktype=pub&domain=${FQDN}" \ - -H "${AUTH_HEADER}" -H "Content-Type: application/json" \ - --data-binary "@${CRT_FILE}" 2>&1) || true - if echo "$R" | grep -qi '"success"\|"ok"\|^{}$'; then - echo " [OK] $FQDN registered" - else - echo " [WARN] $FQDN: $(echo $R | head -c 80)" - fi - done + # Register certificates using Go tool (handles CSRF/session properly) + echo "[*] Registering certificates via Go tool..." + REG_TOOL="$REPO_DIR/tools/register-certs/register-certs" + if [ -f "$REG_TOOL" ]; then + cd "$REPO_DIR/tools/register-certs" && go build -o register-certs . 2>/dev/null && cd "$REPO_DIR" + "$REG_TOOL" "${ADMIN_USERNAME:-master}" "${ADMIN_PASSWORD:-9Aku7MfklZU9ldnZ}" \ + ${APP_SUB}.${DOMAIN} ${DNS_SUB}.${DOMAIN} ${WWW_SUB}.${DOMAIN} 2>&1 || true + else + echo " [WARN] Register tool not found, building..." + cd "$REPO_DIR/tools/register-certs" + go build -o register-certs . 2>&1 || true + cd "$REPO_DIR" + fi # Stop Zoraxy to write proxy configs @@ -255,14 +245,46 @@ ZORAXY_AUTH echo "[*] Generating apps.yaml..." cat > "$TARGET_DIR/config/nextworkspace/apps.yaml" < [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() + + re := regexp.MustCompile(`content="([^"]+)"`) + match := re.FindStringSubmatch(string(body)) + var csrf string + for _, m := range match { + if len(m) > 20 { + csrf = m + break + } + } + 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(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 + 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) + if err != nil { + fmt.Printf("SKIP: %s (no cert file)\n", domain) + continue + } + + // Get fresh CSRF for each upload + 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) + resp.Body.Close() + match = re.FindStringSubmatch(string(body)) + csrf = "" + for _, m := range match { + if len(m) > 20 { + csrf = m + break + } + } + 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.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) + success = false + continue + } + body, _ = 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))) + success = false + } + } + + if !success { + os.Exit(1) + } +} diff --git a/tools/register-certs/register-certs b/tools/register-certs/register-certs new file mode 100755 index 0000000..2193277 Binary files /dev/null and b/tools/register-certs/register-certs differ