cs:tech:idp:5:jetty

Jetty

Na této stránce se nachází návod, jak do linuxové distribuce Debian nainstalovat Jetty jako servlet kontejner a HTTP server pro potřeby Shibboleth IdP. Před instalací musíme zprovoznit MariaDB.

Tento návod lze použít také pro aktualizaci z Jetty 11 na Jetty 12. Předpokladáme, že při instalaci Jetty 11 jste postupovali podle našeho návodu.

V případě aktualizace doporučujeme prvně pročíst postup migrace a dokumentaci od Shibboleth IdP.

Oproti řadě 11 jsme přejmenovali adresář /opt/jetty11 a /opt/jetty11-idp na /opt/jetty a /opt/jetty-idp. Přejmenovali jsme také službu (v systemd) z jetty11 na jetty. Díky tomu je aktualizace vlastně praralelní instalace Jetty 12 vedle Jetty 11.


Authbind

Nejdříve nainstalujeme Authbind:

# Instalace Authbind
apt install authbind

V adresáři /etc/authbind/byport vytvoříme soubory 80 a 443 s tímto obsahem:

jetty

Souborům změníme práva:

# Změna práv u souborů 80 a 443
chmod 644 /etc/authbind/byport/80 /etc/authbind/byport/443

Instalace

Dále potřebujeme nainstalovat Javu 17:

# Instalace Javy 17
apt install openjdk-17-jre

Nyní nastavíme proměnnou JAVA_HOME pro současnou i budoucí relace:

# Nastavení proměnné JAVA_HOME
eval $(echo "export JAVA_HOME=/usr" | tee -a /root/.bashrc)

Jelikož Jetty není součástí balíčků Debianu, stáhneme následující soubory ze stránek projektu:

# Stažení Jetty
wget -P /opt \
     https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-home/12.0.14/jetty-home-12.0.14.tar.gz \
     https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-home/12.0.14/jetty-home-12.0.14.tar.gz.asc \
     https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-home/12.0.14/jetty-home-12.0.14.tar.gz.sha1
 
# Přepnutí do adresáře /opt
cd /opt
 
# Kontrola SHA1 otisku
echo -e '\t jetty-home-12.0.14.tar.gz' >> jetty-home-12.0.14.tar.gz.sha1
sha1sum -c jetty-home-12.0.14.tar.gz.sha1
 
# Kontrola GPG podpisu
gpg --verify jetty-home-12.0.14.tar.gz.asc
 
# Rozbalení archivu
tar -xzf jetty-home-12.0.14.tar.gz
 
# Přesunutí adresáře
mv jetty-home-12.0.14 jetty
 
# Vytvoření adresáře se základem jetty-idp
mkdir -p /opt/jetty-idp/etc

Konfigurace

Nakonfigurovat Jetty pro běh Shibboleth IdP je velice triviální, protože stačí stáhnout již připravené konfigurační soubory a pouze v některých z nich udělat drobné změny.

idp.mod

Vytvoříme si v Jetty tzv. modul pro Shibboleth IdP, který obsahuje veškeré závislosti, jež budou pro běh IdP vyžadovány.

V adresáři /opt/jetty/modules si tedy vytvoříme soubor idp.mod s tímto obsahem:

[description]
Shibboleth IdP
 
[depends]
ee10-annotations
ee10-deploy
ee10-jsp
ee10-jstl
ee10-plus
ee10-servlets
ext
http
http2
https
requestlog
resources
rewrite
server
ssl
 
[files]
tmp/

Nejsnáze toho dosáhneme stažením již připraveného souboru:

# Stažení souboru idp.mod
wget -P /opt/jetty/modules \
    https://www.eduid.cz/jetty/idp.mod

keystore

Pro šifrovanou komunikaci bude IdP využívat TLS certifikát. Ten je možné získat na službě TCS CESNET. V případě, že nemáte ke službě TCS přístup, můžete využít zdarma certifikát od Let's Encrypt anebo si zakoupit certifikát u libovolné důvěryhodné certifikační autority.

Získaný certifikát (zde označený jako cert.pem) musíme nejprve doplnit o mezilehlý certifikát vyjma kořenového certifikátu (zde označený chain.pem).

