fix(secubox-yacy): ship public vhost + acme-challenge + admin auto-inject (closes #246) (#249)

* 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:<realm>:<password>") host-side via md5sum (matches
  what YaCy's own bin/passwd.sh produces via Digest -strfhex)
* Writes adminAccountBase64MD5=MD5:<hash> 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:<pw> 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 <gandalf@Gk2.net>
This commit is contained in:
CyberMind 2026-05-20 12:53:42 +02:00 committed by GitHub
parent 262c0c3138
commit b0b2b9293b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 145 additions and 1 deletions

View File

@ -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:<adminRealm>:<password>", 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 <devel@cybermind.fr> 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 <devel@cybermind.fr> 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

View File

@ -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

View File

@ -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:<md5sum of "admin:<realm>:<password>">
#
# 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."

View File

@ -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 "</body>" '<script src="/shared/health-banner.js"></script></body>';
# 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;
}