Skip to content

Stage 2: web, runtime and database modules - #21

Draft
VectoDE wants to merge 92 commits into
feat/lss-1bfrom
feat/lss-2
Draft

VectoDE wants to merge 92 commits into
feat/lss-1bfrom
feat/lss-2

Conversation

@VectoDE

@VectoDE VectoDE commented Oct 13, 2026

Copy link
Copy Markdown
Owner

Stage 2 of the lss 2.0 rebuild (#4): web layer, nginx, apache, caddy, tls, vhost, haproxy, modsecurity, runtimes, databases and admin interfaces. Draft until every module of the stage is in and CI is green.

Refs #4

🤖 Generated with Claude Code

VectoDE and others added 7 commits October 13, 2026 21:07
- svc_sandbox UNIT [DIRECTIVE...] writes a systemd drop-in that confines
  a service (read-only system, private /tmp and /dev, no new privileges,
  no kernel tunables, modules or logs); services add the paths they
  write or relax what they cannot live with.
- lib/web.sh holds the conventions the web, runtime and database modules
  share: the web group, the PHP-FPM socket, the TLS and ACME directories
  and the admin port, plus web_server and web_group_ensure.
- A module that runs in docker mode requires docker or podman.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The three web servers share the web layer: HTTPS only with TLS 1.2/1.3,
security headers, a closed default host that answers ACME challenges
only, an admin port on 127.0.0.1 (or public with TLS and basic auth),
a systemd sandbox and the fail2ban or CrowdSec integration. Caddy comes
from its signed upstream repositories with pinned key fingerprints.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The admin API listened on localhost:2019, where every local account
could replace the running configuration; it now uses a unix socket
readable by the caddy user only. The private admin site matched the
Host header but listened on every interface; bind keeps it on the
loopback interface. file_server served dot files such as .git and
.env, which nginx and Apache deny; a shared snippet now answers 404.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
tls sets up certbot for every web server: webroot challenges on the
shared ACME directory, ECDSA keys, any ACME server (a private CA such as
Pebble is trusted through LSS_TLS_CA_BUNDLE), the renewal timer and a
deploy hook that installs each certificate below /etc/lss/tls and
reloads the web server. LSS_TLS_DOMAINS replaces the self-signed default
certificate with a real one.

vhost publishes static, PHP and reverse-proxy sites. Each site starts
with a copy of the default certificate and gets its own from the tls
module; sites that leave LSS_VHOST_SITES are removed. The daily guard
reports certificates that expire within 14 days.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
HAProxy balances services on their own ports (80 and 443 stay with the web server) with health checks, a statistics page on 127.0.0.1, a sandbox, SELinux's haproxy_connect_any where needed and firewall rules that follow LSS_HAPROXY_SERVICES. The vhost backend URL check now accepts IPv6 addresses.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Unit tests cover the rendering of sites and admin paths for nginx, Apache and Caddy, the HTTP/2 switch at nginx 1.25.1, OCSP stapling and the certificate helpers. Apache proxies keep the request path the same way nginx and Caddy do and upgrade WebSockets. fail2ban and CrowdSec add the web server jails and log sources when they are installed after the web server.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The new integration group web installs each web server alone and together with tls and vhost, with certificates from Pebble (pinned by SHA-256) running inside the target, then HAProxy. run.sh tests comma-joined modules together and removes them in reverse order.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Comment thread lib/web.sh Fixed
Comment thread lib/web.sh Fixed
Comment thread lib/web.sh Fixed
VectoDE and others added 22 commits October 13, 2026 21:29
Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
php installs PHP-FPM with the common extensions from the distribution (on
RHEL 9 from its newest module stream) and runs one pool "lss" as its own
user on WEB_PHP_SOCKET (root:lss-web, 0660). The distribution's www pool
is switched off, php.ini settings are hardened in a drop-in, dangerous
functions are disabled for the web pool only, and the FPM unit runs in
the lss sandbox. LSS_PHP_VERSION can ask for another version, which
Debian and Ubuntu get from Ondrej Sury's repositories (pinned keys).
verify sends a real FastCGI request over the socket.

composer installs a checksum-verified composer.phar (2.10.3) as
/usr/local/bin/composer.

Refs #4

Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Node.js from the NodeSource repository (pinned keys for apt and RPM), major
version 22, 24 (default, the active LTS line) or 26, on every supported
system and both architectures. An apt pin keeps the distribution's older
nodejs out; switching LSS_NODE_VERSION moves the installed package over.

Refs #4

Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
python installs python3 with venv, pip and pipx from the distribution
(pipx from EPEL on the RHEL family); verify builds a virtual environment.
ruby installs ruby, Bundler and the headers plus a compiler for native
gems. Both remove only the packages lss installed, never python3 itself.

Refs #4

Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
grep -q stops reading at the first match, so php-fpm got SIGPIPE and the
pipeline failed under pipefail although the setting was in effect.

Refs #4

Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OpenJDK from the distribution (headless JDK on Debian and Ubuntu, the
-devel JDK on the RHEL family), version 21 by default. Where the
distribution has no OpenJDK of that version (Debian 12 stops at 17), the
module installs Eclipse Temurin from the Adoptium repository with a pinned
key. verify compiles and runs a test class.

Refs #4

Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Go 1.27.2 from the official release archive, checked against the SHA-256
the Go team publishes (amd64 and arm64), unpacked into /usr/local/go with
a profile.d entry for the PATH. A pinned version is replaced only when it
changes; verify runs go version and checks a login shell finds it.

Refs #4

Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The .NET SDK, 10.0 (LTS) by default, from the source each system has:
Ubuntu's archive, Canonical's backports PPA where the archive lacks the
version (.NET 10 on 22.04), AppStream on the RHEL family and Microsoft's
repository on Debian (both pinned keys). DOTNET_CLI_TELEMETRY_OPTOUT=1 is
set for login shells, PAM sessions and systemd user services.

Refs #4

Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ShellCheck (all optional checks) reads OS_NAME as a misspelt MOD_NAME;
OS_ID and OS_VERSION say the same. The login-shell probe of dotnet keeps
its single quotes on purpose.

Refs #4

Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The three database admin interfaces live on the admin port of the installed web server (127.0.0.1:8090 unless
admin_public is set): phpMyAdmin under /phpmyadmin/, Adminer under /adminer/ and pgAdmin under /pgadmin4/.

phpMyAdmin 5.2.3 and Adminer 6.1.2 are downloaded with the SHA-256 their vendors publish (new data/checksums.txt).
phpMyAdmin gets a config.inc.php with a blowfish secret, cookie login against 127.0.0.1 only, no arbitrary
servers, no root login by default, no version check or error reports, and its template cache in PHP-FPM's private
/tmp; the setup wizard is not installed. Adminer gets a login plugin that offers and allows only the configured
local servers.

pgAdmin 4 9.18 runs from a virtual environment on every system and architecture (the vendor packages are
x86_64-only and have no WSGI server), with pinned, checksummed wheels of pgadmin4 and gunicorn, as its own
sandboxed service lss-pgadmin on 127.0.0.1:5050. The first user is created non-interactively; its password is the
secret pgadmin-admin-password.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
pgAdmin uses Socket.IO; Apache now upgrades those connections on the admin port as nginx and Caddy do.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
MariaDB from the distribution (the 10.11 LTS module stream on RHEL 9) on
127.0.0.1:3306, configured through a drop-in: no LOAD DATA LOCAL, no
symbolic links, utf8mb4. Every run keeps what mysql_secure_installation
does true in SQL: root only through the Unix socket, no anonymous or
remote root accounts, no test database. Optional databases and
application users with generated passwords, a systemd sandbox, a
firewall rule only when access is vpn or public, and a SELinux port
label for a non-default port.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
MySQL on 127.0.0.1:3306, the 8.4 LTS release wherever the system offers
it: Oracle's APT repository on Debian amd64 (Debian ships no MySQL),
Oracle's generic aarch64 build on Debian arm64 (the repository has no
arm64 packages; the archive is pinned by SHA-256 in data/checksums.txt
after its MD5 and GnuPG signature were checked), the mysql:8.4 module
stream on RHEL 9 and mysql8.4 on RHEL 10, and the distribution package
on Ubuntu.