# Spojení koncového a mezilehlého certifikátu 
cat cert.pem chain.pem > jetty.txt

Následně si certifikát (jetty.txt) a privátní klíč (key.pem) převedeme do formátu PKCS#12. Nejprve zadáme heslo k privátnímu klíči (key.pem) a následně dvakrát úplně nové heslo, které si vygenerujeme pomocí openssl a poznamenáme!

# Generování nového hesla
openssl rand -hex 20
 
# Převod certifikátu do formátu PKCS#12
openssl pkcs12 -export -inkey key.pem -in jetty.txt -out jetty.pkcs12

Pak již zbývá jen do adresáře /opt/jetty-idp/etc nakopírovat soubor jetty.pkcs12 a přejmenovat ho na keystore.p12 s TLS certifikátem, kterému nastavíme práva 640 a skupinu jetty.

# Přesunutí souboru keystore.12
mv jetty.pkcs12 /opt/jetty-idp/etc/keystore.p12
 
# Vytvoření uživatele jetty
useradd -s /bin/false -d /opt/jetty jetty
 
# Změna práv ke keystoru
chmod 640 /opt/jetty-idp/etc/keystore.p12
chgrp jetty /opt/jetty-idp/etc/keystore.p12

Heslo, které jsme si vygenerovali pomocí příkazu openssl a použili ho, následně použijeme v dalším kroku, kde heslo vložíme do konfiguračního souboru idp.ini.

idp.ini

V adresáři /opt/jetty-idp/start.d si vytvoříme konfigurační soubor idp.ini s tímto obsahem:

# --------------------------------------- 
# Module: idp
# Shibboleth IdP
# --------------------------------------- 
--module=idp
 
# Allows setting Java system properties (-Dname=value)
# and JVM flags (-X, -XX) in this file
--exec
 
# Newer garbage collector that reduces memory needed for larger metadata files
-XX:+UseG1GC
 
# Maximum amount of memory that Jetty may use
-Xmx1500m
 
# Keystore password
jetty.sslContext.keyStorePassword=FIXME
# Truststore password
jetty.sslContext.trustStorePassword=FIXME
# KeyManager password
jetty.sslContext.keyManagerPassword=FIXME
 
# HTTP
jetty.http.host=127.0.0.1
jetty.http.port=80
 
# HTTPS
jetty.ssl.host=0.0.0.0
jetty.ssl.port=443
 
# Disable SSL renegotiation
jetty.sslContext.renegotiationAllowed=false
 
# Hide Jetty version
jetty.httpConfig.sendServerVersion=false
 
etc/tweak-ssl.xml

Opět můžeme stáhnout připravený soubor, tentokrát ho však budeme muset upravit:

# Stažení souboru idp.ini
wget -P /opt/jetty-idp/start.d \
    https://www.eduid.cz/jetty/idp.ini
 
# Otevřeme konfigurační soubor idp.ini
vim /opt/jetty-idp/start.d/idp.ini

V souboru /etc/jetty-idp/start.d/idp.ini nyní musíme nastavit ještě následující hesla na hodnotu vygenerovanou v předchozím kroku pomocí příkazu openssl:

jetty.sslContext.keyStorePassword=   ___HESLO___
jetty.sslContext.trustStorePassword= ___HESLO___
jetty.sslContext.keyManagerPassword= ___HESLO___

tweak-ssl.xml

V adresáři /opt/jetty-idp si vytvoříme soubor tweak-ssl.xml, v němž definujeme zakázané a povolené šifry a protokoly:

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
<Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
 
    <!-- Exclude old and unsafe ciphers -->
    <Call name="addExcludeCipherSuites">
        <Arg>
            <Array type="String">
                <Item>.*DES.*</Item>
                <Item>.*DSS.*</Item>
                <Item>.*MD5.*</Item>
                <Item>.*NULL.*</Item>
                <Item>.*RC4.*</Item>
                <Item>.*_RSA_.*MD5$</Item>
                <Item>.*_RSA_.*SHA$</Item>
                <Item>.*_RSA_.*SHA1$</Item>
                <Item>TLS_DHE_RSA_WITH_AES_128.*</Item>
                <Item>TLS_DHE_RSA_WITH_AES_256.*</Item>
                <Item>TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256</Item>
                <Item>TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384</Item>
            </Array>
        </Arg>
    </Call>
 
    <!-- Exclude old and unsafe protocols -->
    <Call name="addExcludeProtocols">
        <Arg>
            <Array type="java.lang.String">
                <Item>SSL</Item>
                <Item>SSLv2</Item>
                <Item>SSLv2Hello</Item>
                <Item>SSLv3</Item>
            </Array>
        </Arg>
    </Call>
 
    <!-- Forward Secrecy -->
    <Set name="IncludeCipherSuites">
        <Array type="String">
            <Item>TLS_ECDHE.*</Item>
        </Array>
    </Set>
 
</Configure>

Opět můžete použít jíž připravený soubor:

# Stažení souboru tweak-ssl.xml
wget -P /opt/jetty-idp/etc \
    https://www.eduid.cz/jetty/tweak-ssl.xml

idp.xml

V adresáři /opt/jetty-idp/webapps si vytvoříme konfigurační soubor, který zajistí, že v kontejneru Jetty poběží Shibboleth IdP. Soubor s názvem idp.xml bude mít tento obsah:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://eclipse.dev/jetty/configure_10_0.dtd">
 
<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext"> 
    <Set name="war"><SystemProperty name="idp.war.path" default="/opt/shibboleth-idp/war/idp.war" /></Set>
    <Set name="contextPath"><SystemProperty name="idp.context.path" default="/idp" /></Set>
    <Set name="extractWAR">false</Set>
    <Set name="copyWebDir">false</Set>
    <Set name="copyWebInf">true</Set>
</Configure>

Již připravený soubor si můžete stáhnout:

# Stažení souboru idp.xml
wget -P /opt/jetty-idp/webapps \
    https://www.eduid.cz/jetty/idp.xml

index.jsp

Vytvoříme adresář /opt/jetty-idp/webapps/root a následně do souboru index.jsp umístíme přesměrování na stránku naší domovské organizace.

Změňte www.example.org na domovskou adresu své organizace!

# Vytvoření složky pro statický web v Jetty
mkdir -p /opt/jetty-idp/webapps/root
 
# Vytvoření přesměrování na domovskou stránku
echo '<% response.sendRedirect("https://www.example.org"); %>' > \
    /opt/jetty-idp/webapps/root/index.jsp

Knihovny

Shibboleth IdP 5.0.0 bez viditelných oznámení — podobně jako řada 4 — obsahuje commons-dbcp2.jar a commons-pool2.jar, takže není nutné tyto knihovny dodávat do Jetty.

Jetty potřebuje pro správnou funkčnost do složky s externími knihovnami /opt/jetty-idp/lib/ext nalinkovat pouze mariadb-java-client.jar, což je JDBC konektor do databáze MariaDB, kam se ukládají persistentní identifikátory uživatelů a souhlasy s uvolněním osobních informací. JDBC pro MariaDB jsme nainstalovali v předchozím kroku instalací balíčku libmariadb-java.

# Vytvoření složky lib/ext
mkdir -p /opt/jetty-idp/lib/ext
 
# Vytvoření symbolického odkazu na JDBC
ln -s /usr/share/java/mariadb-java-client.jar \
      /opt/jetty-idp/lib/ext/mariadb-java-client.jar

Bezpečnost

Tento krok není pro běh IdP nutný, ale je velice vhodný pro zvýšení bezpečnosti uživatelů, kteří budou do přihlašovací stránky na IdP zadávat svá uživatelská jména a hesla.

jetty-rewrite-rules.xml

V adresáři /opt/jetty-idp/etc/ vytvoříme soubor jetty-rewrite-rules.xml s následujícím obsahem:

Zkontrolujte si zejména Content-Security-Policy hlavičku!

Pokud se v prohlížeči na IdP nenačtou např. styly nebo obrázky, je to chybějícími záznamy právě v Content-Security-Policy hlavičce. Pokud byste např. chtěli povolit načítání obrázků ze všech [šifrovaných] URL adres (a ne jenom z URL adresy serveru s IdP, čili 'self'), pak musíte upravit img-src následovně: img-src: 'self' https:.

Používáte-li pro obrázky formát SVG, který v sobě obsahuje definici kaskádových stylů, nezapomeňte do style-src přidat ještě hodnotu 'unsafe-inline', jinak se styly na SVG obrázek neaplikují. Výsledná definice pro styly tedy bude muset vypadat takto: style-src 'unsafe-inline' 'self'.

I přes tyto počáteční problémy se však toto bezpečnostní opatření vyplatí, protože webový prohlížeč odmítne uživatelům načíst podstrčené soubory, pokud by se to útočníkovi nějak povedlo.

Budete-li používat výchozí přihlašovací stránku IdP, budete mít s těmito pravidly drobný zádrhel. Výchozí přihlašovací stránka zobrazuje loga služeb. Pokud však v img-src nebudete mít https:, loga se vám na přihlašovací stránce zobrazovat nebudou. A to nevypadá moc hezky. Zvažte tedy úpravu souboru views/login.vm, anebo povolte načítání všech obrázků po protokolu https.

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">
<Configure id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RuleContainer">
 
<!-- Strict-Transport-Security -->
<Call name="addRule">
  <Arg>
    <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
      <Set name="pattern">*</Set>
      <Set name="headerName">Strict-Transport-Security</Set>
      <Set name="headerValue">Max-Age=15768000</Set>
    </New>
  </Arg>
</Call>
 
<!-- X-Content-Type-Options -->
<Call name="addRule">
  <Arg>
    <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
      <Set name="pattern">*</Set>
      <Set name="headerName">X-Content-Type-Options</Set>
      <Set name="headerValue">nosniff</Set>
    </New>
  </Arg>
</Call>
 
<!-- X-Xss-Protection -->
<Call name="addRule">
  <Arg>
    <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
      <Set name="pattern">*</Set>
      <Set name="headerName">X-Xss-Protection</Set>
      <Set name="headerValue">1; mode=block</Set>
    </New>
  </Arg>
</Call>
 
<!-- X-Frame-Options -->
<Call name="addRule">
  <Arg>
    <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
      <Set name="pattern">*</Set>
      <Set name="headerName">X-Frame-Options</Set>
      <Set name="headerValue">DENY</Set>
    </New>
  </Arg>
</Call>
 
<!-- Content-Security-Policy -->
<Call name="addRule">
  <Arg>
    <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
      <Set name="pattern">*</Set>
      <Set name="headerName">Content-Security-Policy</Set>
      <Set name="headerValue">default-src 'self'; style-src 'self'; script-src 'self' 'unsafe-inline'; img-src 'self'; font-src; frame-ancestors 'none'</Set>
    </New>
  </Arg>
</Call>
 
<!-- Referrer-Policy -->
<Call name="addRule">
  <Arg>
    <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
      <Set name="pattern">*</Set>
      <Set name="headerName">Referrer-Policy</Set>
      <Set name="headerValue">no-referrer-when-downgrade</Set>
    </New>
  </Arg>
</Call>
 
</Configure>

Jako v předchozích případech, i tento soubor je již připravený a můžete si ho stánout:

# Přepsání původního souboru jetty-rewrite.xml novým
wget -O /opt/jetty-idp/etc/jetty-rewrite-rules.xml \
    https://www.eduid.cz/jetty/jetty-rewrite-rules.xml

jetty-rewrite.xml

V adresáři /opt/jetty-idp/etc/ dále vytvoříme soubor jetty-rewrite.xml s následujícím obsahem:

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">
 
<Configure id="Server" class="org.eclipse.jetty.server.Server">
  <Call name="insertHandler">
    <Arg>
      <New class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
        <Set name="originalPathAttribute">
          <Property name="jetty.rewrite.originalPathAttribute" default="jetty.rewrite.originalRequestPath" />
        </Set>
        <Get id="Rewrite" name="ruleContainer" />
        <!-- see jetty-rewrite-rules.xml in $JETTY_BASE for how to add a rule(s) -->
      </New>
    </Arg>
  </Call>
</Configure>

Soubor je již připravený a můžete si ho stánout:

# Přepsání původního souboru jetty-rewrite.xml novým
wget -O /opt/jetty-idp/etc/jetty-rewrite.xml \
    https://www.eduid.cz/jetty/jetty-rewrite.xml

Služba

Vytvoříme soubor služby jetty pomocí následující příkazu:

