Skip to content

Add cross-OS install/uninstall CI + fix 2 SUSE bugs found along the way - #40

Merged
jsokol merged 11 commits into
mainfrom
ci/install-uninstall-test-matrix
Sep 20, 2026
Merged

jsokol merged 11 commits into
mainfrom
ci/install-uninstall-test-matrix

Conversation

@jsokol

@jsokol jsokol commented Sep 19, 2026

Copy link
Copy Markdown
Member

Summary

Builds on #39 (this branch includes those commits — merge #39 first, or this PR's diff will show both until then).

  • CI: adds .github/workflows/install-test.yml, which on every push/PR builds a Docker image per supported OS, runs the real simplerisk-setup.sh --yes, verifies the install, runs --uninstall, and verifies that too. Matrix: ubuntu-22.04, ubuntu-24.04, debian-13, centos-stream-9, centos-stream-10, opensuse-leap-15.
  • Ports and extends test infrastructure that previously lived on the abandoned feature/uninstall-support branch (never merged — simplerisk-setup.sh has diverged ~700 lines since).
  • Updates tests/verify-install.sh/verify-uninstall.sh's cron checks: they previously asserted the cron job lives in root's crontab (the pre-fix, vulnerable behavior from Run backup cron as web-server account instead of root #39 / HackerOne #3761952). Now they assert it's in /etc/cron.d/simplerisk running as the web-server account.
  • Adds openSUSE/SLES coverage, previously missing entirely. Real SLES needs a live SUSE Customer Center subscription that a public CI container can't have (validate_os_and_version() calls suseconnect --list-extensions), so tests/run-suse-function-test.sh sources the script and calls setup_suse()/uninstall_suse() directly against openSUSE Leap 15.6 — exercising the real apache2/mysql/php8/cron install logic without going through the subscription-gated entry point.
  • Drops Debian 12 from the matrix: current validate_os_and_version() only accepts Debian 13.
  • Two real simplerisk-setup.sh bugs found and fixed while validating this against openSUSE Leap in Docker (separate commit, 47d7a29):
    • setup_suse(): SUSE's mysql-community-server package ships /etc/my.cnf with no !includedir /etc/my.cnf.d, so the sql_mode drop-in written there was silently never read and STRICT_TRANS_TABLES stayed live. Added a SET GLOBAL safety net, matching how the CentOS/RHEL path already handles this exact MySQL 8.4+ behavior.
    • uninstall_suse(): zypper -n autoremove isn't a valid zypper subcommand (unlike apt-get/dnf), so it always failed with "Unknown command" and aborted the rest of the function under set -e — before the MySQL repo, firewall rules, and password file were ever removed. Removed the call (zypper has no built-in equivalent).

Test plan

  • bash -n simplerisk-setup.sh — syntax check passes
  • Full local Docker run, install → verify → uninstall → verify, against current main + Run backup cron as web-server account instead of root #39's fix:
    • ubuntu-22.04 (the "normal" setup()-entry-point path used by 5 of 6 matrix entries): 27/27 install checks, 14/14 uninstall checks.
    • opensuse-leap-15 (the new function-level path): 27/27 install checks, 13/13 uninstall checks, after fixing the two bugs above plus two test-script-only issues (wrong binary name httpd2 vs actual httpd-prefork; a pgrep -f self-matching-its-own-invocation false positive).
  • ubuntu-24.04, debian-13, centos-stream-9, centos-stream-10 reuse Dockerfiles/shims already proven working on the prior (abandoned) branch, but haven't been re-run locally against current main in this session — first real run will be this PR's own CI once it's live on GitHub Actions.

jsokol and others added 5 commits September 19, 2026 13:40
The installer chowns /var/www/simplerisk (including cron/cron.php) to
the web-server account, then registered the backup cron in root's
crontab. Any attacker with write access as that web account could
overwrite cron.php and get root code execution on the next minute
tick, defeating the containment the web account is supposed to
provide.

set_up_backup_cronjob() now installs a /etc/cron.d/simplerisk entry
with the web-server account in the user column instead of appending
to root's crontab, so cron.php only ever executes with the privileges
the web account already has. remove_backup_cronjob() also cleans up
any legacy root-crontab entry left by older installs.

Reported via HackerOne #3761952.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
exec_cmd_nobail appends its own `> /dev/null 2>&1` to every command
(to suppress non-debug output), which collided with the literal `>`
redirect in set_up_backup_cronjob() and truncated
/etc/cron.d/simplerisk to 0 bytes instead of writing the cron entry -
silently disabling the scheduled backup cron entirely.

Piping through tee instead avoids the collision (matching the existing
convention already used elsewhere in this script for writing files via
exec_cmd, e.g. the apt source list entries). Caught by running the
actual patched function against a real cron daemon in Docker across
all three supported OS families.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- setup_suse(): the SUSE mysql-community-server package's /etc/my.cnf has
  no `!includedir /etc/my.cnf.d`, so the sql_mode drop-in written there is
  silently never read and STRICT_TRANS_TABLES stays live. Apply the same
  setting via SET GLOBAL as a safety net, matching how the CentOS/RHEL path
  already handles this same MySQL 8.4+ behavior.
- uninstall_suse(): `zypper -n autoremove` isn't a valid zypper subcommand
  (unlike apt-get/dnf), so it always failed with "Unknown command" and
  aborted the rest of the function under set -e before the MySQL repo,
  firewall rules, and password file were ever removed. Removed the call;
  zypper has no built-in equivalent.

Found and verified via a real install/uninstall run against openSUSE Leap
15.6 in Docker while building the install-test CI workflow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ports and extends the Docker-based install/uninstall test suite that
previously lived on the abandoned feature/uninstall-support branch
(never merged; simplerisk-setup.sh has diverged substantially since).

- .github/workflows/install-test.yml: on every push/PR, builds a container
  per supported OS, runs the real simplerisk-setup.sh --yes, verifies the
  install, runs --uninstall, and verifies that too.
- tests/verify-install.sh / verify-uninstall.sh: updated the cron checks to
  assert the backup cron runs via /etc/cron.d as the web-server account
  (not root's crontab) - the old checks asserted the pre-fix, vulnerable
  behavior from HackerOne #3761952.
- Added openSUSE/SLES coverage (previously missing entirely): real SLES
  requires a live SUSE Customer Center subscription that a public CI
  container can't have (validate_os_and_version() calls `suseconnect
  --list-extensions`), so tests/run-suse-function-test.sh instead sources
  the script and calls setup_suse()/uninstall_suse() directly against
  openSUSE Leap 15.6, exercising the real apache2/mysql/php8/cron install
  logic without the subscription-gated entry point.
- Dropped Debian 12 from the matrix: current validate_os_and_version()
  only accepts Debian 13.

Matrix: ubuntu-22.04, ubuntu-24.04, debian-13, centos-stream-9,
centos-stream-10, opensuse-leap-15.

Locally verified end-to-end (install -> verify -> uninstall -> verify)
against current main plus the cron fix for ubuntu-22.04 (the "normal"
setup()-entry-point path) and opensuse-leap-15 (the new function-level
path); the remaining matrix entries reuse Dockerfiles/shims already
proven working on the prior branch and will get their first run in CI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…s CI

start_mysqld() pre-created /var/log/mysqld.log (flat), copied verbatim
from the CentOS shim, but setup_suse() passes /var/log/mysql/mysqld.log
(with subdirectory) to set_up_database() - matching the package's own
my.cnf log-error= directive. Without that directory existing (no live
systemd-tmpfiles), set_up_database's grep for the startup temp-password
line failed with "No such file or directory", aborting the rest of
setup_suse under set -e.

Passed locally against Docker Desktop because /var/log/mysql already
existed there for unrelated reasons; failed on the real GitHub Actions
runner, which is how this was caught.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@jsokol
jsokol enabled auto-merge (squash) September 19, 2026 20:36
jsokol and others added 6 commits September 19, 2026 18:15
- validate_os_and_version(): accept 26.04 (current LTS, "Resolute
  Raccoon", already released) instead of the 25.* wildcard. Interim
  (non-LTS) Ubuntu releases get ~9 months of upstream support and churn
  every 6 months; the intent is to support LTS releases specifically
  (22.04/24.04/26.04), not track every interim point release.
- uninstall_ubuntu_debian(): also purge sensible-mda alongside
  sendmail/sendmail-bin. sensible-mda is a sendmail dependency with its
  own hard Depends on the mail-transport-agent virtual package; left
  behind, apt keeps that dependency satisfied by auto-installing a
  replacement MTA (courier-mta, pulling in ~40 packages including a full
  C build toolchain) instead of just removing it. Reproduced against a
  clean Ubuntu 26.04 install with nothing but sendmail/sendmail-bin
  present - not specific to 26.04, but only surfaced once 26.04's test
  container (no live systemd) turned an apt side-effect into a hard
  uninstall failure via a separate, unrelated postinst issue.

Verified end-to-end (install -> verify -> uninstall -> verify) against
Ubuntu 26.04 in Docker, 28/28 and 14/14 checks passing, zero courier-mta
packages touched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- tests/dockerfiles/Dockerfile.ubuntu-26.04 + mysql-init-ubuntu-26.04.sh:
  Ubuntu 26.04's mysql-server package ships no /etc/init.d/mysql (systemd
  units only). Verified via /usr/sbin/service's own source
  (`[ -d /run/systemd/system ]` gates delegation to systemctl) that a real,
  systemd-booted 26.04 server handles `service mysql start` fine via its
  live mysql.service unit - this is purely a no-init-system container gap,
  not a simplerisk-setup.sh issue, so the fix is test-only (same category
  as the existing Debian 12/13 shim).
- tests/verify-install.sh: fixed the HTTP check added in the prior commit.
  It grepped for the login form (name="authenticate"), but a genuinely
  fresh database has zero users, so index.php's own gate
  (`if ($count == 0) { create_default_admin_account(); exit(); }`) shows
  the "Default Admin Account Creation" wizard first and never reaches the
  login form - that page, not the login screen, is what a truly fresh
  install is supposed to show. Now checks for that page instead
  (verify_create_default_admin_account, its submit button's literal name).
- .github/workflows/install-test.yml: added ubuntu-26.04 to the matrix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SimpleRisk's current release requires PHP >= 8.3 (Composer platform
check). Debian and CentOS/RHEL already pin a modern PHP via a
third-party repo (Sury / Remi), but Ubuntu just ran `apt-get install
lamp-server^` and took whatever PHP version Ubuntu's own archive
defaults to for that release. Ubuntu 24.04 (8.3.6) and 26.04 (8.5)
happen to be fine; Ubuntu 22.04 only offers 8.1 - every fresh install
completed "successfully" per the script's own output, but silently
served a PHP fatal error (Composer's platform_check.php) instead of
the app.

Fix: for Ubuntu, still install via lamp-server^ (unchanged - keeps
Ubuntu's own MySQL/MariaDB provisioning as-is, only the PHP version was
the problem), but also add the same Sury PHP8 repo Debian uses, install
the pinned 8.5 packages, and switch Apache's active PHP module to it
(scanning /etc/apache2/mods-enabled/php*.load rather than assuming a
specific old version, since it varies by release). Also extended
uninstall_ubuntu_debian's repo cleanup to remove the Sury repo/key for
Ubuntu too, matching install.

This was previously invisible because verify-install.sh's HTTP check
never followed the http->https redirect, so it only ever saw the
redirect's 301/302 status and never reached the page where the fatal
error actually occurs.

Verified end-to-end (install -> verify -> uninstall -> verify) on all
three supported Ubuntu versions in Docker: 22.04 (28/28, 14/14 - PHP
correctly pinned to 8.5.10 via Sury), 24.04 (28/28, 14/14), and 26.04
(28/28, 14/14 - confirmed no conflict between the newly-added Sury repo
and the native php8.5 package it already ships).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SimpleRisk's current release requires PHP >= 8.3 (Composer platform
check). SLES 15's own repositories cap out at PHP 8.2 (the php8
package) with no upgrade path: openSUSE's community
devel:languages:php OBS project, which sometimes backports a newer PHP
to older releases, has dropped 15.6 support entirely and only targets
the next major release (16.0), not yet generally available for SLES.
Shipping an install path that silently serves a PHP fatal error instead
of the app (as Ubuntu 22.04 did until the previous commit) serves no
one, so validate_os_and_version() now rejects SLES outright with a
clear explanation instead of attempting the install.

setup_suse()/uninstall_suse() are left in place (unreachable for now)
as a starting point: openSUSE Leap 16.0 already ships PHP 8.4 natively,
so SLES 16 (once released) should be a viable target, but those
functions are written entirely around SLES 15's package names, module
structure, and MySQL repo RPM naming
(mysql84-community-release-sl15) - supporting SLES 16 needs its own
dedicated pass, verified against a real SLES 16/Leap 16.0 environment,
not just relaxing this version check.

Also removes the SLES/openSUSE CI matrix entry and its
run-suse-function-test.sh workaround (it bypassed the now-intentional
rejection above to exercise setup_suse() directly) and updates the
required status checks on main's branch protection to match the
current matrix (ubuntu-22.04/24.04/26.04, debian-13,
centos-stream-9/10).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Supported versions: 22.04/24.04/26.04 LTS only (matches
  validate_os_and_version() as of this branch - Ubuntu interim releases
  and SLES 15 are no longer accepted), with a brief note on why SLES
  isn't currently supported.
- Fixed the install one-liners pointing at a 'master' branch that no
  longer exists (the repo's default branch is 'main'; raw.githubusercontent.com
  happens to still resolve the old ref today, but that's not something
  to document as if intentional).
- Added a CI status badge for the new install-test workflow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Doc-only commits (README, etc.) were triggering the full ~25-minute
matrix for no reason. Scoped both push and pull_request triggers to
simplerisk-setup.sh, tests/**, and the workflow file itself (shared via
a YAML anchor so the two trigger blocks can't drift out of sync).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@jsokol
jsokol merged commit 677dfe7 into main Sep 20, 2026
6 checks passed
@jsokol
jsokol deleted the ci/install-uninstall-test-matrix branch September 20, 2026 02:47
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.

1 participant