cs:tech:idp:jetty

Java servlet kontejner pro Shibboleth IdP

Na této stránce se nachází návod, jak nainstalovat do linuxové distribuce Debian 8 (Jessie) podporu pro Java servlety pomocí Jetty pro potřeby Shibboleth IdP 3. Po úspěšné instalaci pokračujte v instalaci Shibboleth IdP.


Jetty

Přestože Shibboleth konsorcium uvádí, že pro běh Shibboleth IdP 3.x je potřeba buď Apache Tomcat 7 a novější (Debian 8 (Jessie) obsahuje Tomcat 7 i Tomcat 8) nebo Jetty 8 a novější (Debian 8 (Jessie) obsahuje Jetty 8), v našem návodu se budeme držet doporučení použít Jetty 9.3, v němž probíhá vývoj Shibbolethu i většina testování. Oficiálně je podporován pouze Tomcat 8 a Jetty 9.2–9.3.

Výhoda Jetty oproti Tomcatu je např. i v tom, že Shibboleth IdP 3.x startuje v Jetty výrazně rychleji. I když lze určitými triky startování v Tomcatu urychlit, stále je Jetty rychlejší. Pokud se vám stále Jetty nelíbí, doporučuji přečíst článek Why Choose Jetty? (anglicky), kde jsou uvedeny další argumenty pro Jetty.

Rozhodnete-li se i přesto použít Tomcat namísto Jetty, je velmi pravděpodobné, že Vám bude vše fungovat k Vaší plné spokojenosti. Pokud se však vyskytnou jakékoliv problémy, počítejte s tím, že budete moci být požádáni o reprodukci problému s doporučovaným software, budete-li žádat o pomoc.

Prerekvizity

Aby Jetty mohlo sloužit i jako HTTP server na portu 443 (standardní port pro protokol HTTPS), museli bychom Jetty pouštět pod uživatelem root. My však budeme Jetty z bezpečnostních důvodů spouštět pod neprivilegovaným uživatelem a přístup k portu 443 vyřešíme pomocí setuid.

Nejprve si vytvoříme neprivilegovaného uživatele idp ze skupiny idp. Tento uživatel bude vlastnit instalaci Jetty a později i Shibboleth IdP.

# Vytvoření skupiny idp a uživatele idp
groupadd idp
useradd -m -g idp -s /bin/bash idp

Instalace

Instalace Jetty je velice jednoduchá. Nejprve stáhneme zdrojové kódy Jetty (ověříme jejich otisky!), rozbalíme je a nastavíme několik voleb pro Jetty.

# Základní instalace Jetty
cd /opt
mkdir -p src jetty/tmp
chown -R idp:idp src jetty
su idp
cd /opt/src
wget http://central.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.3.27.v20190418/jetty-distribution-9.3.27.v20190418.tar.gz \
  http://central.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.3.27.v20190418/jetty-distribution-9.3.27.v20190418.tar.gz.sha1 \
  http://central.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.3.27.v20190418/jetty-distribution-9.3.27.v20190418.tar.gz.asc
 
# Kontrola SHA1 otisků
# Výstup z příkazu sha1sum se musí shodovat s obsahem souboru
# jetty-distribution-9.3.27.v20190418.tar.gz.sha1
echo "`cat jetty-distribution-9.3.27.v20190418.tar.gz.sha1`  jetty-distribution-9.3.27.v20190418.tar.gz" | sha1sum -c
 
# Doporučuji zkontrolovat i podpis samotného archivu zdrojových kódů
# Nejprve si musíme importovat klíč 5989BAF76217B843D66BE55B2D0E1FB8FE4B68B4
#gpg --keyserver hkp://keys.gnupg.net --search-keys 5989BAF76217B843D66BE55B2D0E1FB8FE4B68B4
# Následně můžeme provést kontrolu
#gpg --verify jetty-distribution-9.3.27.v20190418.tar.gz.asc
 