# Vytvoření služby jetty pro systemd
systemctl edit --full --force jetty.service

Po překopírování níže uvedeného obsahu soubor uložíme:

[Unit]
Description=Jetty Web Application Server
After=syslog.target network.target remote-fs.target nss-lookup.target
 
[Service]
AmbientCapabilities=CAP_NET_BIND_SERVICE
 
# Configuration
Environment="JETTY_HOME=/opt/jetty"
Environment="JETTY_STATE=/opt/jetty-idp/jetty/jetty.state"
Environment="JETTY_RUN=/opt/jetty-idp/jetty"
Environment="JAVA_OPTS=-Djava.awt.headless=true"
EnvironmentFile=-/opt/jetty/default/jetty
 
# Security
User=jetty
Group=jetty
PrivateTmp=yes
NoNewPrivileges=true
WorkingDirectory=/opt/jetty-idp/
LogsDirectory=jetty
LogsDirectoryMode=750
ProtectSystem=strict
ReadWritePaths=/opt/jetty-idp/logs/
ReadWritePaths=/opt/jetty-idp/jetty/
TimeoutSec=infinity
 
# Lifecycle
Type=forking
ExecStart=/opt/jetty/bin/jetty.sh start
PIDFile=/opt/jetty-idp/jetty/jetty.pid
SuccessExitStatus=143
Restart=on-failure
 
# Logging
SyslogIdentifier=jetty
 
[Install]
WantedBy=multi-user.target

Dále si ještě vytvoříme pracovní adresáře a stavový soubor pro Jetty:

# Vytvoření složek pro jetty
mkdir /opt/jetty-idp/{jetty,logs}
 
# Upravení práv složek
chown jetty /opt/jetty-idp/{jetty,logs}
 
# Vytvoření souboru stavu jetty
touch /opt/jetty-idp/jetty/jetty.state
chown jetty:jetty /opt/jetty-idp/jetty/jetty.state

Modul rewrite

Před samotným spuštěním Jetty, přidáme a povolíme modul rewrite pomocí následujícího příkazu:

export JETTY_HOME=/opt/jetty
export JETTY_BASE=/opt/jetty-idp
cd $JETTY_HOME
java -jar start.jar --add-modules=rewrite

Úprava systemd pro Jetty

Nyní musíme ještě v systemd pro Jetty povolit přístup pro zápis do adresářů s logy a metadaty.

# Úprava služby jetty v systemd  
systemctl edit jetty

Nastavení práv pro záspis do adresářů /opt/shibboleth-idp/{logs,metadata}:

[Service]  
ReadWritePaths=/opt/shibboleth-idp/logs/  
ReadWritePaths=/opt/shibboleth-idp/metadata/  

Aktualizace z Jetty 11 na Jetty 12

V případě, že provádíme aktualizaci z Jetty 11 na Jetty 12, musíme nyní zastavit původní službu jetty11 a vypnout její spouštění po startu:

# Nespouštět Jetty 11 po startu operačního systému
systemctl disable jetty11
 
# Zastavit Jetty 11
systemctl stop jetty11

Restart

Konfigurace je hotová, čili můžeme Jetty spouštět po startu operačního systému a zároveň restartovat:

# Spouštět Jetty po startu operačního systému
systemctl enable jetty
 
# Restartování Jetty
systemctl restart jetty

Přesvědčíme se, že vše funguje:

# Kontrola síťových spojení
ss -tlpn | fgrep java

Měli bychom vidět, že port 80 poslouchá pouze na adrese 127.0.0.1 a port 443 na všech adresách:

# Kontrola síťových spojení
LISTEN  0  50  [::ffff:127.0.0.1]:80   *:*  users:(("java",pid=15630,fd=61))                                               
LISTEN  0  50                   *:443  *:*  users:(("java",pid=15630,fd=55))                                               

Vyzkoušíme, jestli funguje přesměrování na domovskou stránku naší organizace:

# Vyzkoušení přesměrování
wget -S -O/dev/null http://localhost
wget -S -O/dev/null https://`hostname -f`

Máme-li nainstalováno Jetty, můžete pokračovat instalací Shibboleth IdP.

Poslední úprava:: 2024/11/13 12:14