Deployment guide

Shibboleth Identity Provider 5

Overview

This guide covers the deployment and verification of Shibboleth Identity Provider 5.2.3 on Microsoft Azure using SecureAnyCloud's preconfigured virtual machine image from the Azure Marketplace.

Shibboleth Identity Provider is the SAML 2.0 identity provider used across research and education federations worldwide, and widely in the public sector. It authenticates your users against your own directory and issues signed SAML assertions to the services they sign in to, so a single sign-on works across every service you federate with. It is open source, published under the Apache License 2.0 by the Shibboleth Consortium.

Shibboleth is a registered trademark of Internet2. SecureAnyCloud is not affiliated with, endorsed by, or sponsored by Internet2 or the Shibboleth Consortium. The name is used here only to identify the software this image runs.

Every secret on this image is generated on your own virtual machine, at first boot. That matters more for an identity provider than for almost any other kind of software.

An identity provider signs assertions with a private key, and any service that trusts your identity provider trusts that key. If the key were baked into the image, every customer who deployed it would hold the same one, and any of them could forge assertions for any other.

So this image does not ship an installed identity provider at all. It ships the pinned upstream distribution. Your first boot runs Shibboleth's own installer, which mints this machine's signing key, encryption key, back-channel keystore, sealer keystore and published metadata. Nothing under /opt/shibboleth-idp exists in the image before you launch it.

What you get:

  • Shibboleth Identity Provider 5.2.3, released 18 June 2026, installed from the official distribution tarball with its SHA-256 pinned and verified at build time
  • Eclipse Jetty 12.1.13 as the servlet container, pinned and verified from Maven Central. Shibboleth 5.2's web application declares Jakarta Servlet 6.1, so it needs a Jakarta EE 11 container. Jetty 12.0 and Tomcat 10.1 are both too old
  • OpenJDK 21 LTS from Ubuntu main
  • nginx terminating TLS on port 443 with a certificate generated on your machine at first boot
  • OpenLDAP as a co-located directory on the loopback interface, holding one test user so you can prove a sign-in works within a minute of the machine booting
  • A self-test service provider registered out of the box, and shibboleth-idp-roundtrip.py, which drives a complete SAML 2.0 sign-in and checks the assertion that comes back
  • Ubuntu 24.04 LTS, fully patched at build time, with unattended security upgrades enabled

Exposed ports: 22 and 443, and nothing else. The directory listens on 127.0.0.1:389 and Jetty on 127.0.0.1:8080, and neither is reachable from outside the machine. There is deliberately no port 80 listener.

Before you deploy: choose your hostname

A SAML identity provider has an entity ID, and it is a permanent identifier. Every service provider you federate with records it, and the endpoints in your published metadata are built from your hostname. Changing it later means re-keying and re-registering with every service provider you have.

If you already know the DNS name this identity provider will answer on, set it before the first boot by passing cloud-init user data when you create the VM:

#cloud-config write_files: - path: /etc/sac/shibboleth-idp.conf permissions: '0644' content: | IDP_HOSTNAME=idp.example.ac.uk IDP_SCOPE=example.ac.uk IDP_ENTITY_ID=https://idp.example.ac.uk/idp/shibboleth

If you do not, the first boot uses the machine's public IP address for all three, which is fine for evaluation. Step 6 shows how to move to a real hostname afterwards.

IDP_SCOPE is the domain your scoped attributes are suffixed with, for example eduPersonPrincipalName becomes alice@example.ac.uk. It is normally your organisation's domain.

1

Deploy the Virtual Machine

Option A: Azure Portal

  • Sign in to the Azure Portal and choose Create a resource
  • Search for Shibboleth Identity Provider 5 on Ubuntu 24.04 LTS by SecureAnyCloud and select it
  • Choose your subscription, resource group and region
  • Choose a VM size. Standard_B2ms (2 vCPU, 8 GiB) is the recommended minimum, and the Java heap is set to 1280 MB
  • Set the administrator username to azureuser and upload your SSH public key
  • Under Inbound port rules, allow SSH (22) and HTTPS (443)
  • Review and create

