From b0b2b9293b2ac91fd852fb89275ee74adf4fe78a Mon Sep 17 00:00:00 2001 From: CyberMind Date: Wed, 20 May 2026 12:53:42 +0200 Subject: [PATCH] fix(secubox-yacy): ship public vhost + acme-challenge + admin auto-inject (closes #246) (#249) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(secubox-yacy): ship public vhost + acme-challenge block (closes #246) The yacy.maegia.tv public vhost was previously hand-crafted on the board and not shipped by the package. As a result: * `acme.sh --issue -d yacy.maegia.tv -w /usr/share/secubox/www` failed with HTTP 404 — the catch-all `location /` proxied the path to the YaCy LXC, which doesn't know about ACME. * Without a valid cert in /data/haproxy/certs/yacy.maegia.tv.pem, HAProxy SNI fallback served the alphabetically-first cert (`*.gk2.secubox.in.pem`), so browsers showed a domain-mismatch warning that looked like a self-signed cert. Fix: ship `nginx/yacy-vhost.conf` to /etc/nginx/sites-available/yacy.conf with the mandatory `^~ /.well-known/acme-challenge/` block placed BEFORE the catch-all proxy, so renewals win. Board-validated 2026-05-20: cert issued (LE E7, exp 2026-08-18), HAProxy reloaded, `openssl s_client -servername yacy.maegia.tv` returns subject=CN = yacy.maegia.tv. Closes #246 * feat(secubox-yacy): auto-inject admin Digest hash at install (ref #246) Earlier the operator couldn't log in to https://yacy.maegia.tv/ even though install-lxc.sh had generated a strong password in /etc/secubox/secrets/yacy-admin. The reason: that password was never written into yacy.conf — the LXC kept YaCy's installed default adminAccountBase64MD5 hash, which doesn't match the password file. install-lxc.sh now runs configure_yacy_admin() after the .deb install: * Reads /etc/secubox/secrets/yacy-admin * Pulls the actual adminRealm from yacy.conf inside the LXC (YaCy uses a long human-readable sentence, NOT the literal string "YaCy", and the realm is part of the Digest hash input) * Computes MD5("admin::") host-side via md5sum (matches what YaCy's own bin/passwd.sh produces via Digest -strfhex) * Writes adminAccountBase64MD5=MD5: to yacy.conf if it doesn't already match (idempotent) Board-validated 2026-05-20: * Reset the hash to MD5:000... before install * Ran `yacyctl install` after deploying v1.0.9 * Verified: expected hash == actual hash (MATCH) * curl --digest -u admin: https://yacy.maegia.tv/Settings_p.html → HTTP 200 Removes a UX paper-cut: post-install login works on first hit without any manual `bin/passwd.sh` dance inside the LXC. --------- Co-authored-by: CyberMind-FR --- packages/secubox-yacy/debian/changelog | 37 +++++++++++ packages/secubox-yacy/debian/rules | 3 + packages/secubox-yacy/lib/yacy/install-lxc.sh | 44 ++++++++++++- packages/secubox-yacy/nginx/yacy-vhost.conf | 62 +++++++++++++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 packages/secubox-yacy/nginx/yacy-vhost.conf diff --git a/packages/secubox-yacy/debian/changelog b/packages/secubox-yacy/debian/changelog index 58dad2bb..135d68cf 100644 --- a/packages/secubox-yacy/debian/changelog +++ b/packages/secubox-yacy/debian/changelog @@ -1,3 +1,40 @@ +secubox-yacy (1.0.9-1~bookworm1) bookworm; urgency=medium + + * install-lxc.sh: new configure_yacy_admin() step injects the generated + admin password into yacy.conf as a Digest hash so the admin login + works on first hit. Before this, the operator had a password file at + /etc/secubox/secrets/yacy-admin but the actual yacy.conf still + carried YaCy's installed default hash — login always failed until + someone manually ran `bin/passwd.sh` inside the LXC. + * Hash format: MD5 of "admin::", matching what + YaCy's own bin/passwd.sh script computes via Digest -strfhex. + * Idempotent: only patches yacy.conf when the current hash doesn't + already match the password file (re-running `yacyctl install` is + a no-op once aligned). + * Note: the realm string is pulled from the running yacy.conf at + install time — YaCy uses a long human-readable sentence as the + Digest realm, not the bare string "YaCy", and the realm IS part of + the hash input. + + -- Gerald KERMA Wed, 20 May 2026 21:30:00 +0200 + +secubox-yacy (1.0.8-1~bookworm1) bookworm; urgency=medium + + * Ship the public-facing yacy.maegia.tv vhost as + /etc/nginx/sites-available/yacy.conf (from nginx/yacy-vhost.conf). + Operator enables with `ln -s ../sites-available/yacy.conf + /etc/nginx/sites-enabled/`. Was previously hand-crafted on the board. + * The shipped vhost now carries the `^~ /.well-known/acme-challenge/` + block so `acme.sh --issue -d yacy.maegia.tv -w /usr/share/secubox/www` + succeeds — without it, every renewal proxies the path to the YaCy + LXC which 404s, and HAProxy falls back to the alphabetically-first + cert (`*.gk2.secubox.in.pem`), making browsers reject the connection. + * Board-validated 2026-05-20: cert issued (LE E7, exp 2026-08-18), + HAProxy reloaded, `openssl s_client -servername yacy.maegia.tv` + returns `subject=CN = yacy.maegia.tv`. Closes #246. + + -- Gerald KERMA Wed, 20 May 2026 18:00:00 +0200 + secubox-yacy (1.0.7-1~bookworm1) bookworm; urgency=medium * install-lxc.sh: update YACY_RELEASE_URL to v1.941 (the v1.940 tarball diff --git a/packages/secubox-yacy/debian/rules b/packages/secubox-yacy/debian/rules index b3f6224b..e8745954 100755 --- a/packages/secubox-yacy/debian/rules +++ b/packages/secubox-yacy/debian/rules @@ -13,6 +13,9 @@ override_dh_auto_install: [ -d menu.d ] && cp -r menu.d/. debian/secubox-yacy/usr/share/secubox/menu.d/ || true install -d debian/secubox-yacy/etc/nginx/secubox.d [ -f nginx/yacy.conf ] && cp nginx/yacy.conf debian/secubox-yacy/etc/nginx/secubox.d/ || true && (install -d debian/secubox-yacy/etc/nginx/secubox-routes.d && cp nginx/yacy.conf debian/secubox-yacy/etc/nginx/secubox-routes.d/) + # Public vhost yacy.maegia.tv — shipped to sites-available (operator opts in via ln -s) + install -d debian/secubox-yacy/etc/nginx/sites-available + [ -f nginx/yacy-vhost.conf ] && cp nginx/yacy-vhost.conf debian/secubox-yacy/etc/nginx/sites-available/yacy.conf || true install -d debian/secubox-yacy/etc/secubox [ -f conf/yacy.toml.example ] && cp conf/yacy.toml.example debian/secubox-yacy/etc/secubox/yacy.toml.example || true install -d debian/secubox-yacy/usr/share/secubox/lib/yacy diff --git a/packages/secubox-yacy/lib/yacy/install-lxc.sh b/packages/secubox-yacy/lib/yacy/install-lxc.sh index 45b8e92b..843c24da 100755 --- a/packages/secubox-yacy/lib/yacy/install-lxc.sh +++ b/packages/secubox-yacy/lib/yacy/install-lxc.sh @@ -207,10 +207,51 @@ generate_admin_password() { local pw; pw=$(openssl rand -hex 16) echo "$pw" > "$SECRETS_DIR/yacy-admin" chmod 600 "$SECRETS_DIR/yacy-admin" - log "Generated YaCy admin password (set via the YaCy admin UI on first login)." + log "Generated YaCy admin password (32 hex chars) at $SECRETS_DIR/yacy-admin" fi } +# Inject the generated password into YaCy's yacy.conf as a Digest hash so +# the admin login works immediately. Without this, the operator couldn't +# log in: YaCy's installed default hash doesn't match the password file. +# +# YaCy uses HTTP Digest authentication with the realm string copied from +# the `adminRealm` pref (a long human-readable sentence). The expected +# hash format is: +# adminAccountBase64MD5=MD5::"> +# +# Computed host-side (md5sum is in coreutils, no java dep needed; matches +# the output of YaCy's own bin/passwd.sh script which calls +# `net.yacy.cora.order.Digest -strfhex` internally). +configure_yacy_admin() { + local pw_file="$SECRETS_DIR/yacy-admin" + [ -f "$pw_file" ] || { log "no password file at $pw_file, skipping admin inject"; return; } + + local pw realm hash yacy_conf + pw=$(cat "$pw_file") + yacy_conf="/opt/yacy/DATA/SETTINGS/yacy.conf" + + # Pull the realm string from the running YaCy config inside the LXC. + realm=$(lxc-attach -n "$LXC_NAME" -P "$LXC_PATH" -- sh -c "grep '^adminRealm=' $yacy_conf | cut -d= -f2-" 2>/dev/null || true) + [ -z "$realm" ] && realm="YaCy" + + hash=$(printf 'admin:%s:%s' "$realm" "$pw" | md5sum | cut -d' ' -f1) + [ -z "$hash" ] && { log "WARN: md5sum produced empty hash, skipping admin inject"; return; } + + # Skip if the right hash is already set (idempotent). + if lxc-attach -n "$LXC_NAME" -P "$LXC_PATH" -- grep -q "^adminAccountBase64MD5=MD5:${hash}$" "$yacy_conf" 2>/dev/null; then + log "YaCy admin hash already matches generated password — skipping" + return + fi + + log "Injecting YaCy admin Digest hash into yacy.conf ..." + lxc-attach -n "$LXC_NAME" -P "$LXC_PATH" -- systemctl stop yacy 2>/dev/null || true + # YaCy rewrites the conf when running; pause it before editing. + lxc-attach -n "$LXC_NAME" -P "$LXC_PATH" -- sed -i \ + "s|^adminAccountBase64MD5=.*|adminAccountBase64MD5=MD5:${hash}|" "$yacy_conf" + lxc-attach -n "$LXC_NAME" -P "$LXC_PATH" -- systemctl start yacy 2>/dev/null || true +} + mark_provisioned() { install -d -m 0755 -o secubox -g secubox "$STATE_DIR" date -Iseconds > "$SENTINEL" @@ -228,6 +269,7 @@ main() { ensure_resolv install_yacy_in_lxc generate_admin_password + configure_yacy_admin provision_yacy mark_provisioned log "OK — LXC '$LXC_NAME' at $LXC_IP, yacy provisioned + running on port 8090." diff --git a/packages/secubox-yacy/nginx/yacy-vhost.conf b/packages/secubox-yacy/nginx/yacy-vhost.conf new file mode 100644 index 00000000..9b2cb6c9 --- /dev/null +++ b/packages/secubox-yacy/nginx/yacy-vhost.conf @@ -0,0 +1,62 @@ +# /etc/nginx/sites-available/yacy.conf — public vhost yacy.maegia.tv +# Installed by secubox-yacy (#232, #246). +# +# Routing flow (validated 2026-05-20): +# Browser → HAProxy:443 (TLS, cert /data/haproxy/certs/yacy.maegia.tv.pem) +# → mitmproxy WAF (LXC) — entry registered in +# /srv/mitmproxy/haproxy-routes.json INSIDE the mitmproxy LXC +# → nginx:9080 (this vhost) +# → YaCy LXC at 10.100.0.80:8090 +# +# Operator-side prerequisites (NOT done by this snippet): +# 1. DNS A record yacy.maegia.tv → public IP +# 2. TLS cert in /data/haproxy/certs/yacy.maegia.tv.pem +# acme.sh --issue -d yacy.maegia.tv -w /usr/share/secubox/www --keylength ec-256 +# ↑ relies on the ^~ /.well-known/acme-challenge/ block below +# 3. haproxyctl vhost add yacy.maegia.tv nginx_vhosts ssl +# ↑ CAUTION: known to drop backend defs (see #238 / v2.11.1). +# Prefer manual ACL injection or restore haproxy.cfg from backup. +# 4. mitmproxy route INSIDE the LXC: +# lxc-attach -n mitmproxy -- python3 -c \ +# "import json; p='/srv/mitmproxy/haproxy-routes.json'; d=json.load(open(p)); \ +# d['yacy.maegia.tv']=['192.168.1.200',9080]; json.dump(d, open(p,'w'), indent=2, sort_keys=True)" +# lxc-attach -n mitmproxy -- systemctl restart mitmproxy + +server { + listen 0.0.0.0:9080; + server_name yacy.maegia.tv; + + # ACME HTTP-01 challenge — MUST come before any other location so + # acme.sh renewals win against the catch-all proxy below. Without this + # block, every renewal returns 404 from the YaCy LXC. + location ^~ /.well-known/acme-challenge/ { + root /usr/share/secubox/www; + try_files $uri =404; + } + + # Health Banner injection (same pattern as the canonical hub vhost) + sub_filter_types text/html; + sub_filter_once on; + sub_filter "" ''; + + # Shared assets — banner JS, error pages + location /shared/ { + add_header Access-Control-Allow-Origin "*" always; + alias /usr/share/secubox/www/shared/; + } + + # YaCy admin UI proxied to the LXC + location / { + proxy_pass http://10.100.0.80:8090/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_read_timeout 300s; + } + + access_log /var/log/nginx/yacy_access.log; + error_log /var/log/nginx/yacy_error.log; +}