Repository navigation
Conversation
…web server Stage 6 brings the applications a small company runs itself: Nextcloud with Collabora Online, Forgejo, Uptime Kuma, Vaultwarden, Keycloak, Matomo, WordPress, n8n, MinIO (as the maintained fork Silo, since MinIO archived its community edition) and Self-Updater. The apps share lib/apps.sh so that every stack gets the same guarantees without each module repeating them: images pinned by digest, no capabilities, no new privileges, read-only roots where the image allows, memory and process limits, ports on 127.0.0.1 only and an HTTPS site of the installed web server in front. Secrets are copied from /etc/lss/secrets as read-only files for the container user (mapped through Docker's user namespace), so no password lands in a Compose file, an environment variable or a command line; first administrators get generated passwords that lss only points to. mod_verify uses each app (WebDAV round trip, document conversion, API create/delete, tracking and reporting, S3 bucket and object) instead of only checking that a port answers. MinIO and Self-Updater also run natively, so they are covered on arm64 in the container jobs; the Docker stacks run in new apps VM jobs on all seven systems. The office and devops profiles gain the apps they were meant to have. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
An optional edge proxy (Traefik, #27) may later own ports 80 and 443 and route to the apps itself. Every app now becomes reachable through web_proxy_route/web_proxy_route_remove in lib/web.sh, which today writes the reverse-proxy site of the web server, so that switch touches one place. Also fixes found while reviewing the apps against their sources: Uptime Kuma's deleteMonitor takes three arguments, Vaultwarden's user deletion needs a JSON content type, and Matomo waits for a static file instead of the tracker, which answers differently before the installation. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The coverage run uses a kcov image without jq, where the mapping of container users silently fell back to no remapping. The docker module writes "userns-remap" on a line of its own, so sed reads it everywhere. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
… start Three causes of the failing app jobs in CI: - Caddy answered 404 for every dot path except /.well-known/ at the root, so Keycloak's /realms/master/.well-known/openid-configuration never got through. nginx and Apache already let .well-known through at any depth; Caddy now does the same, and other dot files stay hidden. - Without the tls module, every route used a copy of the default certificate, which only names the host. Nextcloud checks the host name when it calls Collabora, so trusting that certificate could not work. A route now gets a self-signed placeholder for its own names (renewed when the names change or expiry is near; a certificate from the tls module is never replaced), and Nextcloud trusts the certificate of the collabora route. - Uptime Kuma registers its Socket.IO handlers only after answering the connection, so the first question could get lost. The script now asks again until it gets an answer. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…cate helper The Matomo and WordPress installers loaded the administrator password into a variable they never used. curl reads the password from the secret file, so the installers now only make sure the secret exists. The helper for self-signed certificates names its subject in a local variable instead of using $1 directly. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
web_proxy_route now passes the host names of a route to its placeholder certificate, and the self-signed helper names the subject in its error message. Two tests still expected the old call and the old message. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…ora, cover arm64 The restic backup left the app stacks out: their data sits in Docker volumes, and a live copy of a database's files may not load again. Every app now registers what lss-app-backup saves of it before each restic run: - PostgreSQL and MariaDB are dumped inside their container; - the SQLite apps (Forgejo, Vaultwarden, Uptime Kuma) are copied while the app is paused for a moment; - file volumes and native data directories go to restic as they are. lss verify runs this step for each app, so a dump that breaks shows up before the night's backup. Collabora no longer turns off the certificate check without the tls module. It loads the host's CAs and the trust anchor of the Nextcloud route (the self-signed route certificate, or the private ACME CA) as its storage CA file, and verify asks Collabora's own WOPI access check whether it reaches Nextcloud with a valid certificate. When Nextcloud is installed after Collabora, it hands its certificate over and starts Collabora again. The arm64 runners have no KVM, so the VM jobs cannot cover the apps there. A new job runs the three app sets on the arm64 runner itself (Ubuntu 24.04), after removing the runner's own Docker. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
… S3 body hash Three failures from the CI run of 65b906b: - Matomo: the console writes a setting as a quoted string ("0"), so the check for the plain value never matched, and every install set enable_browser_archiving_triggering again. The check now accepts both forms. - Vaultwarden on Rocky 9 and AlmaLinux 10: the init service could not chown the data volume. Each container of a stack gets its own SELinux MCS categories, and the volume carried the app's. init and the app now mount the shared volume with ":z" (shared label); this also affects MinIO in Docker mode. - MinIO (native, Debian 12): storing an object answered 403. Older curl sends no x-amz-content-sha256 with --aws-sigv4, and the server then checks against the hash of an empty body. verify sends the hash of the body explicitly and puts the S3 error code in the message. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Docker Hub answered one pull of the arm64 apps job with HTTP 500, and the n8n installation was rolled back over that single answer. ctr_compose_up now pulls the missing images first, three attempts 15 s apart, and only then starts the stack, so a passing registry hiccup no longer undoes an installation while a registry that stays down still fails loudly. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
SonarCloud asks for positional parameters to be bound to a named local (shelldre:S7679); the other helpers of the module already do so. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…t the test cookie A fresh site has plain permalinks, so /wp-json/ never reaches the REST API and the check failed on every system. index.php?rest_route=/ works with every permalink setting. The login no longer posts testcookie=1: curl cannot send the jar and a fixed cookie together on every version, and without the field WordPress skips that browser check. The start check waits for a stylesheet instead of a PNG, which bash read with a null-byte warning. The REST error now names the status and address. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Pebble serves its API with the minica certificate but signs issued certificates with a root it creates at start. Nextcloud and Collabora check each other's certificate against LSS_TLS_CA_BUNDLE, so with only minica in it the office profile failed with "unable to get local issuer certificate". The bundle now holds minica and every issuing root, as a step-ca root would cover both in a real private ACME setup. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…er SELinux On AlmaLinux 10 with SELinux enforcing the profile office hung in mailserver: the sandbox sets NoNewPrivileges, and SELinux then denies postfix_master_t the change into postfix_qmgr_t, postfix_pickup_t and postfix_smtpd_t (avc: nnp_transition), so no daemon ever started. Dovecot changes into dovecot_auth_t the same way. The mail tests run in containers without SELinux, so this never showed there. svc_spawner_directives turns NoNewPrivileges off for such masters under SELinux only, as svc_scanner_sandbox already does for the scanners: the masters run as root and drop their rights themselves, and the policy confines every worker. The rest of the sandbox stays. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The conversion check of the profile all failed with a bare HTTP 403, which Collabora, nginx and ModSecurity can all answer. The error now carries the start of the answer and, with the modsecurity module, the rule IDs and messages of the latest audit entries (app_waf_hits), so the next failure points at its cause instead of needing another CI round. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The second install of the office profile on AlmaLinux 10 rewrote lss-nextcloud-cron.service: command -v docker answered /bin/docker in one run and /usr/bin/docker in the next, because /bin links to /usr/bin and the PATH differed. The units now take the resolved path, as aide, rkhunter and clamav already do, so a second run reports unchanged. The Self-Updater unit resolves its directory the same way. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…load The profile all failed the Collabora PDF conversion with 403; the new diagnostics named rule 200004, "possible unmatched boundary". ModSecurity 3 sets MULTIPART_UNMATCHED_BOUNDARY to 2 for every well-formed body that ends with its final boundary and to 1 only for a suspicious one, and its own modsecurity.conf-recommended therefore tests "@eq 1". lss used the "!@eq 0" of ModSecurity 2, so nginx turned away every multipart upload: file uploads, forms with files and the conversion API alike. nginx now gets "@eq 1"; Apache runs ModSecurity 2, which only ever sets 1, and keeps the strict test. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…profiles The figures come from the RESOURCES lines of the green apps, profile and container jobs of runs 38840181620, 38849074335 and 38863689930: the largest value across the seven systems (Rocky 9 outliers aside, as for the other tables), rounded up, with the dnf peak shown apart where the RHEL family needs clearly more. The note says where the apps are measured and what "while running" means for a stack. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
In the profile all, behind nginx with ModSecurity, the Matomo installer stopped after two seconds with "see above" and nothing above: a GET step had failed inside curl, whose error -s hides. Each step now logs its curl exit status and error text, the failure adds the latest ModSecurity rule hits, and a step may take five minutes, since creating the tables on a server with sixty modules can outlast the 30 s of app_get. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The containers jobs with firewall and k3s fail now and then on the
second install with "ERROR: initcaps" (debian-13 and ubuntu-24.04, runs
38826334577 and 38874654698): before each change ufw tests iptables with
a chain of its own, and k3s rewriting its chains at the same moment makes
that test fail ("CHAIN_DEL failed: No such file or directory"). A ufw
function now repeats such a command up to three times, two seconds
apart; every other failure and all output pass through unchanged. The
function hides the binary from command -v, so the checks for an
installed ufw use type -P.
Refs #8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…53110 off
With the diagnostics from the last run the profile all named its cause:
the system check page of the Matomo installer contains PHP code, CRS rule
953110 ("PHP source code leakage") raised the outbound score over the
threshold, and ModSecurity cut the HTTP/2 answer off (curl 92).
web_waf_exclude NAME "RULE_ID..." DOMAIN... records, per route, which
CRS rules ModSecurity skips for the route's host names. The record is
data in /etc/lss/waf, so it applies whether the modsecurity module comes
before or after the app: the module renders the records into sites.conf
(host-matched phase-1 rules with ctl:ruleRemoveById) and includes it
before the CRS rules, and a new record rewrites that file and reloads
the web server. Removing the route drops its record. Matomo excludes
953110 for its own host names only; every other rule stays in force.
Refs #8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The fully green run 38880612325 measured some apps a little above the figures from the earlier runs (Forgejo and Matomo peaks on the RHEL family, WordPress and Forgejo times on Rocky 9). The table now takes the largest value of all four green runs (38840181620, 38849074335, 38863689930, 38880612325), so it holds for every measurement so far. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The Postfix master and Dovecot start their workers in SELinux domains of their own. Instead of turning NoNewPrivileges off for them, a policy module (lss_nnp_postfix, lss_nnp_dovecot) allows exactly the domain changes the policy defines for the programs of their daemon directories (selinuxexeccon). It is loaded only when missing or changed and removed again with the module. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The arm64 runners have no KVM. Docker runs inside the privileged systemd test containers, its storage on volumes, the netfilter modules loaded on the runner. The docker module expects AppArmor for its containers only where securityfs shows it. The office profile also runs on Rocky Linux 9. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The policy module of the Dovecot workers was named like the one that lets systemd start dovecot_t (lss_nnp_dovecot) and replaced it; Dovecot then ran unconfined and Postfix could not reach its sockets. The modules of svc_selinux_spawner are lss_spawn_NAME now. On the RHEL family postfix.service also runs restorecon and newaliases (sendmail) from init_t before the start; lss_spawn_postfix_start allows their domains. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
profile all took 2386 s of the 2400 s limit in the last green run and ran over it in the next one. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The second run of the office profile sorted the rules of lss_spawn_postfix in another order and rewrote the module. The sort uses the C locale now; the stub of a unit test has its default case. Refs #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Stage 6 of the lss 2.0 rebuild: the app modules.
lib/apps.sh: shared code for app stacks. It covers the Compose project per module and secrets as read-only files for the (userns-mapped) container user. Each app gets an HTTPS site of the installed web server. It also adds PostgreSQL/MariaDB services on an internal network, timers, a functional-check helper and removal.modules/apps/):mod_verifyexercises each app: WebDAV round trip, document conversion, API create/delete, tracking/reporting, S3 bucket/object.officeanddevopsgain their apps;alllists every app.appsVM job (7 systems × 3 sets: nginx, apache and caddy+tls as base).apps(self-updater, minio native) on x64 and arm64.tests/unit/apps.bats.Resource figures in the README are filled in from a green run.
Refs #8
🤖 Generated with Claude Code