diff --git a/README.rst b/README.rst index 1e0f8d2..06740ad 100644 --- a/README.rst +++ b/README.rst @@ -13,7 +13,7 @@ and on top of that: - Mantis configurations: - Installed from upstream source code to /var/www/mantis - - Includes graphing support, Twitter integration and documentation. + - Includes the upstream administration documentation. **Security note**: Updates to Mantis may require supervision so they **ARE NOT** configured to install automatically. See `Mantis @@ -26,8 +26,8 @@ and on top of that: password recovery). - Webmin modules for configuring Apache2, PHP, MySQL and Postfix. -- Configure system email addresses: */etc/mantis/config\_inc.php* -- Integrate Mantis with twitter: */etc/mantis/config\_inc.php* +- Configure Mantis settings, including system email addresses: + */etc/mantis/config\_inc.php* Credentials *(passwords set at first boot)* ------------------------------------------- @@ -39,5 +39,5 @@ Credentials *(passwords set at first boot)* .. _MantisBT: https://www.mantisbt.org .. _TurnKey Core: https://www.turnkeylinux.org/core -.. _Mantis documentation: https://www.mantisbt.org/docs/master/en-US/Admin_Guide/html/admin.install.upgrade.html +.. _Mantis documentation: https://github.com/mantisbt/mantisbt#upgrading .. _Adminer: https://www.adminer.org/ diff --git a/changelog b/changelog index 4ca307b..f714710 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,16 @@ +turnkey-mantis-19.0 (1) turnkey; urgency=low + + * Update to the supported upstream MantisBT 2.28.4 release. + + * Verify the upstream release archive with its published SHA-256 digest. + + * Support Debian 13/Trixie, including PHP 8.4 and MariaDB 11.8. + + * Preserve dynamic deployment URLs and expose the active Mantis + configuration at /etc/mantis/config_inc.php. + + -- TurnKey Linux Developers Tue, 25 Aug 2026 09:30:00 +0000 + turnkey-mantis-18.0 (1) turnkey; urgency=low * Update to latest upstream source version: 2.25.8 diff --git a/conf.d/downloads b/conf.d/downloads index 3071140..6fca9ec 100755 --- a/conf.d/downloads +++ b/conf.d/downloads @@ -1,13 +1,17 @@ -#!/bin/bash -ex +#!/bin/bash +set -Eeuxo pipefail -dl() { - [ "$FAB_HTTP_PROXY" ] && PROXY="--proxy $FAB_HTTP_PROXY" - cd $2; curl -L -f -O $PROXY $1; cd - -} +VERSION=2.28.4 +ARCHIVE="mantisbt-$VERSION.tar.gz" +URL="https://downloads.sourceforge.net/project/mantisbt/mantis-stable/$VERSION/$ARCHIVE" +SHA256=a400bd2957154baad3412130031e893d5c18cf1ed1cfb38b77ee64afd1ed2a33 -VERSION=2.25.8 -SRC="/usr/local/src" -URL="https://downloads.sourceforge.net/project/mantisbt/mantis-stable/$VERSION/mantisbt-$VERSION.tar.gz" - -dl $URL $SRC +proxy=() +if [[ -n ${FAB_HTTP_PROXY:-} ]]; then + proxy=(--proxy "$FAB_HTTP_PROXY") +fi +curl --fail --location "${proxy[@]}" "$URL" \ + --output "/usr/local/src/$ARCHIVE" +printf '%s %s\n' "$SHA256" "/usr/local/src/$ARCHIVE" | + sha256sum --check --strict - diff --git a/conf.d/main b/conf.d/main index 80718f5..b7e695a 100755 --- a/conf.d/main +++ b/conf.d/main @@ -1,70 +1,75 @@ -#!/bin/bash -ex +#!/bin/bash +set -Eeuxo pipefail DB_NAME=mantis DB_USER=mantis DB_PASS=$(mcookie) ADMIN_NAME=admin -ADMIN_MAIL=admin@example.com -ADMIN_PASS=turnkey -WEBROOT=/var/www/mantis SRC=/usr/local/src +WEBROOT=/var/www/mantis +VERSION=2.28.4 +ARCHIVE="$SRC/mantisbt-$VERSION.tar.gz" +CONF="$WEBROOT/config/config_inc.php" # unpack -tar xf $SRC/mantisbt-*.tar.gz --no-same-owner -C $(dirname $WEBROOT) -mv $(dirname $WEBROOT)/mantisbt-* $WEBROOT -rm $SRC/mantisbt-*.tar.gz +tar xf "$ARCHIVE" --no-same-owner -C "$(dirname "$WEBROOT")" +mv "$(dirname "$WEBROOT")/mantisbt-$VERSION" "$WEBROOT" +rm "$ARCHIVE" +grep -Fq "define( 'MANTIS_VERSION', '$VERSION' );" \ + "$WEBROOT/core/constant_inc.php" a2dissite 000-default a2ensite mantis -chown www-data:www-data $WEBROOT -chown www-data:www-data $WEBROOT/admin -chown -R www-data:www-data $WEBROOT/config +chown -R root:root "$WEBROOT" +chmod 0755 "$WEBROOT" +chown -R www-data:www-data "$WEBROOT/config" -service mysql start +service mariadb start service apache2 start # create DB - -MYSQL_BATCH="mysql --user=root --password=$MYSQL_PASS --batch" -MYSQL_ADMIN="mysqladmin --user=root --password=$MYSQL_PASS" - -$MYSQL_ADMIN create $DB_NAME -$MYSQL_BATCH --execute "grant all privileges on $DB_NAME.* to $DB_USER@localhost identified by '$DB_PASS'; flush privileges;" +mariadb --batch --execute "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS'; GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" # complete installation - URL="http://127.0.0.1/admin/install.php" -CURL="curl -c /tmp/cookie -b /tmp/cookie" -$CURL $URL --data "db_type=mysqli&hostname=localhost&db_username=$DB_USER&db_password=$DB_PASS&database_name=$DB_NAME&admin_username=&admin_password=&db_table_prefix=mantis&db_table_plugin_prefix=plugin&db_table_suffix=table&timezone=UTC&install=2&go=Install%2FUpgrade+Database" +curl --fail --silent --show-error \ + --cookie-jar /tmp/mantis-cookie --cookie /tmp/mantis-cookie \ + --data-urlencode db_type=mysqli \ + --data-urlencode hostname=localhost \ + --data-urlencode "db_username=$DB_USER" \ + --data-urlencode "db_password=$DB_PASS" \ + --data-urlencode "database_name=$DB_NAME" \ + --data-urlencode admin_username= \ + --data-urlencode admin_password= \ + --data-urlencode db_table_prefix=mantis \ + --data-urlencode db_table_plugin_prefix=plugin \ + --data-urlencode db_table_suffix=table \ + --data-urlencode timezone=UTC \ + --data-urlencode install=2 \ + --data-urlencode 'go=Install/Upgrade Database' \ + "$URL" > /tmp/mantis-install-result +grep -Fq 'MantisBT was installed successfully.' /tmp/mantis-install-result +test -f "$CONF" + +# Let Mantis derive its public URL from each request rather than preserving the +# build-only loopback URL written by the web installer. +sed -i '/^\$g_path[[:space:]]*=/d' "$CONF" # change administrator name to admin -mysql --defaults-extra-file=/etc/mysql/debian.cnf <> $WEBROOT/config/config_inc.php - -// support graphing -$g_use_jpgraph = true; -$g_jpgraph_path = "/usr/share/jpgraph/"; - -// integrate mantis with twitter (disabled if user is empty) -$g_twitter_username = ''; -$g_twitter_password = ''; -EOF - -service mysql stop +service mariadb stop service apache2 stop -rm -f /tmp/cookie -rm -rf $WEBROOT/admin +rm -f /tmp/mantis-cookie /tmp/mantis-install-result +rm -rf "$WEBROOT/admin" mkdir -p /etc/mantis -ln -s $WEBROOT/config_inc.php /etc/mantis/config_inc.php -chown -R root:root $WEBROOT/config - +ln -s "$CONF" /etc/mantis/config_inc.php +chown -R root:root "$WEBROOT/config" diff --git a/docs/v19.0-testing.md b/docs/v19.0-testing.md new file mode 100644 index 0000000..d16883d --- /dev/null +++ b/docs/v19.0-testing.md @@ -0,0 +1,66 @@ +# Mantis 19.0 testing + +## Scope and source decision + +Debian 13 Trixie does not package the MantisBT server. The appliance installs +the official supported MantisBT 2.28.4 release archive from the upstream +SourceForge project and verifies its published SHA-256 digest +`a400bd2957154baad3412130031e893d5c18cf1ed1cfb38b77ee64afd1ed2a33`. +Upstream supports only the latest stable 2.x release, so this appliance tracks +that maintained line. PHP 8.4, MariaDB, Apache, Postfix, Adminer and Webmin come +from the signed Debian Trixie and TurnKey repositories. + +## Acceptance command + +```sh +/sandboxed-git/turnkey/tools/test-v19-appliance mantis \ + --source /home/agent/.local/worktrees/turnkey-apps/mantis/wish-mantis-v19-trixie +``` + +## README crosswalk + +| README contract | Focused check | Required result | Evidence | +| --- | --- | --- | --- | +| MantisBT provides a web bug tracker | Authenticate as the firstboot administrator, create a project and category, then create and read an issue through the real HTTPS forms | The administrator session works and the issue summary and description are returned by MantisBT | `tests/v19.sh` | +| MariaDB stores MantisBT state | Read the web-created issue directly from the issue and issue-text tables, restart MariaDB and Apache, then read it again through MantisBT | The same issue survives the service restart | `tests/v19.sh` | +| SSL works out of the box | Run the complete login and issue flow through Apache HTTPS | Every application request succeeds over TLS | `tests/v19.sh` | +| The active MantisBT configuration is available at `/etc/mantis/config_inc.php` | Resolve the convenience link and use the configured firstboot administrator email | The link targets the live application configuration and the email matches firstboot input | `tests/v19.sh` | +| Postfix supports application email | Inspect service state and the SMTP listener | Postfix is active and bound to loopback | `tests/v19.sh` | +| Adminer manages MariaDB on port 12322 | Open Adminer over HTTPS and authenticate to the Mantis database | Adminer displays the Mantis database with the firstboot credential | `tests/v19.sh` | +| Webmin manages Apache, PHP, MariaDB and Postfix | Require the documented modules and request Webmin HTTPS | The modules are installed and Webmin responds on port 12321 | `tests/v19.sh` | +| MantisBT and Debian components retain maintained update paths | Query the official stable release channel, refresh APT metadata and inspect package candidates | The upstream stable release is discoverable, eligible Trixie candidates remain and the check does not mutate application files | `tests/v19.sh` | +| Root SSH, backup and other standard administration are inherited from Core | Cite the unchanged Core layer | Core 19 baseline passes | Core run `20260824t010251z-1634-32241`, source `24c82ee3540ce545422742b0e28ba6b687c53ec2`, verdict `PASS` | + +## Supervised MantisBT updates + +Back up the appliance and database before updating. Download the next stable +archive from the official MantisBT SourceForge project and verify its published +digest. Extract it into a clean directory, copy +`/var/www/mantis/config/config_inc.php` and any local custom files into that +directory, then temporarily expose the new `admin` directory through Apache. +Open `admin/check/index.php`, run `admin/install.php` to apply required schema +updates, remove the `admin` directory, and restart Apache. Follow the upstream +upgrade notes when crossing a release family. + +Debian components continue to receive normal `apt-get update` and +`apt-get upgrade` maintenance from the signed Trixie and TurnKey repositories. + +## Economical preflight evidence + +A disposable Debian Trixie container installed MantisBT 2.28.4 on PHP 8.4 and +MariaDB 11.8 using the official web installer. It authenticated the configured +administrator through MantisBT's two-step login, created a project and +category, and completed an issue create and read round trip. A direct MariaDB +query confirmed the persisted issue. This preflight also verified the upstream +release digest and current stable-release discovery endpoint. + +## Known limitation and deferred issues + +- Docker runtime validation does not exercise the installer, appliance kernel, + bootloader or physical hardware. Mantis adds no appliance-specific behavior + at those boundaries, so the accepted Core 19 baseline supplies inherited + evidence. +- Application upgrades remain supervised because they can include database + schema changes and local configuration migration. The acceptance check + validates the official discovery channel without changing the installed + application. diff --git a/tests/v19.sh b/tests/v19.sh new file mode 100755 index 0000000..06204ad --- /dev/null +++ b/tests/v19.sh @@ -0,0 +1,222 @@ +#!/bin/bash +set -Eeuo pipefail +umask 077 + +result=${TKL_TEST_RESULT:?TKL_TEST_RESULT is required} +app_password=${TKL_TEST_APP_PASS:?TKL_TEST_APP_PASS is required} +db_password=${TKL_TEST_DB_PASS:?TKL_TEST_DB_PASS is required} +base=https://localhost +admin_email=admin@example.invalid +cookies=/tmp/tkl-mantis-cookies.$$ +adminer_cookies=/tmp/tkl-mantis-adminer-cookies.$$ +page=/tmp/tkl-mantis-page.$$ +headers=/tmp/tkl-mantis-headers.$$ +upstream=/tmp/tkl-mantis-upstream.$$ +policy=/tmp/tkl-mantis-policy.$$ + +cleanup() { + rm -f -- "$cookies" "$adminer_cookies" "$page" "$headers" \ + "$upstream" "$policy" +} +trap cleanup EXIT +trap 'printf "test_failure line=%s status=%s command=%q\n" "$LINENO" "$?" "$BASH_COMMAND" >&2' ERR + +token_value() { + local name=$1 + local file=$2 + + sed -n "s/.*name=\"$name\" value=\"\([^\"]*\)\".*/\1/p" "$file" | + head -n1 +} + +systemctl --quiet is-active apache2.service mariadb.service postfix.service \ + multi-user.target +systemctl --quiet is-enabled apache2.service mariadb.service postfix.service +apache2ctl -t +grep -Fxq 'VERSION_CODENAME=trixie' /etc/os-release +grep -Eq '^turnkey-mantis-19\.0' /etc/turnkey_version +grep -Fq '[40mantis] successfully completed' /var/log/inithooks.log + +installed_version=$(php -r 'require $argv[1]; echo MANTIS_VERSION;' \ + /var/www/mantis/core/constant_inc.php) +test "$installed_version" = 2.28.4 +php_version=$(php --version | head -n1) +[[ $php_version == 'PHP 8.4.'* ]] +for module in curl mbstring mysqli; do + php -m | grep -Fxiq "$module" +done +test ! -e /var/www/mantis/admin +test "$(readlink -f /etc/mantis/config_inc.php)" = \ + /var/www/mantis/config/config_inc.php +test "$(mariadb --batch --skip-column-names mantis --execute \ + "SELECT email FROM mantis_user_table WHERE username='admin'")" = \ + "$admin_email" + +# Authenticate through MantisBT's real two-step administrator login. +curl --insecure --fail --silent --show-error \ + --cookie-jar "$cookies" --cookie "$cookies" \ + "$base/login_page.php" >"$page" +login_token=$(token_value login_token "$page") +test -n "$login_token" +curl --insecure --fail --silent --show-error \ + --cookie-jar "$cookies" --cookie "$cookies" \ + --data-urlencode "login_token=$login_token" \ + --data-urlencode username=admin \ + --data-urlencode return=index.php \ + "$base/login_password_page.php" >"$page" +login_token=$(token_value login_token "$page") +test -n "$login_token" +curl --insecure --silent --show-error \ + --cookie-jar "$cookies" --cookie "$cookies" \ + --data-urlencode "login_token=$login_token" \ + --data-urlencode username=admin \ + --data-urlencode "password=$app_password" \ + --data-urlencode return=index.php \ + --data-urlencode secure_session=1 \ + --dump-header "$headers" --output "$page" "$base/login.php" +grep -q '^HTTP/.* 302' "$headers" +grep -Eqi '^Location: https://localhost/login_cookie_test\.php' "$headers" +curl --insecure --fail --silent --show-error \ + --cookie-jar "$cookies" --cookie "$cookies" \ + "$base/my_view_page.php" >"$page" +grep -Fq 'My View - MantisBT' "$page" +grep -Fq '' "$page" + +# Create a project and category through the administration UI, then exercise +# MantisBT's identity-defining issue create and read flow. +project_name="TurnKey Acceptance Project $$" +curl --insecure --fail --silent --show-error \ + --cookie-jar "$cookies" --cookie "$cookies" \ + "$base/manage_proj_create_page.php" >"$page" +project_token=$(token_value manage_proj_create_token "$page") +test -n "$project_token" +curl --insecure --silent --show-error \ + --cookie-jar "$cookies" --cookie "$cookies" \ + --data-urlencode "manage_proj_create_token=$project_token" \ + --data-urlencode "name=$project_name" \ + --data-urlencode status=10 \ + --data-urlencode view_state=10 \ + --data-urlencode 'description=TurnKey Mantis v19 acceptance' \ + --dump-header "$headers" --output "$page" \ + "$base/manage_proj_create.php" +grep -q '^HTTP/.* 302' "$headers" +project_id=$(mariadb --batch --skip-column-names mantis --execute \ + "SELECT id FROM mantis_project_table WHERE name='$project_name'") +test "$project_id" -gt 0 + +curl --insecure --fail --silent --show-error \ + --cookie-jar "$cookies" --cookie "$cookies" \ + "$base/manage_proj_edit_page.php?project_id=$project_id" >"$page" +category_token=$(token_value manage_proj_cat_add_token "$page") +test -n "$category_token" +curl --insecure --silent --show-error \ + --cookie-jar "$cookies" --cookie "$cookies" \ + --data-urlencode "manage_proj_cat_add_token=$category_token" \ + --data-urlencode "project_id=$project_id" \ + --data-urlencode name=General \ + --dump-header "$headers" --output "$page" \ + "$base/manage_proj_cat_add.php" +grep -q '^HTTP/.* 302' "$headers" +category_id=$(mariadb --batch --skip-column-names mantis --execute \ + "SELECT id FROM mantis_category_table WHERE project_id=$project_id AND name='General'") +test "$category_id" -gt 0 + +curl --insecure --fail --silent --show-error --location \ + --cookie-jar "$cookies" --cookie "$cookies" \ + "$base/set_project.php?project_id=$project_id&ref=bug_report_page.php" \ + >"$page" +issue_token=$(token_value bug_report_token "$page") +test -n "$issue_token" +summary="TurnKey v19 acceptance issue $$" +description="Mantis issue persistence round trip $$" +curl --insecure --silent --show-error \ + --cookie-jar "$cookies" --cookie "$cookies" \ + --data-urlencode "bug_report_token=$issue_token" \ + --data-urlencode m_id=0 \ + --data-urlencode "project_id=$project_id" \ + --data-urlencode "category_id=$category_id" \ + --data-urlencode reproducibility=70 \ + --data-urlencode severity=50 \ + --data-urlencode priority=30 \ + --data-urlencode handler_id=0 \ + --data-urlencode "summary=$summary" \ + --data-urlencode "description=$description" \ + --data-urlencode view_state=10 \ + --dump-header "$headers" --output "$page" "$base/bug_report.php" +grep -q '^HTTP/.* 302' "$headers" +issue_id=$(mariadb --batch --skip-column-names mantis --execute \ + "SELECT id FROM mantis_bug_table WHERE summary='$summary'") +test "$issue_id" -gt 0 +curl --insecure --fail --silent --show-error \ + --cookie-jar "$cookies" --cookie "$cookies" \ + "$base/view.php?id=$issue_id" >"$page" +grep -Fq "$summary" "$page" +grep -Fq "$description" "$page" +mariadb --batch --skip-column-names mantis --execute \ + "SELECT CONCAT(b.summary, '|', t.description) FROM mantis_bug_table b JOIN mantis_bug_text_table t ON t.id=b.bug_text_id WHERE b.id=$issue_id" | + grep -Fxq "$summary|$description" + +systemctl restart mariadb.service apache2.service +curl --insecure --fail --silent --show-error \ + --cookie-jar "$cookies" --cookie "$cookies" \ + "$base/view.php?id=$issue_id" >"$page" +grep -Fq "$summary" "$page" +grep -Fq "$description" "$page" + +test "$(postconf -h inet_interfaces)" = localhost +ss -ltn | awk '$4 ~ /^(127\.0\.0\.1|\[::1\]):25$/ { found=1 } END { exit !found }' +dpkg-query -W adminer webmin-apache webmin-mysql webmin-phpini postfix \ + apache2 mariadb-server >/dev/null +curl --insecure --fail --silent --show-error --head \ + https://127.0.0.1:12321/ >/dev/null +curl --insecure --fail --silent --show-error \ + https://127.0.0.1:12322/ >"$page" +grep -qi Adminer "$page" +curl --insecure --silent --show-error --location \ + --cookie-jar "$adminer_cookies" --cookie "$adminer_cookies" \ + --data-urlencode 'auth[driver]=server' \ + --data-urlencode 'auth[server]=localhost' \ + --data-urlencode 'auth[username]=adminer' \ + --data-urlencode "auth[password]=$db_password" \ + --data-urlencode 'auth[db]=mantis' \ + https://127.0.0.1:12322/ >"$page" +grep -qi mantis "$page" +grep -qi Logout "$page" + +# Discover the maintained upstream release without mutating the installation. +curl --fail --silent --show-error \ + https://sourceforge.net/projects/mantisbt/best_release.json >"$upstream" +latest_path=$(python3 -c \ + 'import json,sys; print(json.load(sys.stdin)["release"]["filename"])' \ + <"$upstream") +latest_version=$(python3 -c \ + 'import re,sys; print(re.search(r"mantis-stable/([^/]+)/", sys.argv[1]).group(1))' \ + "$latest_path") +test "$latest_version" = "$installed_version" + +apache_version=$(dpkg-query -W -f='${Version}' apache2) +mariadb_version=$(dpkg-query -W -f='${Version}' mariadb-server) +adminer_version=$(dpkg-query -W -f='${Version}' adminer) +before="$apache_version|$mariadb_version|$adminer_version" +apt-get update >/dev/null +for package in apache2 mariadb-server php adminer; do + apt-cache policy "$package" >"$policy" + candidate_version=$(awk '/Candidate:/ {print $2}' "$policy") + test -n "$candidate_version" + test "$candidate_version" != '(none)' + grep -Eq 'trixie|deb13' "$policy" +done +after="$(dpkg-query -W -f='${Version}' apache2)|$(dpkg-query -W -f='${Version}' mariadb-server)|$(dpkg-query -W -f='${Version}' adminer)" +test "$after" = "$before" +grep -Rqs '^Suites: trixie' /etc/apt/sources.list.d +! grep -Rqi bookworm /etc/apt/sources.list.d + +cat >"$result" <