Option B: Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:shibboleth-idp-ubuntu-24-04:default:latest
az vm create \
  --resource-group my-resource-group \
  --name my-shibboleth-idp \
  --image sac1647283583153:shibboleth-idp-ubuntu-24-04:default:latest \
  --size Standard_B2ms \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard \
  --custom-data cloud-init.yaml
# then open the two ports you need
az vm open-port --resource-group my-resource-group --name my-shibboleth-idp --port 443 --priority 900
2

Wait for the first boot to finish

The first boot creates the directory, runs the Shibboleth installer, mints every key and generates your metadata. It takes about a minute. SSH in and confirm it completed:

ssh azureuser@<vm-ip>
sudo test -f /var/lib/sac/data/shibboleth-idp-firstboot.done && echo "first boot complete"

Then check the services and the published metadata:

systemctl is-active slapd shibboleth-idp nginx curl -sk -o /dev/null -w '%{http_code}\n' https://localhost/idp/shibboleth

Expected output:

active active active 200

The details generated for your machine are in a root-only file:

sudo cat /root/shibboleth-idp-credentials.txt

It contains your entity ID, your metadata URL, the directory's administrator distinguished name and password, and the test user's username and password. Keep it on the machine. It is mode 0600 and owned by root.

3

Prove a user can sign in

The image ships a self test service provider and a prover that drives a complete SAML 2.0 browser sign in against it. Run it:

sudo bash -c 'SAC_IDP_PW="$(sed -n "s/^IDP_TEST_PASSWORD=//p" /root/shibboleth-idp-credentials.txt)" \
  python3 /usr/local/sbin/shibboleth-idp-roundtrip.py 127.0.0.1 shibtest'

Expected output:

PASS a login form was presented and submitted PASS SAML status is Success urn:oasis:names:tc:SAML:2.0:status:Success PASS an Assertion was issued PASS the response is signed PASS an AuthnStatement is present PASS the Issuer is this IdP https://<your-host>/idp/shibboleth PASS a NameID was issued PASS the audience is the requesting SP https://selftest.sac.local/sp SHIBBOLETH_SAML_LOGIN_OK

Do not rely on the exit status alone. Check for the SHIBBOLETH_SAML_LOGIN_OK line. Shibboleth's own aacli exits zero even when it is refused, so this prover follows the same discipline it asks of you: read the output, not the status.

You can also sign in with a browser. Visit:

https://<vm-ip>/idp/profile/SAML2/Unsolicited/SSO?providerId=https://selftest.sac.local/sp

Your browser will warn about the certificate, because the one generated at first boot is self-signed. Step 6 covers replacing it. Accept the warning and sign in with the test username and password from the credentials file.

The service you are signing in to is the built-in self-test service provider, which is why it is named on the page. After you sign in, the identity provider posts a signed assertion to that service's assertion consumer URL. That URL is on the loopback interface and does not answer, so your browser shows a connection error. This is expected. The assertion was issued, and the prover above is what reads it.

Visit https://<vm-ip>/idp/profile/Logout to end the session. The page lists the services you accessed during it.

4

Give your metadata to a service provider

A service provider needs your published metadata in order to trust you. The metadata is served, unauthenticated, at:

https://<your-host>/idp/shibboleth
curl -sk https://localhost/idp/shibboleth | head -20

It carries your entity ID, your scope, your single sign on endpoints and the signing and encryption certificates minted on this machine.

To register a service provider with your identity provider, add its metadata to conf/metadata-providers.xml. Save the service provider's metadata to a file and add a provider entry before the closing tag:

sudo tee /opt/shibboleth-idp/metadata/my-service.xml >/dev/null <<'EOF' <!-- paste the service provider's EntityDescriptor here --> EOF sudo chown shibboleth:shibboleth /opt/shibboleth-idp/metadata/my-service.xml

Then edit /opt/shibboleth-idp/conf/metadata-providers.xml and add, immediately before the final </MetadataProvider>:

<MetadataProvider id="MyService" xsi:type="FilesystemMetadataProvider" metadataFile="/opt/shibboleth-idp/metadata/my-service.xml"/>

Reload the metadata service without restarting:

sudo -u shibboleth /opt/shibboleth-idp/bin/reload-service.sh \
  -u http://127.0.0.1:8080/idp -id shibboleth.MetadataResolverService

reload-service.sh defaults to http://localhost/idp, which is port 80, and this image does not listen on port 80. Pass -u pointing at Jetty on the loopback interface instead. That also avoids the script having to trust the self-signed certificate.

For a federation, use an HTTPMetadataProvider with a SignatureValidationFilter pointing at your federation's signing certificate instead. Never trust remote metadata without validating its signature.

Remove the self-test service provider

Once your own service providers are registered, remove the self-test one. It exists only so the machine can prove itself on first boot:

sudo rm /opt/shibboleth-idp/metadata/sac-selftest-sp.xml

Then delete the SacSelfTestSP entries from conf/metadata-providers.xml and conf/relying-party.xml, and restart:

sudo systemctl restart shibboleth-idp
5

Point the identity provider at your own directory

The image ships OpenLDAP on 127.0.0.1:389 with one test user, so the machine works out of the box. In production you will point it at your own directory instead.

Edit /opt/shibboleth-idp/conf/ldap.properties as the shibboleth user. Set your own server, base DN and bind account, and turn StartTLS back on. It is disabled here only because the shipped directory is on the loopback interface:

idp.authn.LDAP.authenticator = bindSearchAuthenticator idp.authn.LDAP.ldapURL = ldaps://ldap.example.ac.uk:636 idp.authn.LDAP.useStartTLS = false idp.authn.LDAP.baseDN = ou=people,dc=example,dc=ac,dc=uk idp.authn.LDAP.userFilter = (uid={user}) idp.authn.LDAP.bindDN = cn=shibboleth,ou=services,dc=example,dc=ac,dc=uk idp.authn.LDAP.trustCertificates = %{idp.home}/credentials/ldap-server.crt

The bind password goes in idp.authn.LDAP.bindDNCredential inside /opt/shibboleth-idp/credentials/secrets.properties, never in ldap.properties. Restart once both files are in place:

sudo systemctl restart shibboleth-idp

Once your own directory is in use, stop and disable the local one:

sudo systemctl disable --now slapd
6

Production hardening

Replace the self-signed TLS certificate

The certificate generated at first boot is self-signed. Replace both files with your own, keeping the paths:

sudo install -m 0644 -o root -g root fullchain.pem /etc/sac/tls/idp.crt
sudo install -m 0640 -o root -g www-data privkey.pem /etc/sac/tls/idp.key
sudo nginx -t && sudo systemctl reload nginx

This certificate is for browsers only. It is deliberately unrelated to the identity provider's own signing and encryption keys, which are never used for TLS.

Move to a real hostname

If you deployed without setting a hostname and now want a real one, the entity ID and every endpoint in your metadata must change, so re-run the installer. Do this before you register with any service provider. Afterwards it means re-registering with all of them.

sudo systemctl stop shibboleth-idp
sudo tee /etc/sac/shibboleth-idp.conf >/dev/null <<'EOF' IDP_HOSTNAME=idp.example.ac.uk IDP_SCOPE=example.ac.uk IDP_ENTITY_ID=https://idp.example.ac.uk/idp/shibboleth EOF
sudo rm -f /var/lib/sac/data/shibboleth-idp-firstboot.done
sudo /usr/local/sbin/shibboleth-idp-firstboot.sh
sudo systemctl start shibboleth-idp

This mints a completely new key set and new metadata. Anyone who already trusts the old keys must be given the new metadata.

Administrative endpoints

Shibboleth's administrative endpoints under /idp/profile/admin/ are gated only by an IP access control policy that trusts the loopback address. resolvertest discloses a user's resolved attributes and dumpconfig discloses configuration. This image therefore refuses them at nginx outright.