Root logs in through auth_socket on Debian and Ubuntu; on the RHEL
family it gets a generated password in /root/.my.cnf (mode 0600). Every
run removes anonymous and remote root accounts and the test database,
LOAD DATA LOCAL is off, the X Protocol stays on localhost. Optional
databases and application users, a systemd sandbox and a firewall rule
only for vpn or public access.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The generic build was initialised with --initialize-insecure, and on the
RHEL family mysql-prepare-db-dir does the same on the first start, so
root had an empty password until the configure step changed it; a run
that failed in between left it that way.

lss now creates the data directory itself with --initialize and an
--init-file that sets root in the same bootstrap, before the server
accepts any connection: auth_socket for the generic build, the
generated password on the RHEL family (mysql-prepare-db-dir then finds
the directory initialised). The init file lives in a private directory
readable by root and the mysql group and is deleted right away; a
failed bootstrap empties the data directory again. Existing data
directories with an empty root password are still repaired.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The bootstrap of a MySQL data directory now refuses a directory that is
a symbolic link or has any content, so emptying it after a failed
bootstrap only removes what that bootstrap wrote; an existing data
directory is never touched. The private directory of the init file is
checked before it is deleted. The purge paths delete only the fixed
directories of the module (data, files, keyring, the unpacked build),
never through a symbolic link, and stop the step on anything else.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
PostgreSQL from the distribution (the 16 module stream on RHEL 9, whose
default 13 is past its end of life) or, with LSS_POSTGRESQL_VERSION,
14 to 18 from the PostgreSQL project's repositories (apt.postgresql.org
and download.postgresql.org, keys pinned; the RPM repositories check
packages and metadata).

