Compare commits

...

2 Commits

Author SHA1 Message Date
8905228cbd fix(round): restore ICON_PATHS /var/www/common + defense-in-depth icon copy
Some checks failed
License Headers / check (push) Failing after 6s
These two edits were on fix/round-real-root-icons (commit a5b312f7) but
got wiped by my earlier force-push to fix/round-armv6-boot-services.
Re-applying identically so v4 picks them up.

fallback_manager.py: add /var/www/common/assets/icons as the third
lookup path so the 6 brand icons (auth/wall/boot/mind/root/mesh) ship
via common/ are found. Without this, fallback_manager only sees the
round/-side placeholder set and can't render the canonical module
icons on the fallback radar.

build-eye-remote-image.sh: also copy common/assets/icons/*.png into
/usr/lib/secubox-eye/assets/icons/ (cp -n so placeholders aren't
overwritten when names collide — none do). Pure defense-in-depth.
2026-05-18 06:25:41 +02:00
CyberMind
9a0a9873a7
fix(secubox-metrics): VisitorOrigin entries=[] forever — CAP_NET_ADMIN + DB-IP fallback (closes #194) (#195)
Two latent bugs since #92 that together masked VisitorOrigin:

1. Missing CAP_NET_ADMIN
   The aggregator runs as User=secubox and calls
     nft -j list set inet secubox_metrics seen_src
   which requires CAP_NET_ADMIN. EPERM, subprocess silently returns
   no output, _read_nft_set returns [], entries stays []. Added:
     AmbientCapabilities=CAP_NET_ADMIN
     CapabilityBoundingSet=CAP_NET_ADMIN
   Coexists with NoNewPrivileges=true (systemd sets the ambient set
   before the User= drop).

2. MaxMind license barrier
   secubox-geoipupdate.service had:
     ConditionPathExists=/etc/secubox/secrets/maxmind.conf
   So operators without a MaxMind account couldn't activate VisitorOrigin
   at all. Replaced with a new fetcher /usr/bin/secubox-geoipupdate-fetch
   that:
     - Uses MaxMind geoipupdate if /etc/secubox/secrets/maxmind.conf exists
       AND geoipupdate is installed (Recommends).
     - Falls back to DB-IP free ASN lite (no signup needed). The file is
       a MaxMind-compatible mmdb so maxminddb.open_database reads it
       transparently. Sanity-checked on prod board:
         metadata: DBIP-ASN-Lite (compat=GeoLite2-ASN)
         lookup 1.1.1.1 -> AS13335 Cloudflare, Inc.

Live verification on gk2 board after applying both fixes:
  top visitor ASNs (last 60min):
    AS32934  Facebook              59 hits
    AS8075   Microsoft             34 hits
    AS396982 Google                13 hits
    AS16509  Amazon                 6 hits

Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 06:16:43 +02:00
7 changed files with 106 additions and 8 deletions

View File

@ -0,0 +1,49 @@
#!/bin/bash
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
#
# secubox-geoipupdate-fetch — refresh /var/lib/GeoIP/GeoLite2-ASN.mmdb
#
# Two paths (#194):
# 1. If /etc/secubox/secrets/maxmind.conf exists AND `geoipupdate` is
# installed, use the MaxMind GeoLite2 download (operator has a key).
# 2. Otherwise fall back to DB-IP free ASN lite, no signup required.
# Format is MaxMind-compatible — the visitor_origin aggregator reads
# it with `maxminddb.open_database` transparently.
#
# Invoked by secubox-geoipupdate.service (weekly timer).
set -euo pipefail
MMDB_DEST="/var/lib/GeoIP/GeoLite2-ASN.mmdb"
MAXMIND_CONF="/etc/secubox/secrets/maxmind.conf"
DBIP_URL_BASE="https://download.db-ip.com/free"
log() { echo "[geoipupdate-fetch] $*"; }
err() { echo "[geoipupdate-fetch] ERROR: $*" >&2; }
install -d -m 755 /var/lib/GeoIP
if [ -f "$MAXMIND_CONF" ] && command -v geoipupdate >/dev/null 2>&1; then
log "MaxMind license + geoipupdate present — using MaxMind path"
exec geoipupdate -f "$MAXMIND_CONF" -d /var/lib/GeoIP
fi
log "no MaxMind license (or geoipupdate missing) — falling back to DB-IP free"
MONTH=$(date +%Y-%m)
URL="${DBIP_URL_BASE}/dbip-asn-lite-${MONTH}.mmdb.gz"
TMP=$(mktemp)
TMP_GZ="${TMP}.gz"
trap 'rm -f "$TMP" "$TMP_GZ"' EXIT
log "GET $URL"
if ! curl -fsSL -o "$TMP_GZ" "$URL"; then
err "DB-IP download failed; trying previous month as fallback"
LAST_MONTH=$(date -d "${MONTH}-01 -1 day" +%Y-%m 2>/dev/null || date -v-1m +%Y-%m)
URL="${DBIP_URL_BASE}/dbip-asn-lite-${LAST_MONTH}.mmdb.gz"
log "GET $URL"
curl -fsSL -o "$TMP_GZ" "$URL"
fi
gunzip -f "$TMP_GZ" # writes $TMP
install -m 644 -o secubox -g secubox "$TMP" "$MMDB_DEST"
log "installed $MMDB_DEST ($(stat -c %s "$MMDB_DEST") bytes)"

View File

@ -1,3 +1,24 @@
secubox-metrics (1.0.3-1~bookworm1) bookworm; urgency=medium
* VisitorOrigin: add AmbientCapabilities=CAP_NET_ADMIN to the systemd
unit (#194, latent since #92). Without this the aggregator runs as
secubox user, `nft -j list set inet secubox_metrics seen_src` returns
EPERM, _read_nft_set silently returns [] and entries stays empty
forever. Caps coexist with NoNewPrivileges=true.
* secubox-geoipupdate: drop the ConditionPathExists=/etc/secubox/
secrets/maxmind.conf gate. New helper `secubox-geoipupdate-fetch`
tries MaxMind first if a license is present, else falls back to
DB-IP free ASN lite (https://download.db-ip.com/free/dbip-asn-lite-
YYYY-MM.mmdb.gz). DB-IP releases mmdb in MaxMind-compatible format
so the visitor_origin aggregator reads it transparently.
* Operators get VisitorOrigin out of the box now (no MaxMind account
required); those with a license keep using MaxMind via the same
helper.
-- Gerald KERMA <devel@cybermind.fr> Mon, 18 May 2026 06:16:28 +0200
secubox-metrics (1.0.2-1~bookworm1) bookworm; urgency=medium
* Add Cookie Audit (RGPD / ePrivacy) endpoints: POST /api/v1/cookie-audit/ingest,

View File

@ -39,3 +39,7 @@ override_dh_install:
debian/secubox-metrics/lib/systemd/system/secubox-geoipupdate.service
install -D -m 0644 systemd/secubox-geoipupdate.timer \
debian/secubox-metrics/lib/systemd/system/secubox-geoipupdate.timer
# Helper fetcher (#194): tries MaxMind first if license present,
# else falls back to DB-IP free ASN lite (no signup required).
install -D -m 0755 bin/secubox-geoipupdate-fetch \
debian/secubox-metrics/usr/bin/secubox-geoipupdate-fetch

View File

@ -27,6 +27,13 @@ RestartSec=5
# ProtectSystem=full
NoNewPrivileges=true
ReadWritePaths=/run/secubox /var/cache/secubox
# VisitorOrigin reads the nft `seen_src` set via `nft -j list set ...` which
# requires CAP_NET_ADMIN. Without it, _read_nft_set silently returns [] and
# the aggregator emits empty entries forever (#194 fix, latent since #92).
# Coexists with NoNewPrivileges=true because systemd sets the ambient set
# before the User= drop.
AmbientCapabilities=CAP_NET_ADMIN
CapabilityBoundingSet=CAP_NET_ADMIN
[Install]
WantedBy=multi-user.target

View File

@ -1,14 +1,15 @@
[Unit]
Description=SecuBox — refresh GeoLite2 ASN database
ConditionPathExists=/etc/secubox/secrets/maxmind.conf
Description=SecuBox — refresh GeoLite2-ASN database (MaxMind or DB-IP fallback)
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=secubox
Group=secubox
ExecStart=/usr/bin/geoipupdate -f /etc/secubox/secrets/maxmind.conf -d /var/lib/GeoIP
NoNewPrivileges=true
# Fetcher must run as root because /var/lib/GeoIP is owned by root and the
# DB-IP fallback path uses `install -o secubox` to land the mmdb under the
# correct ownership. The helper does not exec anything as root that takes
# untrusted input.
User=root
ExecStart=/usr/bin/secubox-geoipupdate-fetch
ProtectSystem=full
ReadWritePaths=/var/lib/GeoIP

View File

@ -93,10 +93,15 @@ LOGO_PATHS = [
Path("/etc/secubox/eye-remote/assets/phoenix_logo.png"),
]
# Icon paths - module icons
# Icon paths - module icons.
# Order matters: first existing path wins per icon name. /var/www/common/
# holds the real brand icons (auth, wall, boot, mind, root, mesh) installed
# by build-eye-remote-image.sh; the local /usr/lib/secubox-eye/assets/icons/
# fallback path holds the round-specific placeholder set.
ICON_PATHS = [
Path("/tmp/assets/icons"),
Path("/etc/secubox/eye-remote/assets/icons"),
Path("/var/www/common/assets/icons"),
Path(__file__).parent.parent.parent.parent / "assets" / "icons",
]

View File

@ -704,8 +704,19 @@ if [[ -f "$SCRIPT_DIR/secubox-eye-agent.service" && -f "$SCRIPT_DIR/config.toml.
log "Installing menu system icons..."
mkdir -p "$ROOT_MNT/usr/lib/secubox-eye/assets/icons"
cp "$SCRIPT_DIR/assets/icons"/*.png "$ROOT_MNT/usr/lib/secubox-eye/assets/icons/" 2>/dev/null || true
# Defense-in-depth: also drop the brand-icon PNGs (auth/wall/boot/
# mind/root/mesh) from remote-ui/common/assets/icons/ so any consumer
# that resolves icons via /usr/lib/secubox-eye/ finds them. The
# fallback_manager also searches /var/www/common/assets/icons/
# directly (where build copies common/ in another step) — this is
# redundant shipping for resilience.
_COMMON_ICONS="$(dirname "$SCRIPT_DIR")/common/assets/icons"
if [[ -d "$_COMMON_ICONS" ]]; then
cp -n "$_COMMON_ICONS"/*.png \
"$ROOT_MNT/usr/lib/secubox-eye/assets/icons/" 2>/dev/null || true
fi
ICON_COUNT=$(ls "$ROOT_MNT/usr/lib/secubox-eye/assets/icons"/*.png 2>/dev/null | wc -l)
log "Installed $ICON_COUNT menu icons"
log "Installed $ICON_COUNT menu icons (round/ + common/ brand)"
fi
else
warn "Eye Agent files not found, skipping installation"