diff --git a/README.rst b/README.rst index 6a7d6b9..ddb5d29 100644 --- a/README.rst +++ b/README.rst @@ -23,11 +23,11 @@ and on top of that: window.location = "/cp/" - - All components installed from package management. - - Using OpenJDK Java runtime. + - Tomcat 10.1 and all components installed from package management. + - Using the OpenJDK 21 Java runtime from Debian. - Deployed web applications in /var/lib/tomcat10/webapps. - TurnKey web control panel in /var/lib/tomcat10/webapps/cp. - - JSP console output sent to syslog (/var/log/syslog). + - JSP console output available through the systemd journal. - Created Tomcat admin/manager roles and admin user. - Use Apache2 Jk loadbalancer connector (performance). - JkMounts for admin, manager, host-manager applications @@ -36,16 +36,16 @@ and on top of that: - Removed Tomcat HTTP connector listener (security). - Set system wide Tomcat and Java environment variables. -- Includes MySQL. +- Includes MariaDB, a MySQL-compatible database server. - SSL support out of the box. -- Includes Webmin module for configuring Apache2 and MySQL. +- Includes Webmin modules for configuring Apache2 and MariaDB. See the `Tomcat on Apache docs`_ for further details. Credentials *(passwords set at first boot)* ------------------------------------------- -- Webmin, SSH, MySQL: username **root** +- Webmin, SSH, MariaDB: username **root** - Tomcat administration applications: username **admin** diff --git a/changelog b/changelog index d38ef6a..3c841e7 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,16 @@ +turnkey-tomcat-apache-19.0 (1) turnkey; urgency=low + + * Update the appliance for Debian 13 Trixie packages, including Tomcat + 10.1, Apache 2.4 and OpenJDK 21. + + * Preserve the localhost AJP connector, Apache mod_jk integration, firstboot + manager credentials and documented control panel deployment. + + * Add focused runtime acceptance for Apache-to-Tomcat requests, manager + authentication, JSP deployment, MariaDB, Webmin and APT updates. + + -- TurnKey Linux Developers Mon, 24 Aug 2026 00:00:00 +0000 + turnkey-tomcat-apache-18.0 (1) turnkey; urgency=low * Updated all relevant Debian packages to Bookworm/12 versions; including diff --git a/conf.d/main b/conf.d/main index 181efea..6137b2d 100755 --- a/conf.d/main +++ b/conf.d/main @@ -15,3 +15,4 @@ done sed -i "s|HOSTNAME_DESC|Tomcat Apache|g" $WEBROOT/index.jsp sed -i "s|HOSTNAME|tomcat-apache|g" $WEBROOT/index.jsp +sed -i "s|tomcat-9.0-doc|tomcat-10.1-doc|g" $WEBROOT/index.jsp diff --git a/docs/v19.0-testing.md b/docs/v19.0-testing.md new file mode 100644 index 0000000..8ede0fd --- /dev/null +++ b/docs/v19.0-testing.md @@ -0,0 +1,72 @@ +# Tomcat-Apache 19.0 testing + +## Scope and source decision + +Tomcat-Apache 19.0 uses Debian 13 Trixie packages for Tomcat 10.1, +Apache 2.4, mod_jk, OpenJDK 21 and MariaDB. Trixie supplies the complete +application stack, so no upstream repository or downloaded release artifact +is needed. Webmin modules remain maintained through the signed TurnKey Trixie +APT repository inherited from Core. + +The appliance preserves the established integration: Tomcat listens only on +the localhost AJP connector, Apache serves ports 80 and 443, and mod_jk maps +the control panel, documentation and manager applications to Tomcat. + +## Acceptance command + +```sh +/sandboxed-git/turnkey/tools/test-v19-appliance tomcat-apache \ + --source /home/agent/.local/worktrees/turnkey-apps/tomcat-apache/wish-tomcat-apache-v19-trixie +``` + +The command builds the Trixie root filesystem, imports it into Docker, boots +the normal systemd and firstboot path, then runs `tests/v19.sh`. The accepted +run identity and retained report will be recorded after exact acceptance. + +## README crosswalk + +| README contract | Focused check | Required result | Evidence | +| --- | --- | --- | --- | +| Tomcat 10.1, Apache, mod_jk and OpenJDK 21 come from package management | Query installed versions, binary ownership and APT candidates | Debian-owned binaries retain eligible signed Trixie candidates | `tests/v19.sh` | +| Apache fronts Tomcat through the localhost AJP connector | Validate both services, mod_jk, worker and site configuration, Tomcat XML and sockets, then request the control panel over Apache HTTPS | Apache serves the Tomcat-generated control panel; AJP listens on `127.0.0.1:8009`; no Tomcat HTTP connector listens on 8080 | `tests/v19.sh` | +| The control panel is deployed under `/var/lib/tomcat10/webapps/cp` | Inspect the deployed context, request it, create a temporary JSP in that documented deployment path and request it through Apache | The branded panel and live JSP response are returned through mod_jk | `tests/v19.sh` | +| Firstboot creates the `admin` manager account | Reject anonymous manager access, compare the configured password and roles, then use the HTML and text manager endpoints | Anonymous access receives 401; authenticated manager requests succeed | `tests/v19.sh` | +| MariaDB is included | Verify service supervision and execute a create, insert, select and drop roundtrip | The selected value is returned from the temporary database | `tests/v19.sh` | +| Webmin provides documented Apache and MariaDB administration | Verify both Webmin packages and request Webmin HTTPS | Both modules are installed and Webmin responds | `tests/v19.sh` | +| SSH and common system administration are inherited from Core | Cite the accepted Core 19 baseline | Core supplies the inherited root, SSH and base management behavior | Core run `20260824t010251z-1634-32241`, source `24c82ee3540ce545422742b0e28ba6b687c53ec2`, verdict `PASS` | +| The stack updates through package management | Refresh metadata and inspect candidates without changing installed versions | Signed Trixie metadata is accepted and no Bookworm source remains | `tests/v19.sh` | + +## Updater check + +`tests/v19.sh` runs `apt-get update`, confirms eligible Trixie candidates for +Tomcat, Apache, mod_jk, OpenJDK, MariaDB and the Webmin modules, and verifies +the installed runtime versions remain unchanged. Normal supervised updates +use APT, followed by service restarts when the package transaction requires +them: + +```sh +apt-get update +apt-get install --only-upgrade tomcat10 apache2 libapache2-mod-jk \ + openjdk-21-jre-headless mariadb-server webmin-apache webmin-mysql +systemctl restart tomcat10 apache2 +``` + +## Known limitations + +Docker runtime validation does not exercise the installer, kernel, bootloader +or physical hardware. Tomcat-Apache adds no appliance-specific behavior at +those boundaries, so the accepted Core 19 baseline supplies inherited +evidence. + +The practical deployment check writes a temporary JSP into the documented +control-panel context and requests it through Apache and mod_jk. It avoids an +extra VM or second application server while exercising the appliance-specific +deployment and request path. + +## Deferred issues + +- The shared Tomcat environment still exports the legacy `MaxPermSize` JVM + option. The packaged systemd service does not consume that environment + value, and normal service startup is covered by acceptance. Removing the + shared option belongs in the common Tomcat integration rather than this + appliance lane. diff --git a/plan/main b/plan/main index 13e2e65..ae5c4dd 100644 --- a/plan/main +++ b/plan/main @@ -3,8 +3,8 @@ ant -openjdk-17-jdk-headless -openjdk-17-jre-headless +openjdk-21-jdk-headless +openjdk-21-jre-headless default-mysql-server libmariadb-java diff --git a/tests/v19.sh b/tests/v19.sh new file mode 100755 index 0000000..4d3d30b --- /dev/null +++ b/tests/v19.sh @@ -0,0 +1,191 @@ +#!/bin/bash +set -Eeuo pipefail +umask 077 + +result=${TKL_TEST_RESULT:?TKL_TEST_RESULT is required} +password=${TKL_TEST_APP_PASS:?TKL_TEST_APP_PASS is required} +db_password=${TKL_TEST_DB_PASS:?TKL_TEST_DB_PASS is required} +response=/tmp/tkl-tomcat-apache-response.$$ +headers=/tmp/tkl-tomcat-apache-headers.$$ +apache_modules=/tmp/tkl-tomcat-apache-modules.$$ +policy=/tmp/tkl-tomcat-apache-policy.$$ +probe=/var/lib/tomcat10/webapps/cp/tkl-v19-probe.jsp +database=tkl_tomcat_apache_v19_acceptance +database_created=false + +report_error() { + printf 'test_failure line=%s status=%s command=%q\n' "$1" "$2" "$3" >&2 + exit "$2" +} +trap 'report_error "$LINENO" "$?" "$BASH_COMMAND"' ERR + +cleanup() { + rm -f -- "$response" "$headers" "$apache_modules" "$policy" "$probe" + if $database_created; then + mariadb --user=root --password="$db_password" \ + --execute "DROP DATABASE IF EXISTS $database" || true + fi +} +trap cleanup EXIT + +systemctl --quiet is-active apache2.service tomcat10.service mariadb.service \ + multi-user.target +systemctl --quiet is-enabled apache2.service tomcat10.service mariadb.service +test "$(systemctl show --property=User --value tomcat10.service)" = tomcat +apache2ctl configtest +apache2ctl -M >"$apache_modules" +grep -Fq ' jk_module ' "$apache_modules" +test -L /etc/apache2/sites-enabled/jktomcat.conf +test ! -e /etc/apache2/sites-enabled/000-default.conf + +tomcat_package=$(dpkg-query -W -f='${Version}' tomcat10) +tomcat_admin_package=$(dpkg-query -W -f='${Version}' tomcat10-admin) +apache_package=$(dpkg-query -W -f='${Version}' apache2) +jk_package=$(dpkg-query -W -f='${Version}' libapache2-mod-jk) +java_package=$(dpkg-query -W -f='${Version}' openjdk-21-jre-headless) +mariadb_package=$(dpkg-query -W -f='${Version}' mariadb-server) +java_version=$(java -version 2>&1 | head -n 1) +tomcat_version=$(/usr/share/tomcat10/bin/version.sh 2>&1 | \ + awk '/^Server number:/ { + sub(/^[^:]+:[[:space:]]*/, "") + print + exit + }') + +grep -q '^10\.1\.' <<<"$tomcat_version" +grep -q 'version "21\.' <<<"$java_version" +java_binary=$(readlink -f "$(command -v java)") +dpkg-query -S /usr/share/tomcat10/bin/catalina.sh "$java_binary" \ + /usr/lib/apache2/modules/mod_jk.so >/dev/null +dpkg-query -W turnkey-tomcat-apache-19.0 webmin-apache webmin-mysql \ + >/dev/null + +test -d /var/lib/tomcat10/webapps/cp +test ! -d /var/lib/tomcat10/webapps/ROOT +python3 - <<'PYTHON' +import os +import xml.etree.ElementTree as ET + +contexts = { + "manager": "/usr/share/tomcat10-admin/manager", + "host-manager": "/usr/share/tomcat10-admin/host-manager", + "docs": "/usr/share/tomcat10-docs/docs", +} +for name, doc_base in contexts.items(): + descriptor = f"/etc/tomcat10/Catalina/localhost/{name}.xml" + assert ET.parse(descriptor).getroot().get("docBase") == doc_base + assert os.path.isdir(doc_base) +PYTHON +grep -q 'CATALINA_HOME="/usr/share/tomcat10"' /etc/environment +grep -q 'JAVA_HOME="/usr/lib/jvm/java-21-openjdk-amd64"' /etc/environment +grep -q '^JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64' \ + /etc/default/tomcat10 + +python3 - /etc/tomcat10/server.xml <<'PYTHON' +import sys +import xml.etree.ElementTree as ET + +root = ET.parse(sys.argv[1]).getroot() +connectors = root.findall("./Service/Connector") +assert len(connectors) == 1 +connector = connectors[0] +assert connector.get("protocol") == "AJP/1.3" +assert connector.get("address") == "127.0.0.1" +assert connector.get("port") == "8009" +assert connector.get("secretRequired") == "false" +PYTHON + +listeners=$(ss -ltnH) +! grep -Eq ':8080[[:space:]]' <<<"$listeners" +grep -Fq 'worker.ajp13_worker.host=127.0.0.1' \ + /etc/libapache2-mod-jk/workers.properties +grep -Fq 'worker.ajp13_worker.port=8009' \ + /etc/libapache2-mod-jk/workers.properties +grep -Fq 'Include /etc/tomcat10/mod_jk.conf' \ + /etc/apache2/sites-available/jktomcat.conf +grep -Fq 'JkMount /cp/*' /etc/tomcat10/mod_jk.conf +grep -Fq 'JkMount /manager/*' /etc/tomcat10/mod_jk.conf + +curl --fail --silent --show-error http://127.0.0.1/ >"$response" +grep -Fq 'window.location = "/cp"' "$response" +curl --insecure --fail --silent --show-error --dump-header "$headers" \ + https://127.0.0.1/cp/ >"$response" +grep -qi '^Server: Apache' "$headers" +grep -q 'TurnKey Tomcat Apache' "$response" +grep -q 'href="/manager/html"' "$response" +grep -q 'href="/host-manager/html"' "$response" + +anonymous_status=$(curl --insecure --silent --output /dev/null \ + --write-out '%{http_code}' https://127.0.0.1/manager/html) +test "$anonymous_status" = 401 +curl --insecure --fail --silent --show-error --user "admin:$password" \ + https://127.0.0.1/manager/html >"$response" +grep -q 'Tomcat Web Application Manager' "$response" +curl --insecure --fail --silent --show-error --user "admin:$password" \ + https://127.0.0.1/manager/text/serverinfo >"$response" +grep -Fxq 'OK - Server info' "$response" +grep -q '^Tomcat Version: \[Apache Tomcat/10\.1\.' "$response" +curl --insecure --fail --silent --show-error --user "admin:$password" \ + https://127.0.0.1/manager/text/list >"$response" +grep -Eq '^/cp:running:' "$response" +grep -Eq '^/docs:running:' "$response" + +python3 - /etc/tomcat10/tomcat-users.xml "$password" <<'PYTHON' +import sys +import xml.etree.ElementTree as ET + +root = ET.parse(sys.argv[1]).getroot() +admin = next(user for user in root.findall("user") + if user.get("username") == "admin") +assert admin.get("password") == sys.argv[2] +roles = set(admin.get("roles", "").split(",")) +assert {"admin-gui", "admin-script", "manager-gui", "manager-script"} <= roles +PYTHON + +cat >"$probe" <<'EOF' +<%@ page contentType="text/plain" %>turnkey-tomcat-apache-v19-jsp-ok +EOF +chown tomcat:tomcat "$probe" +curl --retry 5 --retry-delay 1 --insecure --fail --silent --show-error \ + https://127.0.0.1/cp/tkl-v19-probe.jsp >"$response" +grep -Fxq 'turnkey-tomcat-apache-v19-jsp-ok' "$response" +rm -f -- "$probe" + +curl --insecure --fail --silent --show-error --head \ + https://127.0.0.1:12321/ >/dev/null +mariadb --user=root --password="$db_password" \ + --execute "CREATE DATABASE $database" +database_created=true +mariadb --user=root --password="$db_password" "$database" --execute \ + 'CREATE TABLE probe (value VARCHAR(32)); INSERT INTO probe VALUES ("database-ok")' +mariadb --user=root --password="$db_password" --batch --skip-column-names \ + "$database" \ + --execute 'SELECT value FROM probe' | grep -Fxq 'database-ok' +mariadb --user=root --password="$db_password" \ + --execute "DROP DATABASE $database" +database_created=false + +before="$tomcat_package|$tomcat_admin_package|$apache_package|$jk_package|$java_package|$mariadb_package" +apt-get update >/dev/null +for package in tomcat10 tomcat10-admin apache2 libapache2-mod-jk \ + openjdk-21-jre-headless mariadb-server webmin-apache webmin-mysql; do + apt-cache policy "$package" >"$policy" + candidate=$(awk '/Candidate:/ {print $2}' "$policy") + test -n "$candidate" + test "$candidate" != '(none)' + grep -Eq 'trixie|deb13' "$policy" +done +after="$(dpkg-query -W -f='${Version}' tomcat10)|$(dpkg-query -W -f='${Version}' tomcat10-admin)|$(dpkg-query -W -f='${Version}' apache2)|$(dpkg-query -W -f='${Version}' libapache2-mod-jk)|$(dpkg-query -W -f='${Version}' openjdk-21-jre-headless)|$(dpkg-query -W -f='${Version}' mariadb-server)" +test "$after" = "$before" +grep -Rqs '^Suites: trixie' /etc/apt/sources.list.d +! grep -Rqi bookworm /etc/apt/sources.list.d + +cat >"$result" <