Settings go into a conf.d file and an lss pg_hba file, so removing the
module returns the cluster to the distribution's configuration:
listen_addresses localhost, SCRAM passwords, TLS on (the snakeoil
certificate on Debian and Ubuntu, a self-signed one generated by the
postgres user elsewhere) with TLS 1.2 at least. Only the postgres
system user logs in through peer; every other role needs its SCRAM
password, also on the local socket; remote networks only through
hostssl. initdb on RHEL runs with peer and SCRAM, never trust. Optional
databases and roles with generated passwords, a systemd sandbox, a
firewall rule only for vpn or public access.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…d paths

Tenant isolation: pg_hba names each role with its own database (local
socket, loopback and, TLS only, the LSS_POSTGRESQL_ALLOW networks); no
"all" rule exists except peer for the postgres system user. Every
database lss creates has CONNECT revoked from PUBLIC, CREATE on its
public schema revoked from PUBLIC (needed before PostgreSQL 15) and the
schema handed to the owning role. Roles are created and kept without
SUPERUSER, CREATEDB, CREATEROLE, REPLICATION and BYPASSRLS. verify
rejects any other pg_hba rule and a pg_hba with errors.

Root no longer writes into directories postgres owns: the settings and
pg_hba live in /etc/lss/postgresql (root-owned, group postgres), the
include line in postgresql.conf is appended by the postgres user, and
every check or command in the data directory runs as postgres. The
include is include_if_exists, so a removed module leaves a cluster that
starts with the distribution's configuration.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The private directory for the init file was created inside /run/mysqld,
which belongs to the mysql user, who could have replaced it with a link
between mktemp and chgrp/chmod. It now lives directly in root-owned
/run (labelled mysqld_var_run_t where SELinux is on).

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Redis from the distribution (redis-server on Debian and Ubuntu, redis on
RHEL 9) and, on RHEL 10, which ships Valkey instead, from the Redis
project's RPM repository (packages.redis.io, key pinned, packages
signed).

