Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ numerous large-scale, mission-critical web applications across a diverse
range of industries and organizations.

This appliance configures Tomcat as a standalone application server
(I.e., without an external web server). A `Tomcat on Apache Appliance`_
(that is, without an external web server). A `Tomcat on Apache Appliance`_
is also available for integrations requiring a fully-featured web
server.

Expand All @@ -17,25 +17,27 @@ and on top of that:

- Tomcat configurations:

- Installed from package management.
- Using OpenJDK Java runtime.
- Tomcat 10.1 installed from Debian package management.
- Using the OpenJDK 21 Java runtime from Debian.
- Web applications in /var/lib/tomcat10/webapps.
- Includes TurnKey web control panel.
- Created Tomcat admin/manager roles and admin user.
- Bind Tomcat HTTP connector to port 80 (default: 8080).
- Bind SSL interface to port 443 (default: 8443).
- Tomcat Manager at /manager/html and Virtual Host Manager at
/host-manager/html.
- Binds the Tomcat HTTP connector directly to port 80 (default: 8080).
- Binds the Tomcat SSL interface directly to port 443 (default: 8443).
- Disabled AJP connector on port 8009 (security).
- Tomcat and Java environment variables configuration system wide.

- Includes MySQL.
- Includes MariaDB, a MySQL-compatible database server.
- SSL support out of the box.
- Postfix MTA (bound to localhost) to allow sending of email from web
applications (e.g., password recovery).

Credentials *(passwords set at first boot)*
-------------------------------------------

- Webmin, SSH, MySQL: username **root**
- Webmin, SSH, MariaDB: username **root**
- Tomcat administration applications: username **admin**


Expand Down
70 changes: 70 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Tomcat 19.0 acceptance

## Source decision

Tomcat 19.0 uses Debian 13 Trixie packages for Tomcat 10.1, its manager
applications, OpenJDK 21 and MariaDB. Webmin and its MariaDB module continue to
come from the signed TurnKey Trixie repository. The complete documented stack
is maintained through APT, so this appliance does not need an upstream package
source or a separate updater.

The standalone appliance binds Tomcat directly to ports 80 and 443. It does
not use the Apache reverse proxy provided by the separate Tomcat on Apache
appliance.

The shared v19 cipher substitution leaves its quoted placeholder attached to
the selected Tomcat cipher list. The appliance removes only that invalid XML
suffix after shared configuration, preserving the shared Trixie cipher
selection.

## Acceptance command

```sh
/sandboxed-git/turnkey/tools/test-v19-appliance tomcat \
--source /home/agent/.local/worktrees/turnkey-apps/tomcat/wish-tomcat-v19-trixie
```

## README crosswalk

| README contract | Focused check | Required result |
| --- | --- | --- |
| Tomcat 10.1 and OpenJDK 21 come from Debian | Query packages, versions and binary ownership | Versions match Trixie packages |
| Standalone HTTP and HTTPS on ports 80 and 443 | Request the landing page through both connectors | Both responses identify TurnKey Tomcat |
| Manager and Virtual Host Manager use the firstboot admin account | Check unauthenticated denial, then authenticate to both applications and the text manager | Anonymous access is denied and the generated password succeeds |
| Applications deploy under `/var/lib/tomcat10/webapps` | Upload a minimal JSP WAR through the manager, request it through HTTP and HTTPS, then undeploy it | Deployment, both readbacks and removal succeed |
| AJP on port 8009 is disabled | Inspect runtime listeners | No listener exists on port 8009 |
| MariaDB is available for applications | Create a temporary database and table, write and read a row, then remove them | The database roundtrip succeeds |
| Webmin is the documented system management surface | Request its HTTPS endpoint and verify the MariaDB module package | Webmin responds and the module is installed |
| APT maintains the packaged stack | Refresh metadata and inspect candidates for identity-defining packages | Signed Trixie metadata is accepted, candidates exist and installed versions remain unchanged |
| Root Webmin and SSH credentials are inherited from Core | Cite the unchanged Core layer | Core 19 passed at source `24c82ee3540ce545422742b0e28ba6b687c53ec2` |

## Updater check

`tests/v19.sh` runs `apt-get update` and checks candidates for `tomcat10`,
`tomcat10-admin`, `openjdk-21-jre-headless` and `mariadb-server`. It confirms
that the refresh does not change installed versions and that no Bookworm
source remains.

## Core evidence and limitations

Core 19 run `20260824t010251z-1634-32241` passed normal init, multi-user, SSH,
cron, Trixie identity and the signed APT updater at source
`24c82ee3540ce545422742b0e28ba6b687c53ec2`.

Docker acceptance does not exercise the installer, kernel, bootloader or
physical hardware. Tomcat adds no behavior at those boundaries, so the Core
19 result supplies the inherited evidence.

## Accepted run

The exact acceptance command passed at source commit
`7323dd61d1fe75d56afc7831124985aefe278739`. Run identity, appliance
evidence and known limitations are recorded in
[`docs/v19.0-testing.md`](docs/v19.0-testing.md).

## Deferred minor issues

- The package plan still includes `authbind` and its historical port files.
Trixie's Tomcat systemd unit grants only the low-port capability needed for
ports 80 and 443, so Tomcat no longer relies on authbind. The compatibility
files are harmless and remain for v19.0.
12 changes: 12 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
turnkey-tomcat-19.0 (1) turnkey; urgency=low

* Update the standalone appliance for Debian 13 Trixie packages, including
Tomcat 10.1 and OpenJDK 21.

* Update the bundled Tomcat documentation link for the packaged release.

* Add focused runtime acceptance for the Tomcat managers, direct HTTP and
HTTPS connectors, WAR deployment, MariaDB, Webmin and APT updates.

-- TurnKey Linux Developers <admin@turnkeylinux.org> Mon, 24 Aug 2026 00:00:00 +0000

turnkey-tomcat-18.0 (1) turnkey; urgency=low

* Updated all relevant Debian packages to Bookworm/12 versions; including
Expand Down
5 changes: 5 additions & 0 deletions conf.d/main
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ done

sed -i "s|HOSTNAME_DESC|Tomcat|g" $WEBROOT/index.jsp
sed -i "s|HOSTNAME|tomcat|g" $WEBROOT/index.jsp
sed -i "s|tomcat-9.0-doc|tomcat-10.1-doc|g" $WEBROOT/index.jsp

# The shared cipher substitution leaves its quoted placeholder behind.
# Preserve the selected Trixie cipher list and remove that invalid XML suffix.
sed -i 's|""ZZ_SSL_CIPHERS"|"|' /etc/tomcat10/server.xml

