Skip to content

Releases: sysiblesoftware/Sysible-Controller

Sysible Controller 3.0.2

Choose a tag to compare

@sysiblesoftware sysiblesoftware released this 17 Jul 11:35
ddd6066

3.0.2 — 2026-07-16
A fleet-management and reliability release on top of 3.0.1: a much sturdier host enrollment lifecycle (kill-switches and clean-up for runaway/stale hosts), new system-administration tooling (full firewall management, a time-daemon installer, an Environment & Shell section), a console that scales to large fleets, and another round of web-surface hardening. No breaking changes — upgrade in place with sysible_controller update.

Added
Full ufw firewall management — reads status and the rule list, turns the firewall on/off, and adds/deletes allow/deny rules (by port/proto/source), not just an installer.
"Environment & Shell" tools section — manage system-wide environment variables (/etc/environment, profile.d drop-ins) and shell aliases from the console.
Time Synchronization — explicit Install buttons for chrony and NTP (previously the only path was "Configure chrony", with no NTP install at all).
Update Hosts — "Defer to maintenance window" — schedule the install into a recurring maintenance window instead of running it immediately (creates a managed Schedule).
Pause Enrollment kill-switch — one-click emergency brake that stops the controller accepting new enrollments; plus Revoke Checked and robust bulk Disenroll / Force Delete for runaway fleets.
disenroll_agent.sh and migrate_agent.sh in the agent bundle — host-side cleanup and controller-failover re-pointing, no console round-trip.
Deployed build version shown in the console ("Current build: v… · commit (branch)").

Changed
SSH host connections are being phased out in favour of the managed agent — existing SSH records are marked, individually deletable, and clearable via "Remove all SSH hosts"; tool pickers no longer offer SSH-only hosts.
Firewall tool — each installer sits with its own backend (ufw / firewalld / nftables) rather than a separate install group.
Busy tool pages auto-tab instead of one long vertical scroll.
SSH Auth Policy uses explicit intent buttons instead of a checkbox-then-apply toggle.
User & Group Administration — redesigned account list for fleet scale (keyed on the distinct account, coverage counts, per-environment drill-down + "create on the N missing hosts", clickable filter tiles).
Enrolled Hosts footer polish — only the primary action is solid red; Force Delete/Revoke are outline-danger, disabled until a host is checked; Pause/Resume drops its ⏸/▶ glyphs.
Agent terminal defaults to bash, not the systemd /bin/sh.

Fixed
Duplicate enrollment when a host reported an empty hostname (reconciler now adopts the existing record by IP).
Runaway host enrollment — stable host-id derivation + mass-revoke / robust bulk ops.
openSUSE / SLES user management crash (capture_output/text 3.7-only kwargs → 3.6-safe PIPE/universal_newlines, with a regression test).
Updater gives a clear remedy when tracked files are locally modified.
Ported four shared console fixes from Enterprise.

Security
Hardened web-facing defaults — Secure cookie on by default, CSRF Origin/Referer backstop, loopback-only /openapi.json, clamped list limits.
fstab-line injection via mount options (fixed) — whitespace/newlines rejected.
_validate_path rejects CR/LF as well as NUL.
Additional QA-sweep + infra/reliability hardening across command paths.
Pre-release enterprise security & UX audit findings:

HIGH — command injection via an admin username → root on SSH-managed hosts (fixed) — usernames charset-validated at ingest and the run-as status echoes shlex-quoted.
fstab-line injection via fstype / mount options / NFS export path / CIFS share (fixed) — all reject CR/LF/NUL; fstype is a single token.
Session & bearer tokens hashed at rest (SHA-256) — a leaked DB snapshot no longer yields replayable sessions (existing sessions re-login once).
Local-package upload is size-bounded (413) so an operator can't OOM the console.
Removed a stray NUL byte from a console source file.
Console reliability & hardening (audit follow-through):

No more wedging on a hung request or expired session — request timeout, a global 401→login handler, and a top-level error boundary.

Durable login brute-force lockout — moved to the database so it survives a restart/crash-loop; per-IP throttle now mutation-locked.
Python dependencies pinned for reproducible builds (agent keeps a compatible range).

Configurable controller bind via SYSIBLE_CONTROLLER_BIND; firewalling :9000 stays primary.

Sysible Controller 3.0.1

Choose a tag to compare

@sysiblesoftware sysiblesoftware released this 10 Jul 02:55
15519ad