It also sets X-Forwarded-For from the real client address rather than appending to whatever the caller sent. Without that, a remote caller could send X-Forwarded-For: 127.0.0.1 and be read as loopback.

To use them, run them on the machine itself against Jetty directly:

curl -s "http://127.0.0.1:8080/idp/profile/admin/resolvertest?requester=https://selftest.sac.local/sp&principal=shibtest"

/idp/status remains reachable over HTTPS, but the identity provider's own policy allows it only from the loopback address. A remote request therefore receives 403.

Branding the sign-in pages

The sign-in and sign-out pages are plain by design. The upstream sample logo, footer text and dead help links are removed at first boot. Add your own in /opt/shibboleth-idp/messages/messages.properties:

idp.logo = /images/my-logo.png idp.logo.alt-text = Example University idp.footer = Contact the IT Service Desk on 0123 456 7890. idp.url.password.reset = https://accounts.example.ac.uk/reset idp.url.helpdesk = https://help.example.ac.uk/

Put the image in /opt/shibboleth-idp/edit-webapp/images/, then rebuild the web application and restart:

sudo -u shibboleth /opt/shibboleth-idp/bin/build.sh
sudo systemctl restart shibboleth-idp
7

Monitoring and patching

/idp/status reports the version, uptime and enabled modules:

curl -sk https://localhost/idp/status | head -12

nginx serves an unauthenticated health endpoint for load balancer probes:

curl -sk https://localhost/healthz

Logs are in /opt/shibboleth-idp/logs/: idp-process.log for the identity provider, idp-warn.log for warnings and errors, and idp-audit.log for one line per authentication.

Ubuntu security updates are applied automatically by unattended-upgrades. Apply everything and reboot on your own schedule:

sudo apt-get update && sudo apt-get -y dist-upgrade
sudo reboot

Shibboleth releases are separate from the operating system. Watch the Shibboleth announce list and upgrade with bin/update.sh from a newer distribution when one is published.

Troubleshooting

Sign-in is refused for a user you know exists. Check idp-warn.log and confirm the user is under the configured base DN and that the bind account can search for them:

sudo tail -30 /opt/shibboleth-idp/logs/idp-warn.log
sudo ldapsearch -x -H ldap://127.0.0.1:389 -b "ou=people,dc=sac,dc=local" "(uid=shibtest)" dn

A service provider rejects your assertion. The cause is almost always stale metadata, or a hostname that changed after you gave the metadata to the service provider. Compare the certificate it has against the one you publish now:

curl -sk https://localhost/idp/shibboleth | grep -o 'entityID="[^"]*"'
sudo openssl x509 -in /opt/shibboleth-idp/credentials/idp-signing.crt -noout -fingerprint -sha256

The identity provider will not start after you rotated a key by hand. Rotating the sealer or the back-channel keystore without updating credentials/secrets.properties fails at start-up with a DataSealerKeyStrategy error rather than an obvious wrong-password message. Re-run the first-boot script from Step 6 instead of rotating keys individually.

/idp/status returns 403 from your laptop. This is correct. The policy allows the loopback address only, so run it on the machine.

Support

SecureAnyCloud provides 24/7 support for this image. Raise a ticket at secureanycloud.com with your Azure subscription ID and the output of:

sudo systemctl is-active slapd shibboleth-idp nginx
sudo tail -40 /opt/shibboleth-idp/logs/idp-warn.log

Support covers the image, its configuration and its deployment. Questions about SAML federation policy belong with your federation operator, and Shibboleth software questions with the Shibboleth community lists.

Licence

Shibboleth Identity Provider is published under the Apache License, Version 2.0. The licence text ships on the image, copied from the distribution at build time:

cat /usr/share/doc/shibboleth-idp/LICENSE.txt | head -3

Third party component licences from the distribution are alongside it in the same directory. Shibboleth is a registered trademark of Internet2. SecureAnyCloud is not affiliated with Internet2 or the Shibboleth Consortium, and this image does not use the Shibboleth logo.

Deploy on Azure

Launch Shibboleth Identity Provider 5 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure