Compare commits

..

No commits in common. "1d7ca0cd2270bf425ee60b4d4a837bd309b20a9c" and "4fd0d864eea4b65c9c92e05886357b298108739e" have entirely different histories.

5 changed files with 8 additions and 62 deletions

View File

@ -41,17 +41,6 @@ def run_cmd(cmd: list, timeout: int = 60) -> tuple:
return "", str(e), 1
def run_priv(cmd: list, timeout: int = 60) -> tuple:
"""Run a host privileged command via the read/lifecycle sudo grant.
The aggregator mounts this module in-process as the unprivileged `secubox`
user, which cannot see root's /var/lib/lxc containers — bare `lxc-ls`
returns nothing, so the dashboard reported 0 containers (#601). The grant
in /etc/sudoers.d/secubox-vm covers read + lifecycle only.
"""
return run_cmd(["sudo", "-n"] + cmd, timeout=timeout)
def is_libvirt_running() -> bool:
"""Check if libvirtd is running."""
_, _, code = run_cmd(["systemctl", "is-active", "--quiet", "libvirtd"])
@ -92,9 +81,7 @@ def get_virsh_vms() -> list:
def get_lxc_containers() -> list:
"""List all LXC containers."""
containers = []
# NB: the memory column key is `RAM` — `MEMORY` is rejected by lxc-ls
# ("Invalid key") and yields zero output (#601).
stdout, _, code = run_priv(["lxc-ls", "-f", "-F", "NAME,STATE,IPV4,RAM"])
stdout, _, code = run_cmd(["lxc-ls", "-f", "-F", "NAME,STATE,IPV4,MEMORY"])
if code != 0:
return containers
@ -135,7 +122,7 @@ def get_lxc_info(name: str) -> dict:
"""Get detailed LXC container info."""
info = {"name": name, "type": "lxc"}
stdout, _, code = run_priv(["lxc-info", "-n", name])
stdout, _, code = run_cmd(["lxc-info", "-n", name])
if code == 0:
for line in stdout.strip().split('\n'):
if ':' in line:
@ -332,7 +319,7 @@ def start_vm(name: str):
if is_lxc_available():
for c in get_lxc_containers():
if c["name"] == name:
stdout, stderr, code = run_priv(["lxc-start", "-n", name])
stdout, stderr, code = run_cmd(["lxc-start", "-n", name])
if code != 0:
raise HTTPException(status_code=500, detail=f"Failed to start: {stderr}")
return {"status": "started", "name": name}
@ -360,7 +347,7 @@ def stop_vm(name: str, force: bool = False):
cmd = ["lxc-stop", "-n", name]
if force:
cmd.append("-k")
stdout, stderr, code = run_priv(cmd)
stdout, stderr, code = run_cmd(cmd)
if code != 0:
raise HTTPException(status_code=500, detail=f"Failed to stop: {stderr}")
return {"status": "stopped", "name": name}
@ -384,8 +371,8 @@ def restart_vm(name: str):
if is_lxc_available():
for c in get_lxc_containers():
if c["name"] == name:
run_priv(["lxc-stop", "-n", name])
stdout, stderr, code = run_priv(["lxc-start", "-n", name])
run_cmd(["lxc-stop", "-n", name])
stdout, stderr, code = run_cmd(["lxc-start", "-n", name])
if code != 0:
raise HTTPException(status_code=500, detail=f"Failed to restart: {stderr}")
return {"status": "restarted", "name": name}
@ -417,11 +404,9 @@ def delete_vm(name: str):
for c in get_lxc_containers():
if c["name"] == name:
if c.get('state') == 'running':
run_priv(["lxc-stop", "-n", name, "-k"])
run_cmd(["lxc-stop", "-n", name, "-k"])
# lxc-destroy is intentionally NOT in the sudo grant — deleting
# containers from an unauthenticated endpoint stays root-only.
stdout, stderr, code = run_priv(["lxc-destroy", "-n", name])
stdout, stderr, code = run_cmd(["lxc-destroy", "-n", name])
if code != 0:
raise HTTPException(status_code=500, detail=f"Failed to delete: {stderr}")

View File

@ -1,16 +1,3 @@
secubox-vm (1.0.1-1) stable; urgency=medium
* fix(lxc): /vm/ reported 0 containers though the host runs 20 LXC
containers (#601). The aggregator mounts this module in-process as the
unprivileged `secubox` user, so bare `lxc-ls` could not see root's
/var/lib/lxc → empty list. LXC read + lifecycle now go through `sudo -n`
via a new read-only-ish grant (/etc/sudoers.d/secubox-vm: lxc-ls/info/
start/stop, visudo-validated). lxc-create/lxc-destroy stay root-only.
postinst reloads secubox-aggregator so the in-process module refreshes.
(KVM/libvirt readings were already correct: no /dev/kvm, libvirtd off.)
-- Gerald KERMA <devel@cybermind.fr> Mon, 15 Jun 2026 13:00:00 +0200
secubox-vm (1.0.0-1) stable; urgency=low
* Initial release

View File

@ -5,19 +5,6 @@ case "$1" in
install -d -o root -g root -m 755 /var/lib/secubox/vm
install -d -o root -g root -m 755 /var/lib/secubox/vm/iso
install -d -o root -g root -m 755 /var/lib/secubox/vm/disks
# #601 — validate the read+lifecycle cscli/lxc sudo grant; drop it if
# malformed so a bad drop-in can never break sudo for the whole system.
SUDOERS=/etc/sudoers.d/secubox-vm
if [ -f "$SUDOERS" ]; then
chmod 0440 "$SUDOERS" || true
if command -v visudo >/dev/null 2>&1 && ! visudo -cf "$SUDOERS" >/dev/null 2>&1; then
echo "secubox-vm: invalid sudoers drop-in, removing" >&2
rm -f "$SUDOERS"
fi
fi
# The aggregator mounts this module in-process; reload it so the new code
# + LXC listing take effect (the per-module service is not the live path).
systemctl try-reload-or-restart secubox-aggregator.service 2>/dev/null || true
systemctl daemon-reload
systemctl enable secubox-vm.service
systemctl start secubox-vm.service || true

View File

@ -14,7 +14,3 @@ override_dh_auto_install:
install -m 644 menu.d/903-vm.json $(CURDIR)/debian/secubox-vm/usr/share/secubox/menu.d/
install -d $(CURDIR)/debian/secubox-vm/usr/lib/systemd/system
install -m 644 debian/secubox-vm.service $(CURDIR)/debian/secubox-vm/usr/lib/systemd/system/
# #601 — read+lifecycle sudo grant so LXC enumeration works under the
# unprivileged `secubox` user the aggregator mounts this module as.
install -d $(CURDIR)/debian/secubox-vm/etc/sudoers.d
install -m 0440 debian/sudoers.d/secubox-vm $(CURDIR)/debian/secubox-vm/etc/sudoers.d/secubox-vm

View File

@ -1,9 +0,0 @@
# SecuBox VM module — host virtualization management as the `secubox` user.
# The aggregator mounts this module in-process as `secubox`, which cannot see
# root's /var/lib/lxc containers without sudo. READ + container LIFECYCLE only.
# lxc-create / lxc-destroy are deliberately NOT granted — creating/deleting
# containers stays root-only (these endpoints carry no JWT). CSPN least-priv.
secubox ALL=(root) NOPASSWD: /usr/bin/lxc-ls *
secubox ALL=(root) NOPASSWD: /usr/bin/lxc-info *
secubox ALL=(root) NOPASSWD: /usr/bin/lxc-start *
secubox ALL=(root) NOPASSWD: /usr/bin/lxc-stop *