lss writes its own configuration to /etc/lss/redis.conf and points the
unit at it with a drop-in: bind 127.0.0.1 and ::1 (left out without
IPv6), protected mode, journal logging, optional maxmemory and policy.
The default user's password is a generated secret, stored in the
configuration only as its SHA-256 and in clear text in
/etc/lss/redis-password (0640, group lss-redis) for clients. FLUSHALL,
CONFIG, DEBUG, SHUTDOWN, MODULE, MONITOR, replication, MIGRATE and ACL
are blocked through the ACL, not rename-command. verify pings with the
password from the environment (REDISCLI_AUTH), checks that a command
without it is refused and that CONFIG is blocked. systemd sandbox.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Valkey from the distribution on Debian 13, Ubuntu 24.04 (7.2 from
noble-updates) and 26.04 and RHEL 9 and 10 (AppStream), from
bookworm-backports on Debian 12 (Debian archive key, an apt pin limits
backports to the valkey packages) and on Ubuntu 22.04, which has no
package, the Valkey project's jammy build (SHA-256 in
data/checksums.txt) in /opt with a valkey user and a unit of lss's own.

Hardening and checks as in the redis module: lss configuration in
/etc/lss/valkey.conf, localhost only, password as SHA-256 in the ACL of
the default user, dangerous commands blocked, password for clients in
/etc/lss/valkey-password, verify with authenticated PING, refused
unauthenticated command and blocked CONFIG. systemd sandbox. Purge
deletes only the module's fixed directories and refuses symbolic links.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
VectoDE and others added 22 commits October 14, 2026 18:54
The Debian and Ubuntu packages log to /var/log/mysql and create
/run/mysqld in ExecStartPre; under the sandbox both were read-only.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
… 4 everywhere

Ubuntu 22.04 ships ModSecurity 2.9.5, which cannot load CRS 3.3.10
('Unknown variable: &MULTIPART_PART_HEADERS') nor CRS 4. lss builds
2.9.15 from the release signed by OWASP ModSecurity, so every system
runs CRS 4.25 with every rule exclusion plugin; the CRS 3.3 path is gone.
A missing audit log entry now shows the web server's error log.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…tics

The CRS install still called the removed version helper. The MySQL start
under the sandbox hangs on the amd64 runners only; their error log and the
runner's AppArmor denials show why.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Ubuntu's AppArmor profile of mysqld has no attach_disconnected, so in the
mount namespace of the sandbox it denies mysqld the systemd notification
socket and the start never completes. Where that profile confines
mysqld (or AppArmor is on and the profile list is hidden, as in a
container), the sandbox leaves out the settings that need a mount
namespace; the profile confines mysqld's files instead.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Only an enforcing profile counts. Without a readable profile list the
AppArmor label of a running process decides; with neither, the program
counts as unconfined and keeps the full sandbox.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Refs #12

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…security2

swapon on XFS refused the file as not committed: the chmod after the
flush dirtied its inode again. The flush now comes right before the
unit starts. The mod_security2 build on Ubuntu 22.04 needs pkg-config.
A missing audit log entry now also shows the web server processes, the
holders of the log and kernel denials.

Refs #4, #12

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
libmodsecurity 3.0.17 opens the audit log in the worker processes,
which run as the web server user and could not reach the root-only
directory, so nothing was logged on Ubuntu 26.04. The directory now
lets that user in (0710) and the log belongs to it (0640).

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Lynis logs "assigned partial number of hardening points (X of Y)", not
the format the first version of the listing expected.

Refs #12

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…sing

The audit log of nginx on Ubuntu 26.04 stays empty although the worker
may write it and the request is blocked. A failed check now repeats the
request with the engine's debug log on and shows what it says about
audit logging.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The module tests skip upgrades for time, so the Lynis audit after a
profile counted every pending security update (PKGS-7392, 25 points on
the Ubuntu images). A profile now runs with the default upgrade=true,
as on a real server.

Refs #12

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…atched sources

Ubuntu 26.04 patches nginx 1.28.3 with a port field in
ngx_http_request_t (backport of $request_port). --with-compat does not
detect a changed struct layout, so the connector built from the plain
nginx.org sources decremented the wrong field: requests were never
freed, and the log phase with the audit log never ran, although the
rules still blocked.