setup_authbind() {
touch /etc/authbind/byport/$1
Expand Down
67 changes: 67 additions & 0 deletions docs/v19.0-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Tomcat 19.0 test report

## Accepted candidate

- Source commit: `7323dd61d1fe75d56afc7831124985aefe278739`
- Run: `20260826t100659z-2340-26438`
- Verdict: `PASS`
- Retained report SHA-256:
`12f5386f7d4fa14237ba3e69db5924abd4b64bda72f971bcc27e831d296fff85`
- Source archive SHA-256:
`20d40f45a0b9bb8f379b108a9aaace6705c7980ca66a03056267a75dfaaef1c1`

The accepted candidate was built from the recorded source archive, imported
into Docker, booted through the normal systemd and firstboot path, and tested
with the appliance-owned `tests/v19.sh` acceptance script.

## Exact command

```sh
/sandboxed-git/turnkey/tools/test-v19-appliance tomcat \
--source /home/agent/.local/worktrees/turnkey-apps/tomcat/wish-tomcat-v19-trixie
```

## Source and updater evidence

Tomcat 10.1, OpenJDK 21 and MariaDB came from Debian 13 Trixie APT
repositories. Webmin came from the TurnKey Trixie APT repository. The
accepted runtime reported:

- `tomcat10` and `tomcat10-admin` version `10.1.55-1~deb13u1`;
- OpenJDK package `21.0.12.1+1-1~deb13u1`;
- `mariadb-server` version `1:11.8.6-0+deb13u1`.

The acceptance script refreshed signed repository metadata with
`apt-get update`, then checked eligible Trixie candidates for `tomcat10`,
`tomcat10-admin`, `openjdk-21-jre-headless` and `mariadb-server`. Installed
versions remained unchanged. Configured Deb822 sources named Trixie, no
Bookworm source remained, and APT accepted the configured repository keyrings.

## README primary-flow crosswalk

| README contract | Accepted evidence |
| --- | --- |
| Tomcat 10.1 and OpenJDK 21 from Debian | Package versions, runtime versions and Debian ownership of the Tomcat and Java binaries were checked. |
| Standalone HTTP and HTTPS | The TurnKey Tomcat landing page was fetched directly over ports 80 and 443. |
| Firstboot administrator credentials | Anonymous manager access returned 401. The generated `admin` credential authenticated to Manager, Virtual Host Manager and the text manager API. The historical default password was absent. |
| Deploy applications under `/var/lib/tomcat10/webapps` | A temporary JSP WAR was deployed through Manager, read over HTTP and HTTPS, undeployed, then confirmed absent with HTTP 404. |
| AJP is disabled | No listener existed on port 8009. |
| MariaDB for applications | A temporary database and table were created, written, read and removed using the firstboot root credential. |
| Webmin administration | The Webmin HTTPS endpoint responded and the MariaDB module package was installed. |
| Managed package updates | Signed Trixie metadata refreshed, eligible candidates existed, and installed application versions remained stable. |
| Core administration | Core 19 acceptance run `20260824t010251z-1634-32241` at source `24c82ee3540ce545422742b0e28ba6b687c53ec2` supplies inherited SSH, root and common platform evidence. |

## Limitations

Docker runtime acceptance does not exercise the installer, kernel, bootloader
or physical hardware. Tomcat adds no appliance-specific behavior at those
boundaries, so the accepted Core 19 result supplies the inherited evidence.
The Docker kernel rejected the appliance hostname firstboot call and the
`systemd-sysctl` unit; the acceptance run checked the application services
and primary flows explicitly.

## Deferred minor issues

- The package plan retains `authbind` and its historical port files. Trixie's
Tomcat systemd unit supplies the low-port capability used for ports 80 and
443, so the compatibility files do not affect the accepted runtime path.
4 changes: 2 additions & 2 deletions plan/main
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ ant

authbind /* Allows non-root programs to bind() to low ports */

openjdk-17-jdk-headless
openjdk-17-jre-headless
openjdk-21-jdk-headless
openjdk-21-jre-headless

default-mysql-server
libmariadb-java
Expand Down
149 changes: 149 additions & 0 deletions tests/v19.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/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}
manager=http://127.0.0.1/manager/text
context=/turnkey-v19-test
war_root=$(mktemp -d /tmp/tkl-tomcat-war.XXXXXX)
war=/tmp/tkl-tomcat-v19.$$.war
response=/tmp/tkl-tomcat-response.$$
policy=/tmp/tkl-tomcat-policy.$$
database=tkl_tomcat_v19_acceptance
deployed=false
database_created=false

report_error() {
printf 'test_failure line=%s status=%s command=%q\n' \
"$1" "$2" "$3" >&2
exit "$2"
}

cleanup() {
if $deployed; then
curl --fail --silent --show-error --user "admin:$password" \
"$manager/undeploy?path=$context" >/dev/null || true
fi
if $database_created; then
mariadb --user=root --password="$db_password" \
--execute "DROP DATABASE IF EXISTS $database" || true
fi
rm -rf -- "$war_root"
rm -f -- "$war" "$response" "$policy"
}
trap 'report_error "$LINENO" "$?" "$BASH_COMMAND"' ERR
trap cleanup EXIT

systemctl --quiet is-active tomcat10.service mariadb.service multi-user.target
systemctl --quiet is-enabled tomcat10.service mariadb.service

tomcat_package=$(dpkg-query -W -f='${Version}' tomcat10)
tomcat_admin_package=$(dpkg-query -W -f='${Version}' tomcat10-admin)
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 -F':[[:space:]]*' '/^Server number:/ {print $2}')

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" >/dev/null
test -d /usr/share/tomcat10-admin/manager
test -d /usr/share/tomcat10-admin/host-manager
test -d /usr/share/tomcat10-docs/docs
test -s /etc/tomcat10/cert.p12
grep -q 'CATALINA_HOME="/usr/share/tomcat10"' /etc/environment
grep -q 'JAVA_HOME="/usr/lib/jvm/java-21-openjdk-amd64"' /etc/environment
! ss -ltnH 'sport = :8009' | grep -q .