exit
tar -xzf src/jetty-distribution-9.3.27.v20190418.tar.gz
chown -R idp:idp /opt/jetty-distribution-9.3.27.v20190418
ln -snf /opt/jetty-distribution-9.3.27.v20190418/bin/jetty.sh /etc/init.d/jetty
echo "JETTY_HOME=/opt/jetty-distribution-9.3.27.v20190418/" > /etc/default/jetty
echo "JETTY_BASE=/opt/jetty" >> /etc/default/jetty

Základní konfigurace

Nyní je potřeba Jetty ještě správně nakonfigurovat. Základní konfigurace probíhá spuštěním Jetty s definováním modulů, které budou pro provoz Shibbolethu potřeba:

# Základní konfigurace Jetty (jako uživatel idp)
su idp
cd /opt/jetty
java -jar /opt/jetty-distribution-9.3.27.v20190418/start.jar \
    --add-to-startd=http,https,logging,deploy,jsp,jstl,plus,servlets,annotations,ext,resources,logging,requestlog,setuid,rewrite

Chceme, aby Jetty bylo omezeno na nešifrovanou komunikaci přes protokol HTTP (port 80) pouze na lokálním rozhraní. Umožní nám to snadné použití skriptů pro Shibboleth IdP (status.sh, reload-service.sh). V souboru start.d/http.ini tedy provedeme toto omezení.

# Upravíme konfiguraci Jetty pro HTTP (jako uživatel idp)
vi /opt/jetty/start.d/http.ini

Odkomentujeme proměnnou jetty.http.host a nastavíme ji na hodnotu localhost (výchozí 0.0.0.0). Dále odkomentujeme proměnnou jetty.http.port a nastavíme ji na hodnotu 80 (výchozí 8080).

// Povolení HTTP pouze pro localhost
jetty.http.host=localhost
 
// Nastavení HTTP na port 80
jetty.http.port=80

Dále chceme, aby Jetty poslouchalo šifrovaný provoz na portu 443. To nastavíme v souboru start.d/ssl.ini.

# Upravíme konfiguraci Jetty pro HTTPS (jako uživatel idp)
vi /opt/jetty/start.d/ssl.ini

Odkomentujeme proměnnou jetty.ssl.port a nastavíme ji na hodnotu 443 (výchozí 8443).

// Nastavení HTTPS na port 443
jetty.ssl.port=443

Jelikož chceme Jetty provozovat pod neprivilegovaným uživatelem na privilegovaném portu, musíme použít SetUID. V souboru start.d/setuid.ini nastavíme našeho neprivilegovaného uživatele, kterého jsme si dříve vytvořili (idp:idp).

# Upravíme konfiguraci Jetty pro setuid (jako uživatel idp)
vi /opt/jetty/start.d/setuid.ini

Odkomentujeme řádky začínající na jetty.setuid a dále nastavíme jetty.setuid.userName=idp a jetty.userid.groupName=idp (výchozí hodnoty jsou jetty.setuid.userName=jetty a jetty.setuid.groupName=jetty).

jetty.setuid.startServerAsPrivileged=false
jetty.setuid.userName=idp
jetty.setuid.groupName=idp
jetty.setuid.umask=002

V adresáři /opt/jetty/webapps/root vytvoříme jednoduchou stránku, která se zobrazí při zadání URL adresy naší instalace Jetty. Toto je sice nepoviné, ale pokud se někdo dostane na stránku samotného IdP, je zajisté dobré, aby stránka nevypadala matoucím dojmem. Obsah souboru index.html si upravte dle svého vlastního uvážení – můžete např. nastavit přesměrování na domovskou stránku své organizace.

# Vytvoření jednoduché stránky pro Jetty (jako uživatel idp)
mkdir -p /opt/jetty/webapps/root
vi /opt/jetty/webapps/root/index.html

