fix: use Go tool for cert upload, fix apps.yaml YAML format

This commit is contained in:
Claus Lohmar 2026-07-08 08:51:23 +01:00
parent 6ea3164d72
commit aa29d363ce
3 changed files with 185 additions and 33 deletions

View file

@ -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" <<EOF
@ -165,17 +161,15 @@ if [ "$GREENFIELD" = true ]; then
}
EOF
# Seed API token and admin into BoltDB
# Seed admin into BoltDB
if [ -f "$TOOL_BIN" ]; then
mkdir -p "$TARGET_DIR/data/zoraxy"
touch "$TARGET_DIR/data/zoraxy/sys.db"
"$TOOL_BIN" db --db "$TARGET_DIR/data/zoraxy/sys.db" \
--set "auth:api_key:${DEPLOY_TOKEN}:{\"username\":\"${ADMIN_USERNAME:-master}\",\"permission\":\"admin\"}" 2>&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"
# 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] $FQDN: $(echo $R | head -c 80)"
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
done
# Stop Zoraxy to write proxy configs
@ -255,14 +245,46 @@ ZORAXY_AUTH
echo "[*] Generating apps.yaml..."
cat > "$TARGET_DIR/config/nextworkspace/apps.yaml" <<EOF
apps:
- name: "OpenCloud" subtitle: "File Storage" path: "/cloud" upstream: "http://127.0.0.1:9100" icon: "cloud"
- name: "Euro Office" subtitle: "Collaborative Suite" path: "/office" upstream: "http://127.0.0.1:9200" icon: "office"
- name: "ERPNext" subtitle: "Enterprise ERP" path: "/erp" upstream: "http://127.0.0.1:9300" icon: "erp"
- name: "Matrix Chat" subtitle: "Team Communication" path: "/chat" upstream: "http://127.0.0.1:9400" icon: "chat"
- name: "Jitsi" subtitle: "Video Conferencing" path: "/meet" upstream: "http://127.0.0.1:9500" icon: "video"
- name: "Webmail" subtitle: "Email Client" path: "/mail" upstream: "http://127.0.0.1:9600" icon: "mail"
- name: "AI Chat" subtitle: "Open WebUI" path: "/ai" upstream: "http://127.0.0.1:9700" icon: "bot"
- name: "Portainer" subtitle: "Container Management" path: "/admin" upstream: "http://127.0.0.1:9800" icon: "admin"
- name: "OpenCloud"
subtitle: "File Storage"
path: "/cloud"
upstream: "http://127.0.0.1:9100"
icon: "cloud"
- name: "Euro Office"
subtitle: "Collaborative Suite"
path: "/office"
upstream: "http://127.0.0.1:9200"
icon: "office"
- name: "ERPNext"
subtitle: "Enterprise ERP"
path: "/erp"
upstream: "http://127.0.0.1:9300"
icon: "erp"
- name: "Matrix Chat"
subtitle: "Team Communication"
path: "/chat"
upstream: "http://127.0.0.1:9400"
icon: "chat"
- name: "Jitsi"
subtitle: "Video Conferencing"
path: "/meet"
upstream: "http://127.0.0.1:9500"
icon: "video"
- name: "Webmail"
subtitle: "Email Client"
path: "/mail"
upstream: "http://127.0.0.1:9600"
icon: "mail"
- name: "AI Chat"
subtitle: "Open WebUI"
path: "/ai"
upstream: "http://127.0.0.1:9700"
icon: "bot"
- name: "Portainer"
subtitle: "Container Management"
path: "/admin"
upstream: "http://127.0.0.1:9800"
icon: "admin"
EOF
# Remove stale proxy.old dirs created by Zoraxy

View file

@ -0,0 +1,130 @@
package main
import (
"fmt"
"io"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"regexp"
"strings"
)
func main() {
if len(os.Args) < 4 {
fmt.Fprintf(os.Stderr, "Usage: %s <username> <password> <domain> [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)
}
}

Binary file not shown.