mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-06-30 10:00:52 +00:00
Compare commits
3 Commits
4fd0d864ee
...
1d7ca0cd22
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d7ca0cd22 | ||
| a610ffd276 | |||
| 5800ad713d |
|
|
@ -41,6 +41,17 @@ def run_cmd(cmd: list, timeout: int = 60) -> tuple:
|
||||||
return "", str(e), 1
|
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:
|
def is_libvirt_running() -> bool:
|
||||||
"""Check if libvirtd is running."""
|
"""Check if libvirtd is running."""
|
||||||
_, _, code = run_cmd(["systemctl", "is-active", "--quiet", "libvirtd"])
|
_, _, code = run_cmd(["systemctl", "is-active", "--quiet", "libvirtd"])
|
||||||
|
|
@ -81,7 +92,9 @@ def get_virsh_vms() -> list:
|
||||||
def get_lxc_containers() -> list:
|
def get_lxc_containers() -> list:
|
||||||
"""List all LXC containers."""
|
"""List all LXC containers."""
|
||||||
containers = []
|
containers = []
|
||||||
stdout, _, code = run_cmd(["lxc-ls", "-f", "-F", "NAME,STATE,IPV4,MEMORY"])
|
# 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"])
|
||||||
|
|
||||||
if code != 0:
|
if code != 0:
|
||||||
return containers
|
return containers
|
||||||
|
|
@ -122,7 +135,7 @@ def get_lxc_info(name: str) -> dict:
|
||||||
"""Get detailed LXC container info."""
|
"""Get detailed LXC container info."""
|
||||||
info = {"name": name, "type": "lxc"}
|
info = {"name": name, "type": "lxc"}
|
||||||
|
|
||||||
stdout, _, code = run_cmd(["lxc-info", "-n", name])
|
stdout, _, code = run_priv(["lxc-info", "-n", name])
|
||||||
if code == 0:
|
if code == 0:
|
||||||
for line in stdout.strip().split('\n'):
|
for line in stdout.strip().split('\n'):
|
||||||
if ':' in line:
|
if ':' in line:
|
||||||
|
|
@ -319,7 +332,7 @@ def start_vm(name: str):
|
||||||
if is_lxc_available():
|
if is_lxc_available():
|
||||||
for c in get_lxc_containers():
|
for c in get_lxc_containers():
|
||||||
if c["name"] == name:
|
if c["name"] == name:
|
||||||
stdout, stderr, code = run_cmd(["lxc-start", "-n", name])
|
stdout, stderr, code = run_priv(["lxc-start", "-n", name])
|
||||||
if code != 0:
|
if code != 0:
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to start: {stderr}")
|
raise HTTPException(status_code=500, detail=f"Failed to start: {stderr}")
|
||||||
return {"status": "started", "name": name}
|
return {"status": "started", "name": name}
|
||||||
|
|
@ -347,7 +360,7 @@ def stop_vm(name: str, force: bool = False):
|
||||||
cmd = ["lxc-stop", "-n", name]
|
cmd = ["lxc-stop", "-n", name]
|
||||||
if force:
|
if force:
|
||||||
cmd.append("-k")
|
cmd.append("-k")
|
||||||
stdout, stderr, code = run_cmd(cmd)
|
stdout, stderr, code = run_priv(cmd)
|
||||||
if code != 0:
|
if code != 0:
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to stop: {stderr}")
|
raise HTTPException(status_code=500, detail=f"Failed to stop: {stderr}")
|
||||||
return {"status": "stopped", "name": name}
|
return {"status": "stopped", "name": name}
|
||||||
|
|
@ -371,8 +384,8 @@ def restart_vm(name: str):
|
||||||
if is_lxc_available():
|
if is_lxc_available():
|
||||||
for c in get_lxc_containers():
|
for c in get_lxc_containers():
|
||||||
if c["name"] == name:
|
if c["name"] == name:
|
||||||
run_cmd(["lxc-stop", "-n", name])
|
run_priv(["lxc-stop", "-n", name])
|
||||||
stdout, stderr, code = run_cmd(["lxc-start", "-n", name])
|
stdout, stderr, code = run_priv(["lxc-start", "-n", name])
|
||||||
if code != 0:
|
if code != 0:
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to restart: {stderr}")
|
raise HTTPException(status_code=500, detail=f"Failed to restart: {stderr}")
|
||||||
return {"status": "restarted", "name": name}
|
return {"status": "restarted", "name": name}
|
||||||
|
|
@ -404,9 +417,11 @@ def delete_vm(name: str):
|
||||||
for c in get_lxc_containers():
|
for c in get_lxc_containers():
|
||||||
if c["name"] == name:
|
if c["name"] == name:
|
||||||
if c.get('state') == 'running':
|
if c.get('state') == 'running':
|
||||||
run_cmd(["lxc-stop", "-n", name, "-k"])
|
run_priv(["lxc-stop", "-n", name, "-k"])
|
||||||
|
|
||||||
stdout, stderr, code = run_cmd(["lxc-destroy", "-n", name])
|
# 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])
|
||||||
if code != 0:
|
if code != 0:
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to delete: {stderr}")
|
raise HTTPException(status_code=500, detail=f"Failed to delete: {stderr}")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,16 @@
|
||||||
|
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
|
secubox-vm (1.0.0-1) stable; urgency=low
|
||||||
|
|
||||||
* Initial release
|
* Initial release
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,19 @@ 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
|
||||||
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/iso
|
||||||
install -d -o root -g root -m 755 /var/lib/secubox/vm/disks
|
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 daemon-reload
|
||||||
systemctl enable secubox-vm.service
|
systemctl enable secubox-vm.service
|
||||||
systemctl start secubox-vm.service || true
|
systemctl start secubox-vm.service || true
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,7 @@ override_dh_auto_install:
|
||||||
install -m 644 menu.d/903-vm.json $(CURDIR)/debian/secubox-vm/usr/share/secubox/menu.d/
|
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 -d $(CURDIR)/debian/secubox-vm/usr/lib/systemd/system
|
||||||
install -m 644 debian/secubox-vm.service $(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
|
||||||
|
|
|
||||||
9
packages/secubox-vm/debian/sudoers.d/secubox-vm
Normal file
9
packages/secubox-vm/debian/sudoers.d/secubox-vm
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# 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 *
|
||||||
Loading…
Reference in New Issue
Block a user