HOWTO: Setup Jetty 11 as a Service on Debian with SSL Support and Basic Authentication
This Guide is about setting up Jetty as a service on Debian, providing the following features normally needed
for private or internal usage:
- http and https support
- including SSL (using a self-signed certificate)
- basic authentication where needed
- suitable for static web contents as well as web services (servlets)
Install Jetty
Download Jetty 11 from https://jetty.org/download.html.
Choose the zipped tar archive (tgz) as installation source.
Copy downloaded jetty-home-11.0.25.tar.gz to a suitable installation place and unpack the archive file:
cd /usr/local
sudo cp /path/to/downloads/jetty-home-11.0.25.tar.gz .
sudo tar -xzf ./jetty-home-11.0.25.tar.gz
Rename the installation folder and remove the archive file:
sudo mv ./jetty-home-11.0.25 ./jetty11
sudo rm ./jetty-home-11.0.25.tar.gz
So, /usr/local/jetty11 is the Jetty application home directory.
Next we need the Jetty base location. This is the working directory where web contents is located and configured.
The /opt file system is a suiteble place, and we need to have write access in order to not always using root privileges to access our web contents.
mkdir /opt/jetty-base
sudo chmod a+w /opt/jetty-base
Of course, it's up to you to set access rights more restrictively than in this example.
Now, let the system know where Jetty Home and Jetty Base are located, by defining two environment variables:
export $JETTY_HOME="/usr/local/jetty11"
export $JETTY_BASE="/opt/jetty-base"
This should be persistent, for example by adding the above two environmant variable definitions to the /etc/bash.bashrc file.
We want Jetty to be run by a dedicated user named jetty. Let's create this user and make him owner of Jetty Home and Jetty Base:
sudo adduser --system --group --no-create-home jetty
sudo chown -R jetty:jetty /usr/local/jetty11 /opt/jetty-base
Prepare for https and SSL
In order to get https/SSL support, a SSL certificate is needed. In this example, we generate a new self-signed certificate which is valid for 10 years.
Change to some local working directory to create the certificate:
cd .../data/certificate
Enter the following command:
keytool -genkeypair -alias jetty -keyalg RSA -keysize 2048 -keystore jetty.keystore -validity 3650 -dname "CN=hostname.exampledomain.com, OU=exampledomain.com, O=Example-Organization, L=Example-Location, ST=Example-Region, C=DE" -ext "SAN=dns:localhost,dns:hostname,dns:hostname.exampledomain.com"
...
Keystore-Kennwort eingeben: [some new password, see above: /opt/jetty-base/start.d/start.ini]
Neues Kennwort erneut eingeben: [new password once again]
The keytool comes with the installed JDK, in this case OpenJDK 21.
Make sure to replace the example hostname and domain in the -dname and -ext options of the command by your own values.
Now copy the the keystore to the /etc folder of JETTY_BASE:
cp ./jetty.keystore /opt/jetty-base/etc
Using this certificate, when browsing a page adressed with the https protocol, the browser will first
show a warning page, saying that bowsing the page might be unsafe because the certificate is not trusted.
This is because the certificate is self-signed.
Using the extended options link allows us to ignore the warning and to proceed to the page ...
Of course it's up to you to use a free or commercial (signed and trusted) certificate instead.
Configure Jetty
Now that installation and most important preparation is done, let's configure Jetty and at last have it
featured to our needs.
First edit /opt/jetty-base/start.d/start.ini and enter the following contents:
# Server configuration
--module=server
--module=resources
--module=http
--module=https
--module=deploy
--module=security
# Web applications
jetty.webapp.addServerClasses+=,org.eclipse.jetty.logging.
# HTTP/HTTPS ports
jetty.http.port=8080
jetty.https.port=8443
# SSL configuration
jetty.sslContext.keyStorePath=/opt/jetty-base/etc/jetty.keystore
jetty.sslContext.keyStorePassword=[keystore password set when keystore is generated, see above ...]
jetty.sslContext.keyManagerPassword=[keystore password once again]
With this, Jetty should be ready to start:
java -jar /usr/local/jetty11/start.jar
It might fail, because the security module is activated while Basic Authentication is not yet configured.
If so, comment out the --module=security line and try again. Don't forget to comment it back in when
Basic Authentication is configured (see below).
Basic Authentication
If our web contents, completely or partially, should not be public, but instead be accessible by privileged users only,
we need an authentication mechanism. The easiest method is Basic Authentication, where user credentials are
simply held in a file. Let's create this file and edit /opt/jetty-base/etc/realm.properties:
Victoria: topsecret,admin
Mike: secret,user
Nicole: zigzag,user
guest: guest,read-only
In this example, we have 4 user entries, formatted <username>: <password>,<role>, where Roles are
admin, user or read-only.
All the passwords are plain text, but it is possible to use encoded passwords as well, for example,
if we want to use a MD5 encoded password for user Nicole, the encoding can be done via command
echo -n "zigzag" | md5sum
which results
d45e977bfef260da159d651b9de7035d -
Then the entry in realm.properties would be
Nicole: MD5:d45e977bfef260da159d651b9de7035d,user
There are more password encoding methods available.
Now Jetty needs to be configured to provide HashLoginService using the realm defined previously.
Therefore, edit /usr/local/jetty11/etc/jetty.xml with root privileges (use sudo) and
add the following block:
...
<!-- =========================================================== -->
<!-- Add Authentication -->
<!-- =========================================================== -->
<Call name="addBean">
<Arg>
<New class="org.eclipse.jetty.security.HashLoginService">
<Set name="name">Default Realm</Set>
<Set name="config">/opt/jetty-base/etc/realm.properties</Set>
<Set name="hotReload">true</Set>
</New>
</Arg>
</Call>
...
Note that the realm name Default Realm needs to be referenced correctly by the website or service that
requires authentication. This is done the following way:
Let's have a website named samplesite, placed in the webapps folder in the Jetty Base.
The samplesite folder has two subfolders, named public and protected. Each folder
has some content, consisting of html pages.
/opt/jetty-base/webapps
+-- ...
+-- samplesite
+-- index.html
+-- public
+-- public_information.html
+-- protected
+-- protected_information.html
+-- ...
In this file structure, authentication is not present, so all contents can be browsed and is readable.
In order to make authentication applicable, we need to add a WEB-INF subfolder containing a
web.xml file, to the website, so the structure is then
/opt/jetty-base/webapps
+-- ...
+-- samplesite
+-- index.html
+-- public
+-- public_information.html
+-- protected
+-- protected_information.html
+-- WEB-INF
+-- web.xml
+-- ...
web.xml is called a deployment descriptor and, in this example, has the following contents:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- Security constraint for all resources -->
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<url-pattern>/protected/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name> <!-- Must match role in realm.properties -->
</auth-constraint>
</security-constraint>
<!-- Basic authentication configuration -->
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Default Realm</realm-name> <!-- Must match realm name in Jetty configuration -->
</login-config>
<!-- Security role reference -->
<security-role>
<role-name>user</role-name>
</security-role>
</web-app>
The <url-pattern>/protected/*</url-pattern> tag then has the effect, that all contents in and
underneath the samplesite/protected folder, which in this case is protected_information.html, is
secured by basic authentication. Thus, when browsing this contents, the browser will open a login dialog, asking
for username and password.
Setup Jetty as a Service
Instead of using the command line to start Jetty, it is much more comfortable to operate Jetty as a Unix
Service on your system. As mentioned above, in this case we use a Debian (Linux) operating system.
To setup the service, first edit /etc/systemd/system/jetty.service, using sudo, to create a file with the
following contents:
[Unit]
Description=Jetty Web Application Server
After=network.target
[Service]
Type=simple
User=jetty
ExecStart=/usr/local/jdk-21.0.6+7/bin/java -Xms512m -Xmx3072m -jar /usr/local/jetty11/start.jar jetty.state=/opt/jetty-base/jetty.state
WorkingDirectory=/opt/jetty-base
SuccessExitStatus=143
TimeoutStopSec=120
[Install]
WantedBy=multi-user.target
Adapt the ExecStart command line to your JVM memory requirements as well as to your JDK, Jetty installation
and Jetty base location.
Also adapt the WorkingDirectory if needed.
Next reload the service daemon and enable the new Jetty service:
sudo systemctl daemon-reload
sudo systemctl enable jetty.service
The service will automatically start on system boot.
To start or stop the service manually, use
sudo systemctl start jetty.service
sudo systemctl stop jetty.service
To restart the service, for example after changes in web contents when you don't want to wait until Jetty updates contents automatically, use
sudo systemctl restart jetty.service
Reloading the service conifguration:
sudo systemctl reload jetty.service
Query the Service status:
sudo systemctl status jetty.service
If the service could not be started or failed, some information, for example a part of a stack trace, can be seen
in the status.
To see more details and get more information for troubleshooting you can use
sudo journalctl -u jetty.service
This kind of setup is a good starting point, for example, to operate websites and web services at home.
If providing sensible contents, it is, of course, recommended to use more sophisticated security, in
particular for authentication.
If you want your Jetty service to be accessible from outside your local network, dont forget to forward
your http and https ports on your router and other firewalls running on your system.
Addressing websites by name (instead of IP address):
Unless you're lucky and have a dedicated IP address, you may need to install a mechanism that maps your
hostname/domain name to the IP address which is usually dynamically assigned to your system and therefore
changes from time to time. This is typically done by a Dynamic Domain Name Service ("DynDNS" or "DDNS").
whitehorseplanet.com uses the DynDNS service provided by regfish.de, where this domain is
also registered, but there are quite a few alternatives. Just do some research, ideally before you choose
a hoster for your new domain.
Have fun!