Sysible Controller — Community 3.0.1
Released 2026-07-10

A large security-hardening and reliability release on top of 3.0.0, plus new fleet-management conveniences. Every managed-host command path, the agent↔controller protocol, and the web console were re-audited from scratch; the highlights below group the results by theme. No breaking changes — upgrade in place with sysible_controller update.

Security

Command execution & injection

  • Grub command injection → root on the managed host (fixed). cmd_set_grub_default concatenated the raw menu entry into an echo, so an entry like 0'; <cmd>; echo ' ran <cmd> as root. The value is now shlex-quoted via printf and never enters the shell string.
  • SSH argument injection → controller root code execution (fixed). A stored SSH host's user/ip flowed into the ssh command line; a value like -oProxyCommand=… was parsed by ssh as an option and ran on the controller as root (and could be triggered automatically by the read-only fleet sweeps). Host user/username/ip/name are now charset-validated at ingest and the ssh argv carries an explicit -- before the destination.
  • Defense-in-depth on the unit/mount builders. The systemd service/timer builders reject newline breakout of the unit-file heredoc and //.. in a unit name; the mount builder rejects newlines in a mount point (no injected /etc/fstab entry); and an agent-reported ip is charset-validated at ingest.

Authentication, sessions & authorization

  • Forced first-login password change is enforced server-side — every write/dispatch route (and the interactive terminal WebSocket) returns 403 until the temporary password is rotated, and a username-only credential change no longer lifts the gate. Previously this was only a frontend modal an operator could skip.
  • The console session is revalidated against the live account. A demoted or removed admin's signed cookie used to keep BFF-gated powers until the 12h token expiry. Each request now re-checks the admin token against the controller (new GET /admin/whoami, TTL-cached to one call/minute/session) and drops the session the moment the token is revoked (SYSIBLE_SESSION_REVALIDATE_TTL).
  • Admin-login throttle is per-username, and login is constant-time. The lockout was keyed on the caller's IP — which for every console login is the single BFF, so ten failures locked out all admins; it's now per-account. A decoy PBKDF2 verify runs when the username doesn't exist (admin and portal logins), closing a username-enumeration timing oracle.
  • Pure-SSH file upload/download now requires superuser on the controller (was API-key-only), matching the BFF's separation of duties for the SFTP-as-root path.
  • Logout revokes the controller token, not just the stateless cookie, so a captured token can't linger to expiry. /api/me fails closed to auditor on a missing role.

Secrets, keys & TLS

  • TLS pinning is fail-closed. A missing pin file on an https:// controller used to silently fall back to system-CA verification (any CA in the store could MITM the agent/BFF→controller channel); it now refuses to connect (SYSIBLE_ALLOW_SYSTEM_CA=1 to opt into the system trust store).
  • Secrets are created 0600 atomically (O_EXCL|O_NOFOLLOW) — the sudo-store key, the admin API key, and the cookie-signing secret — closing a world-readable umask race a local user could exploit to read the key or forge admin cookies.
  • The install-time default admin password is passed via the environment, not argv, so it's no longer visible to any local user through ps//proc/<pid>/cmdline.
  • The agent no longer puts its secret in a URL. The interactive-terminal long-poll sent the agent secret as a query parameter, recording a live credential in the controller access log every ~25s; it now uses the X-Agent-Secret header.

Enrollment & host identity

  • host_id is charset-validated at enrollment (alphanumeric plus ._-, reserved sentinels refused) — an injection-shaped or *-style id is rejected outright.
  • Enrollment adoption can no longer bypass revocation or hijack a host. The re-enroll "supersede" path matched on the unauthenticated request-body IP and could resurrect an admin-revoked host or seize an offline host's identity. Adoption is now narrow (same hostname and IP, never a revoked record, never a live host), and a revoked host must be explicitly Restored from the console.
  • Alert webhook delivery is pinned to the verified IP — the SSRF guard checked the resolved host was public but urlopen re-resolved it (DNS rebinding); the request now connects to the exact IP that passed the check (TLS cert/SNI still validated against the hostname).