nginx-dev of exactly the installed package version now overlays the
nginx.org tree where the release has it. The build stamp also records
the package version, so a distribution update of the same nginx
release rebuilds the module.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
It served to find why Ubuntu 26.04 wrote no audit entries (the nginx
struct layout, fixed by building against nginx-dev). The verify step
keeps the directory listing and the error log tail.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Measured by the integration run of 01c573e on all seven systems,
x86_64 and arm64; modules that only work together are listed as the
set they were tested in.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Pull requests keep the single Debian 13 run; the nightly and manual
runs install every module at once on Debian 12 and 13, Rocky Linux 9
and AlmaLinux 10 as well.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Ubuntu 22.04, 24.04 and 26.04 cloud images, checked against the
SHA256SUMS Canonical publishes, complete profile all on every supported
distribution; the runners themselves carry preinstalled databases and
cannot run it cleanly. The checksum error of vm.sh no longer starts with
"No".

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The admin interfaces listen on port 8090, which the RHEL policy does
not count as http_port_t, so nginx could not bind it (Permission
denied) once SELinux enforced; the containers of the module tests run
without SELinux, and profile all on the Rocky Linux 9 and AlmaLinux 10
VMs found it. nginx, Apache and Caddy now label the port first.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Once auditd runs, the kernel's denials go to its log, not the journal,
so the diagnostics of a failed run missed them (php-fpm timed out on
the Ubuntu 24.04 VM in profile all without a visible cause).

Refs #12

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
profile all on the Ubuntu 26.04 VM got 403 for phpMyAdmin with no
hint why; PHP-FPM, file permissions and ModSecurity all answer 403.
phpMyAdmin, Adminer and pgAdmin now print the error log tail before
they stop.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The apparmor package confines PHP-FPM to its own files. On Ubuntu 24.04
it also lacks the systemd notification socket (LP: #2061113), so the
package's own start timed out; on 26.04 the lss pool could not read
phpMyAdmin under /usr/share/lss, and no web root or database socket
would have been readable either. The module writes the profile's local
include before the packages start PHP-FPM and reloads the profile.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
NoNewPrivileges blocks the transition from init_t to haproxy_t (avc
nnp_transition), which the RHEL policy allows for nginx and Apache but
not for haproxy; in init_t haproxy could not create its admin socket
and did not start in profile all on Rocky Linux 9 and AlmaLinux 10.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Both inherited descriptor 9, the lock of the lss run, which SELinux
denies passwd_t (avc write /run/lss.lock).

Refs #3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Comment thread tests/unit/web.bats
printf '%s\n' "${TEST_GROUPS}"
return
}
TEST_GROUPS=www-data
VectoDE and others added 5 commits October 15, 2026 13:56
After haproxy, postgresql failed the same way in profile all on Rocky
Linux 9 and AlmaLinux 10: NoNewPrivileges blocks the change from
init_t into the service's domain (avc nnp_transition) wherever the
policy does not allow it, and in init_t the service may not read its
own files. Under SELinux every lss sandbox now leaves NoNewPrivileges
off, as the scanner sandbox already did; the domain confines the
service more than init_t would. The haproxy-only exception goes away.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
On Ubuntu 26.04 the second install of profile all added the JSON
settings that the first had left out: ldd depends on the dynamic linker
finding every library, and the two runs disagreed. The library names an
engine needs are strings in the file; for nginx the engine is the
self-built libmodsecurity, which links YAJL, not the connector.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Ubuntu 24.04's profile knows only the sockets below /run/php, so
PHP-FPM could not create /run/php-fpm/lss.sock.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…main

The previous change turned NoNewPrivileges off for every sandbox under
SELinux. A program without an entrypoint label of its own (bin_t, such
as a gunicorn from PyPI) stays in init_t anyway and lost the setting
for nothing. Only units whose program carries a TYPE_exec_t label, and
so enters a confined domain, now go without it.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…with a policy module

systemd implies NoNewPrivileges for User= services with the sandbox's system call
settings, so turning it off per service does not help (Rocky 9: postgresql stayed in
init_t, avc nnp_transition). A small CIL module per confined program
(lss_nnp_TYPE) lets init_t enter TYPE_t with NoNewPrivileges, so the sandbox keeps
NoNewPrivileges=yes everywhere.

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
@sonarqubecloud

Copy link
Copy Markdown

Comment thread tests/unit/system.bats
# shellcheck disable=SC2329
semodule() {
if [[ $1 == -l ]]; then
printf '%s\n' ${modules}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants