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.
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
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
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.
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
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
.
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___
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
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
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
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
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.
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
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
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
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
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/
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
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.