New features & capabilities

  • The controller enrolls itself as a managed host. On first start it installs a privileged local agent pointed at loopback and enrolls itself, so it appears in the fleet like any other box — patch it, run scripts on it, open a terminal into it. Idempotent, best-effort, opt-out via SYSIBLE_NO_SELF_ENROLL=1; run on demand with sudo sysible_controller self-enroll.
  • Restore a revoked host in place — a Restore button (and POST /agents/{id}/restore) un-revokes without a destructive re-enroll, keeping the agent secret so a still-installed agent resumes immediately.
  • Force Delete now permanently removes a zombie host by also purging the enrollment token bound to it, so a still-running agent can't re-enroll onto the same id with the old token. tools/unenroll_agent gained the same purge, plus --ip/--name matching, DB auto-detection, and --dry-run.
  • Deployment guard. The controller reports which directory and git commit the live process is running from (GET /version, Settings → Controller Configuration), and shows a red "restart needed" banner when the on-disk code has moved since start.
  • On-demand certificate management — a "Regenerate self-signed certificate" action reissues for the current address and restarts atomically; a "Download / Regenerate agent bundle" button re-mints a fresh bundle (new single-use token) after a hostname/IP change.
  • URL-addressable navigation — every console view has a URL (?view=<key>), so you can ⌘/Ctrl-click, middle-click, open in a new tab, and bookmark/share a view.
  • Run a script across the hosts you choose — clarified in Sysible Connect and added as a "Run a script" panel in Quick System Actions.
  • Standalone New Environment button, and an offline documentation download (GET /api/docs/download).

Reliability & fixes

  • Host updates no longer fail on far-behind hosts — the agent command cap moved 5→30 min (SYSIBLE_AGENT_CMD_TIMEOUT) so a real dnf/apt/zypper upgrade isn't SIGKILLed mid-transaction; "Refresh metadata & rescan" allows 180s for a slow mirror; zypper security updates no longer skip license-gated patches.
  • Dashboard host counts are consistent and stable — enrolled/online/offline come from the instant agent inventory instead of the slow probe sweep, so a host no longer "falls off" to 0 during a sweep; revoked hosts are excluded from counts, donut, and fleet-action set.
  • Quarantined hosts fail fast instead of hanging (install reports the reason immediately; sweeps skip them).
  • Re-enrolling a host no longer auto-quarantines it or creates a duplicate "zombie" record at the same IP.
  • "Set Environment" no longer times out — hot enroll/disenroll DB writes release the SQLite WAL writer even on error, and the console write timeout was raised above the DB busy-timeout.
  • Self-update is robust — read-only git ls-remote for the update-check, stale-lock cleanup, and auto-repair of a corrupted ref store instead of dead-ending on "reference already exists"; /update-status bounds its remote check (no phantom agent); "Update agents" excludes revoked hosts.
  • Regenerating the self-signed cert no longer leaves the controller down (restart scheduled via a detached systemd-run timer outside the service cgroup); changing the controller address only flags the cert stale rather than breaking pinned clients.
  • Terminal to an offline agent fails fast; abandoned agent-terminal sessions no longer leak memory. "Upload & install a local package" works on agent-managed hosts. Username-only self-service change works. Host Enrollment refreshes are lighter (lazy portal data).
  • Web console display fixes from a full click-path audit: Webserver Portal timestamps, Environmental Policies "Save" actually persisting, Performance chart edge buckets, Lock/Unlock/Delete confirmations, self-disenroll serialization.

Performance

  • Posture / fleet-health sweeps are much faster — integrity find / walks capped at 8s (SYSIBLE_POSTURE_FIND_TMO), up to 32-way concurrency (SYSIBLE_SWEEP_CONCURRENCY); the ~5s reverse-DNS host-list stall on GET /agents is gone.

Platform & compatibility

  • openSUSE / SLES fixes across the host command builders — sudo policy detects the wheel group (no sudo group on SUSE/RHEL, so the policy was silently ineffective); kernel listing, sshd, and firewalld paths/deps corrected; installer seeds python3-gobject where needed.
  • TLS material is written atomically (cert/key/trust) so an interrupted install can't leave a half-written trust bundle.

Testing

  • New regression coverage: SSH host-injection rejection, the self-enroll bundle, /admin/whoami + per-username throttle, and the unenroll_agent force-removal path.

Want me to draft this as a GitHub Release via the API if you can grant the session tag/release permission, or is creating it from the UI/your machine easier? Either way, the commit and notes are ready on main at 15519ad.

Sysible Controller 3.0.0

Choose a tag to compare

@sysiblesoftware sysiblesoftware released this 07 Jul 17:16
b99ee16

Sysible Controller 3.0.0

