Skip to content

0.11.9

Latest

Choose a tag to compare

@omega8cc omega8cc released this 10 Sep 22:30

BOA fork of lshell, version 0.11.9 — the first tagged 0.11 release on this repository (the previous public tag is 0.10.10.1). Base: upstream ghantoos/lshell 0.11.1, merged 2026-09-02; every BOA change since is below, newest first. The tarball attached is what BOA installs (lshell-0.11.9.tar.gz, md5 eb89bd53b0ad72c499edf4849fb7cce2).

v0.11.9 (BOA fork, 2026-09-10)

  • Security: the Landlock exemption is decided for the whole line. is_exempt read the first word of the joined pipeline, so ping -c 1 host | composer install (both passwd and ping are exempt setuid tools in BOA's config, | is not forbidden) ran composer and every process it spawned with no ruleset at all, the same line-level hole 0.11.8 closed for the noexec layer. A line is exempt only when every one of its segments is; in practice a single exempt command.
  • A kernel refusal of the ruleset at exec time (landlock.restrict raises OSError inside preexec_fn, re-raised in the parent with the child's own type) is handled like every other containment failure (denied, exit 126) instead of ending the session with a traceback.
  • The prompt honours $LPS1 again as the whole prompt (the 0.11.1 merge dropped the override the man page and sample config document), prompt_short : 2 renders the path it is given, and the BOA prompt_short : 1 shapes ([dir], [sites@client]) are what the suite asserts.
  • landlock_ro, landlock_rw and landlock_exempt take the + [...] / - [...] merge like every other list key; a per-group override in that syntax used to refuse the login.
  • The typed line, not the LD_PRELOAD-prefixed one, is stored as a job's command (jobs) and logged on a containment failure, as exec_cmd's own contract says.
  • README: the log carries the Landlock rule counts and refused symlink targets, not the rules themselves.

v0.11.8 (BOA fork, 2026-09-07)

  • Security: the noexec layer is applied per pipeline segment, in the line itself. 0.11.7 handed the library over in LSHELL_NOEXEC with a decision taken per LINE (any segment in allowed_shell_escape => nothing handed over), so find ... -exec ... | true ran find without the preload: one shell-escape segment anywhere in a pipeline disarmed the layer for the whole line (measured on a BOA box through a real tenant session). Now cmd_parse_execute prefixes every segment whose executable is outside allowed_shell_escape with LD_PRELOAD=<library>, a POSIX assignment prefix the shell applies to that one command only, and leaves shell-escape segments as typed: drush status | grep x preloads grep and not drush, find ... | true preloads find. A segment led by a POSIX special builtin is never prefixed (the assignment would persist), nor one led by a landlock_exempt setuid tool, sudo/su or an SFTP protocol binary (the loader ignores LD_PRELOAD for setuid binaries, and those names are read as the command by the exempt test and the privileged branch); exec_cmd now takes the typed line as display and uses it for every message, log line and decision, so only the shell ever sees the prefix; landlock.is_exempt skips leading VAR=value words (LANG=C passwd stays exempt); a user's own LD_PRELOAD= is still refused; with a usable path_noexec the upstream whole-line environment form applies instead. LSHELL_NOEXEC is gone; no dispatcher support is needed, the shell that finally runs the line does the work.

v0.11.7 (BOA fork, 2026-09-07)

  • Security: the noexec layer is back for commands outside allowed_shell_escape. It could not be preloaded on the shell commands run through (0.11 runs every command as [exec_shell, "-c", cmd], and a working sudo_noexec.so on that shell stops it executing anything), so the library found by set_noexec is now kept as noexec_library and handed to the command in LSHELL_NOEXEC; a dispatcher that knows the variable (BOA's websh) applies LD_PRELOAD to the command it finally launches, so find -exec, an editor's shell escape or GNU sed's e can no longer exec anything, while a plain shell ignores the variable and behaves as before. Shell-escape commands never carry it, and a value already in the session environment is dropped at every launch, so it cannot ride into a shell-escape command's children.

v0.11.6 (BOA fork, 2026-09-07)

  • Fix: say what the noexec check actually decided. A box that ships sudo_noexec.so logged disabling incompatible noexec library followed by noexec library not found at every session -- both misleading: the library is present and working, and preloading it is refused only because 0.11 runs every command as [exec_shell, "-c", cmd], so LD_PRELOAD lands on that shell and would stop it executing anything (measured on a BOA box: wget, openssl, grep, sed and find all exit 126). One accurate line is written instead, not found is kept for a library that really is absent, and the probe carries the reason it must keep executing a binary, with unit tests that fail if it is turned into a builtin probe.
  • Fix: path_noexec : '', the documented way to switch LD_PRELOAD off, silently made every allowed_shell_escape command forbidden (the early return skipped the merge that folds that list into allowed) and left an empty path_noexec in the runtime config, which utils.exec_cmd reads as "preload configured" and turns into LD_PRELOAD= on every command. The list is merged and the key dropped.
  • Note: under 0.10 the preload was applied to the command process itself, so the noexec layer was real there. It is unavailable in the 0.11 execution model; confinement rests on Landlock, the allowed/forbidden lists and the dispatcher named by exec_shell.

v0.11.5 (BOA fork, 2026-09-07)

  • Security: only a root-owned symlink inside a user's path root extends the Landlock read-write set. A link the user can create or move is ignored (owner and target are read from the one open link inode), the shared landlock_rw roots (/tmp, /var/tmp, /dev) are never scanned, configured roots are compared by their real paths, and a derived target that is /, a parent of a configured root, or a landlock_ro root or inside one is refused and logged. Before, any link in those places widened the confinement of every later shell session, up to a / rule that dissolved it, and a link into a read-only root silently turned it read-write.

v0.11.4 (BOA fork, 2026-09-06)

  • Security: the per-user log file is created and kept 0600 (owner only), as 0.10 did. 0.11 set 0660 at every session start; with the log directory shared by every restricted user and the file's group being the user's primary group, a neighbour in the same group could read the whole command history for the window between a login and the next 0600 re-assertion.

v0.11.3 (BOA fork, 2026-09-02)

  • Fix: symlink targets are resolved three levels inside each read-write root (was two), so a per-user drush extension farm at ~/.drush/usr/<tool> pointing into the account tree keeps loading under Landlock (found on the first real-shell upgrade drill: drush8 failed to open a command file).

v0.11.2 (BOA fork, 2026-09-02)

  • Feature: Landlock child-process confinement (landlock, landlock_ro, landlock_rw, landlock_exempt, landlock_strict): every command and every process it spawns is confined by the kernel to the configured roots plus the user's path entries and home, applied before exec and inherited, never liftable. Fails open with a log line on kernels without Landlock unless strict.
  • Feature: exec_shell (default /bin/sh) names the shell commands run through, restoring the /bin/sh -c execution of 0.10 so a dispatcher installed as /bin/sh is honoured (upstream hardcodes bash -c).
  • BOA prompt shapes kept (user:[dir]$, user:[sites@client]$).