Připravíme si konfigurační soubor idp.xml, pomocí něhož definujeme, který WAR (Web application ARchive) bude obsahovat webovou aplikaci našeho IdP a na jaké adrese (v tomto případě https://HOSTNAME_SERVERU/idp) bude přes web IdP naslouchat.

# Stáhneme předpřipravený konfigurační soubor idp.xml (jako uživatel idp)
wget -P /opt/jetty/webapps/ https://www.eduid.cz/_media/cs/tech/idp/idp.xml

Obsah konfiguračního souboru /opt/jetty/webapps/idp.xml je následující:

<Configure class="org.eclipse.jetty.webapp.WebAppContext">
    <Set name="war">/opt/shibboleth-idp/war/idp.war</Set>
    <Set name="contextPath">/idp</Set>
    <Set name="extractWAR">false</Set>
    <Set name="copyWebDir">false</Set>
    <Set name="copyWebInf">true</Set>
    <Set name="tempDirectory">/opt/jetty/tmp</Set>
</Configure>

Tímto máme Jetty téměř připraveno. Zatím jej však nebudeme pouštět, jelikož stejně nemáme nainstalovaný Shibboleth IdP a tedy idp.war zatím neexistuje. Navíc jsme ještě nenakonfigurovali SSL certifikát, aby bylo možné k Jetty (a tedy Shibbolethu) přistupovat přes HTTPS.

Pokročilá konfigurace

V následujících sekcích si Jetty trochu zabezpečíme – nebudeme ukazovat verzi Jetty a nastavíme různé bezpečnostní hlavičky (Strict Transport Security, X-Content-Type-Options, X-Xss-Protection, X-Frame-Options, Referrer-Policy a Content-Security-Policy).

Nezobrazení verze Jetty

V souboru /opt/jetty/start.d/server.ini najdeme volbu jetty.httpConfig.sendServerVersion, která je ve výchozí instalaci zakomentována, odkomentujeme ji a nastavíme na hodnotu false: jetty.httpConfig.sendServerVersion=false. Tím zajistíme, že Jetty nebude posílat v hlavičkách a zobrazovat na webu svou verzi.

# Upravíme konfiguraci Jetty tak, aby se nezobrazovala verze (jako uživatel idp)
vi /opt/jetty/start.d/server.ini
jetty.httpConfig.sendServerVersion=false

HTTP hlavičky

Nastavení HTTP hlaviček se provádí v konfiguračním souboru /opt/jetty/etc/jetty-rewrite.xml. Kromě Content-Security-Policy, kde si musíme pravidla upravit dle našich potřeb, můžeme v drtivé většině případů použít tuto konfiguraci:

FIXME: Content-Security-Policy-Report-Only

# Stáhneme předpřipravený konfigurační soubor jetty-rewrite.xml (jako uživatel idp)
wget -P /opt/jetty/etc/ https://www.eduid.cz/_media/cs/tech/idp/jetty-rewrite.xml
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
<Configure id="Server" class="org.eclipse.jetty.server.Server">
 
  <!-- =========================================================== -->
  <!-- configure rewrite handler                                   -->
  <!-- =========================================================== -->
  <Call name="insertHandler">
    <Arg>
      <New class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
        <Set name="rewriteRequestURI"><Property name="jetty.rewrite.rewriteRequestURI" deprecated="rewrite.rewriteRequestURI" default="true"/></Set>
        <Set name="rewritePathInfo"><Property name="jetty.rewrite.rewritePathInfo" deprecated="rewrite.rewritePathInfo" default="false"/></Set>
        <Set name="originalPathAttribute"><Property name="jetty.rewrite.originalPathAttribute" deprecated="rewrite.originalPathAttribute" default="requestedPath"/></Set>
 
 
        <!-- Set DispatcherTypes  -->
        <Set name="dispatcherTypes">
          <Array type="javax.servlet.DispatcherType">
            <Item><Call class="javax.servlet.DispatcherType" name="valueOf"><Arg>REQUEST</Arg></Call></Item>
            <Item><Call class="javax.servlet.DispatcherType" name="valueOf"><Arg>ASYNC</Arg></Call></Item>
          </Array>
        </Set>
 
 
        <Call name="addRule">
          <Arg>
            <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
              <Set name="pattern">*</Set>
              <Set name="name">Strict-Transport-Security</Set>
              <Set name="value">max-age=15768000</Set>
            </New>
          </Arg>
        </Call>
 
        <Call name="addRule">
          <Arg>
            <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
              <Set name="pattern">*</Set>
              <Set name="name">X-Content-Type-Options</Set>
              <Set name="value">nosniff</Set>
            </New>
          </Arg>
        </Call>
 
        <Call name="addRule">
          <Arg>
            <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
              <Set name="pattern">*</Set>
              <Set name="name">X-Xss-Protection</Set>
              <Set name="value">1; mode=block</Set>
            </New>
          </Arg>
        </Call>
 
        <Call name="addRule">
          <Arg>
            <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
              <Set name="pattern">*</Set>
              <Set name="name">X-Frame-Options</Set>
              <Set name="value">DENY</Set>
            </New>
          </Arg>
        </Call>
 
        <Call name="addRule">
          <Arg>
            <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
              <Set name="pattern">*</Set>
              <Set name="name">Content-Security-Policy-Report-Only</Set>
              <Set name="value">default-src 'self'; style-src 'self' https://maxcdn.bootstrapcdn.com; script-src 'self' https://maxcdn.bootstrapcdn.com https://ajax.googleapis.com; img-src 'self'; font-src https://maxcdn.bootstrapcdn.com; frame-ancestors 'none'</Set>
            </New>
          </Arg>
        </Call>
 
        <Call name="addRule">
          <Arg>
            <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
              <Set name="pattern">*</Set>
              <Set name="name">Referrer-Policy</Set>
              <Set name="value">no-referrer-when-downgrade</Set>
            </New>
          </Arg>
        </Call>
 
      </New>
    </Arg>
  </Call>
</Configure>

SSL certifikát

Nyní je ještě potřeba nakonfigurovat použití SSL certifikátu v Jetty, aby bylo možné provozovat Shibboleth IdP přes HTTPS. K tomuto účelu slouží u Javy tzv. „keystore“. Pro korektní zprovoznění HTTPS je potřeba, aby se do klíčenky („keystore“) uložil SSL certifikát včetně kompletního řetězce až ke kořenovému certifikátu certifikační autority (CA).

Následující návod je pro SSL certifikát získaný pomocí služby TCS CESNET. V případě, že nemáte možnost aktuálně získat certifikát od TCS CESNET, můžete využít službu Let's Encrypt.

Soubor cert.pem obsahuje cílový certifikát pro server whoami-dev.cesnet.cz, soubor key.pem obsahuje privátní klíč k certifikátu a chain_TERENA_SSL_CA_2.pem obsahuje řetězec certifikátů až k samotnému kořenovému CA. Certifikát pro server nejprve sloučíme s kompletním řetězcem:

# Sloučíme serverový certifikát s certifikáty mezilehlých CA a kořenové CA (jako uživatel idp)
cat cert.pem chain_TERENA_SSL_CA_2.pem > jetty-cert.txt

Nyní převedeme certifikát s kompletním řetězcem až ke kořenovému CA (soubor jetty-cert.txt, který jsme právě vytvořili) do formátu PKCS #12.

# Převod certifikátu do formátů PKCS #12 (jako uživatel idp)
openssl pkcs12 -export -inkey key.pem -in jetty-cert.txt -out jetty-cert.pkcs12

Budeme požádáni o heslo k privátnímu klíči (soubor key.pem) a následně budeme požádáni o nové (označíme si ho #1) heslo k souboru jetty-cert.pkcs12, které budeme muset pro kontrolu zadat ještě jednou.

# Výstup příkazu
Enter pass phrase for key.pem:
Enter Export Password:
Verifying - Enter Export Password:

Nyní certifikát včetně kořene ve formátu PKCS #12 (soubor jetty-cert.pkcs12) importujeme do „klíčenky“ Java keystore:

# Import certifikátu do klíčenky keystore (jako uživatel idp)
keytool -importkeystore -srckeystore jetty-cert.pkcs12 -srcstoretype PKCS12 -destkeystore keystore

Nejprve budeme požádáni o další nové heslo (označíme si ho #2) k nově vytvářenému „keystore“ (Enter destination keystore password). Pak budeme požádáni o zopakování tohoto hesla (Re-enter new password). Následně budeme požádáni o heslo (#1) k certifikátu (soubor jetty-cert.pkcs12), který importujeme do „keystore“ (Enter source keystore password).

# Výstup příkazu
Enter destination keystore password:  
Re-enter new password: 
Enter source keystore password:  
Entry for alias 1 successfully imported.
Import command completed:  1 entries successfully imported, 0 entries failed or cancelled

Následně je už jen potřeba keystore uložený v souboru keystore přesunout do Jetty.

# Přesun keystore do Jetty (jako uživatel idp)
mv keystore /opt/jetty/etc

Předposledním krokem je vygenerovat si pomocí jetty-util, jež je součástí instalace Jetty, obfuskované podoby hesel pro přístup ke „keystore“ (#2) a pro přístup k certifikátu (#1).

Heslo (#2) ke keystore:

# Obfuskované heslo (#2) ke keystore (jako uživatel idp)
java -cp /opt/jetty-distribution-9.3.27.v20190418/lib/jetty-util-9.3.27.v20190418.jar \
    org.eclipse.jetty.util.security.Password <heslo_(2)_ke_keystore>
# Výstup příkazu
2015-06-16 15:56:58.986:INFO::main: Logging initialized @322ms
keystore
OBF:1u9x1vn61z0p1yta1ytc1z051vnw1u9l
MD5:5fba3d2b004d68d3c5ca4e174024fc81

Heslo (#1) k certifikátu (heslo, které jste použili při generování klíče k certifikátu):

# Obfuskované heslo (#1) k certifikátu (jako uživatel idp)
java -cp /opt/jetty-distribution-9.3.27.v20190418/lib/jetty-util-9.3.27.v20190418.jar \
    org.eclipse.jetty.util.security.Password <heslo_(1)_k_certifikátu>
# Výstup příkazu
2015-06-16 15:57:02.322:INFO::main: Logging initialized @308ms
certificate
OBF:1sot1w1c1uvk1vo01unz1thb1unz1vn21uum1w261sox
MD5:e0d30cef5c6139275b58b525001b413c

Hesla je potřeba zadat do souboru start.d/ssl.ini (jetty.sslContext.keyStorePassword bude stejné jako jetty.sslContext.trustStorePassword):

# Zadání obfuskovaných hesel #1 a #2 do konfigurace Jetty (jako uživatel idp)
vi /opt/jetty/start.d/ssl.ini
// Konfigurační změny v souboru 'ssl.ini'
jetty.sslContext.keyStorePassword=OBF:1u9x1vn61z0p1yta1ytc1z051vnw1u9l                # heslo (#2)
jetty.sslContext.keyManagerPassword=OBF:1sot1w1c1uvk1vo01unz1thb1unz1vn21uum1w261sox  # heslo (#1)
jetty.sslContext.trustStorePassword=OBF:1u9x1vn61z0p1yta1ytc1z051vnw1u9l              # heslo (#2)

Proměnné jetty.sslContext.keyStorePassword a jetty.sslContext.trustStorePassword nastavte na obfuskované heslo (#2) ke „keystore“. Proměnnou jetty.sslContext.keyManagerPassword nastavte na obfuskované heslo (#1) ke klíči certifikátu (soubor jetty-cert.pkcs12!). Pokud to popletete, Jetty odmítne nastartovat, jelikož nepřečte ani keystore ani klíč.

SSL konfigurace

Výchozí konfigurace Jetty umožňuje i použití dnes již nepříliš důvěryhodných šifer. Proto jejich použití v konfiguraci Jetty zakážeme. K tomu si vytvoříme soubor tweak-ssl.xml v adresáři /opt/jetty/etc.

# Úprava SSL konfigurace pro Jetty (jako uživatel idp)
wget -P /opt/jetty/etc/ https://www.eduid.cz/_media/cs/tech/idp/tweak-ssl.xml

Obsah souboru tweak-ssl.xml je následující:

<?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">
 
  <!-- Zakázání starých a nedůvěryhodných šifer -->
  <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>
      </Array>
    </Arg>
  </Call>
 
  <!-- Zakázání nedůvěryhodných protokolů -->
  <Call name="addExcludeProtocols">
    <Arg>
     <Array type="java.lang.String">
       <Item>SSL</Item>
       <Item>SSLv2</Item>
       <Item>SSLv2Hello</Item>
       <Item>SSLv3</Item>
     </Array>
    </Arg>
  </Call>
 
  <!-- Povolení Forward Secrecy -->
  <Set name="IncludeCipherSuites">
    <Array type="String">
      <Item>TLS_DHE_RSA.*</Item>
      <Item>TLS_ECDHE.*</Item>
    </Array>
  </Set>
 
</Configure>

Nyní už zbývá jen Jetty instruovat, aby tento konfigurační soubor načetlo, to se provede jeho zápisem do start.d/https.ini:

# Přidání tweak-ssl.xml do konfigurace Jetty (jako uživatel idp)
echo etc/tweak-ssl.xml >> /opt/jetty/start.d/https.ini
exit

S tímto nastavením je zabezpečení komunikace na výrazně vyšší úrovni.

Spuštění Jetty

Nyní zbývá nastavit vlastnická práva na Jetty uživateli idp ze skupiny idp (pro případ, že jsme v předchozích krocích někde zapomněli provést určitou úpravu jako uživatel idp a provedli jsme ji jako root) a spustit Jetty.

# Nastavení vlastnických práv pro Jetty a spuštění (jako uživatel root)
systemctl enable jetty
systemctl start jetty
systemctl status jetty

Jetty běží, takže zkontrolujeme, zda nám poslouchá „nešifrovaně“ (port 80) pouze na „localhostu“ (IPv4 adresa 127.0.0.1) a to např. pomocí příkazu nestat:

# Kontrola, zda Jetty nešifrovaně poslouchá jen na localhostu
netstat -an | grep ":80"

Měli bychom vidět následující výstup.

# Výstup příkazu netstat
tcp6       0      0 127.0.0.1:80          :::*                    LISTEN

Podíváme-li se nyní do logů Jetty, uvidíme chybu, která se „táhne“ přes mnoho řádků. Je to v pořádku. Shibboleth IdP ještě není nainstalován, soubor /opt/shibboleth-idp/war/idp.war tedy ještě neexistuje:

2015-08-05 09:02:22.871:WARN:oejw.WebInfConfiguration:main: Web application not found /opt/shibboleth-idp/war/idp.war
2015-08-05 09:02:22.872:WARN:oejw.WebAppContext:main: Failed startup of context o.e.j.w.WebAppContext@df27fae{/idp,null,null}{/opt/shibboleth-idp/war/idp.war}
java.io.FileNotFoundException: /opt/shibboleth-idp/war/idp.war
        at org.eclipse.jetty.webapp.WebInfConfiguration.unpack(WebInfConfiguration.java:495)
        at org.eclipse.jetty.webapp.WebInfConfiguration.preConfigure(WebInfConfiguration.java:72)
        at org.eclipse.jetty.webapp.WebAppContext.preConfigure(WebAppContext.java:474)
        at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:510)
...

Jetty však běží a můžeme ověřit, že v pořádku funguje. Vyzkoušíme přístup přes HTTPS ze svého počítače a případně i přístup přes HTTP z příkazového řádku na serveru:

# Test přístupu k HTTP na localhostu
wget -q -O - http://127.0.0.1

Po zadání příkazu výše uvidíme obsah souboru /opt/jetty/webapps/root/index.html.


Nyní, když máme nainstalováno Jetty, můžeme pokračovat instalací Shibboleth IdP.

Last modified:: 2019/05/20 10:36