First official tagged release. Sysible Controller is now fully browser-based — the legacy desktop (PySide6) GUI, X11-forwarding, and FreeRDP/RDP paths are gone. Everything is delivered through the web console, so the controller can live on a headless server and administrators can work from any browser on Windows, macOS, or Linux.

🔒 Security

  • Closed a client-controlled audit/authorization bypass — an operator could set log=false on an SSH exec to skip both the read-only-auditor block and the audit record. The auditor block is now unconditional.
  • Enrollment-token replay hardening — a leaked token can no longer take over a still-live host or resurrect an administrator-revoked one.
  • Agent integrity — a host that sealed a measurement baseline and then stops reporting is quarantined (evasion-by-omission closed); the integrity state store is locked and written atomically at 0600.
  • Payload size caps — agent-channel payloads (metrics / snapshot / measurements / task result / PTY output) are size-capped to bound controller memory against a hostile agent.
  • Secret scrubbing — audit-log command text is scrubbed of secret-bearing arguments (--password, --token, Authorization: Bearer, KEY=value, …).
  • No cross-host SSH clobber — an agent-reported IP can no longer delete or repoint another host's SSH record; the collision is surfaced instead.
  • Terminal auditing — interactive terminals are audited (open/close) and bound to the operator who opened them.
  • Forced password change is now enforced in the console (previously ignored).

⚙️ Reliability & operations

  • Fixed a web-console startup crash (module-level use of threading before its import).
  • Serialized the heartbeat-path JSON stores to end lost-update races under concurrent heartbeats.
  • Hottest DB writers release their SQLite connection even on exception — no leaked WAL reservations compounding lock contention.
  • Fleet-health is cached and shared across concurrent dashboard loads instead of re-probing every host per load.
  • A failed agent task reports back immediately instead of hanging until a 15-minute reclaim rewrites it as a fabricated timeout.
  • Foot-gun guardrails: the last superuser can't be deleted; an "all hosts" reboot/power-off skips the controller's own node; destroy's DB backup is 0600; deleting an environment with hosts is refused; duplicate environment names return a clear error.
  • Force Delete for zombie hosts — Enrolled Hosts can immediately drop a broken agent build that keeps heartbeating but can't cleanly disenroll, skipping the graceful teardown that would otherwise stall. The record deletion also locks the agent out on its next heartbeat.
  • Replacing the TLS certificate now warns and confirms (it breaks pinned agents until the trust bundle is redistributed).
  • Agent install now works on SUSE and other minimal imagesrun_agent.sh installs Python 3 if absent, prefers the distro python3-requests package (no PyPI/compiler, sidesteps PEP 668), only falls back to pip and only passes --break-system-packages when that pip supports it, and hard-fails with clear per-distro guidance if requests still can't be installed. The systemd unit points at the python3 actually found rather than a hardcoded /usr/bin/python3.

✨ Usability

  • Host-enrolled notification — when a new host enrolls, the console pops a toast from any page and records it in the Live Activity feed.
  • Community Edition: all host/administrator seat caps removed; a small "Community Edition" badge replaces the counts.
  • Nav reordered to follow the fleet workflow; consistent file-transfer/browse controls; de-cramped Enrolled Hosts rows.
  • Schedule builder validates its fields (no more */0 / NaN cron) and notes that jobs run in the target host's local timezone.
  • Accessibility & consistency pass: focus rings, colour contrast, missing confirmations, and loading indicators.

🧪 Testing & tooling

  • Exhaustive API test-suite covering authentication, RBAC/permissions, input validation, SQL/XSS injection, size caps, duplicate requests, and rate limiting. Run with pytest.
  • SessionStart hook that provisions the test environment for Claude Code on the web.

🧹 Housekeeping

  • Removed legacy backward-compat shim scripts (sysible, start_sysible.sh, stop_sysible.sh). Use sysible_controller {start|stop|…}.
  • Removed all desktop-GUI / xserver / RDP / FreeRDP references and host/user seat-cap language from the README, SECURITY, and docs.

📚 Documentation

  • README and SECURITY updated for the current feature set, plus a Known limitations & operational notes section (TLS trust-bundle refresh, SIEM forwarding for durable audit, single-node SQLite write ceiling, bearer enrollment tokens, host-local schedule timezones).

Upgrading: run the update flow for the controller and agents as usual. Agent bundles are regenerated with the more robust Python bootstrap, so previously-failing SUSE/minimal hosts will install cleanly.