curl --retry 15 --retry-all-errors --retry-delay 1 \
--fail --silent --show-error http://127.0.0.1/ >"$response"
grep -q 'TurnKey Tomcat' "$response"
grep -q 'href="/manager/html"' "$response"
grep -q 'href="/host-manager/html"' "$response"
grep -q 'https://127.0.0.1:12321' "$response"
curl --insecure --fail --silent --show-error \
https://127.0.0.1/ >"$response"
grep -q 'TurnKey Tomcat' "$response"

test "$(curl --silent --output /dev/null --write-out '%{http_code}' \
http://127.0.0.1/manager/html)" = 401
curl --fail --silent --show-error --user "admin:$password" \
http://127.0.0.1/manager/html >"$response"
grep -q 'Tomcat Web Application Manager' "$response"
curl --fail --silent --show-error --user "admin:$password" \
http://127.0.0.1/host-manager/html >"$response"
grep -q 'Tomcat Virtual Host Manager' "$response"
curl --fail --silent --show-error --user "admin:$password" \
"$manager/serverinfo" >"$response"
grep -Fxq 'OK - Server info' "$response"
grep -q '^Tomcat Version: \[Apache Tomcat/10\.1\.' "$response"
grep -q 'username="admin"' /etc/tomcat10/tomcat-users.xml
! grep -q 'password="turnkey"' /etc/tomcat10/tomcat-users.xml

cat >"$war_root/index.jsp" <<'EOF'
<%@ page contentType="text/plain" %>turnkey-tomcat-v19-deploy-ok
EOF
jar --create --file "$war" -C "$war_root" .
curl --fail --silent --show-error --user "admin:$password" \
--upload-file "$war" \
"$manager/deploy?path=$context&update=true" >"$response"
grep -q '^OK - Deployed application at context path' "$response"
deployed=true
curl --retry 5 --retry-delay 1 --fail --silent --show-error \
"http://127.0.0.1$context/" >"$response"
grep -Fxq 'turnkey-tomcat-v19-deploy-ok' "$response"
curl --insecure --fail --silent --show-error \
"https://127.0.0.1$context/" >"$response"
grep -Fxq 'turnkey-tomcat-v19-deploy-ok' "$response"
curl --fail --silent --show-error --user "admin:$password" \
"$manager/undeploy?path=$context" >"$response"
grep -q '^OK - Undeployed application at context path' "$response"
deployed=false
test "$(curl --silent --output /dev/null --write-out '%{http_code}' \
"http://127.0.0.1$context/")" = 404

dpkg-query -W webmin-mysql >/dev/null
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" "$database" \
--execute 'DELETE FROM probe; DROP TABLE probe'
mariadb --user=root --password="$db_password" \
--execute "DROP DATABASE $database"
database_created=false

before="$tomcat_package|$tomcat_admin_package|$java_package|$mariadb_package"
apt-get update >/dev/null
for package in tomcat10 tomcat10-admin openjdk-21-jre-headless mariadb-server; 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}' 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" <<EOF
package_source=Debian 13 Trixie APT repositories for Tomcat 10.1, OpenJDK 21 and MariaDB; TurnKey APT for Webmin
installed_version=tomcat10 $tomcat_package (Tomcat $tomcat_version); tomcat10-admin $tomcat_admin_package; openjdk-21-jre-headless $java_package ($java_version); mariadb-server $mariadb_package
runtime_checks=normal init; Tomcat and MariaDB services; direct HTTP and HTTPS landing page; unauthenticated manager denial; authenticated manager and host manager; WAR deploy, HTTP and HTTPS readback, and undeploy; MariaDB roundtrip; Webmin endpoint
updater_command=apt-get update; apt-cache policy tomcat10 tomcat10-admin openjdk-21-jre-headless mariadb-server
updater_result=signed metadata refreshed; eligible Trixie candidates found; installed versions unchanged
updater_channel=Debian Trixie and TurnKey Trixie APT repositories
integrity_evidence=APT accepted signed repository metadata through configured Deb822 sources and keyrings; no Bookworm source remained
EOF