Ln
Application Infrastructure Azure

LemonLDAP::NG 2.21 LTS on Ubuntu 24.04 LTS on Azure User Guide

| Product: LemonLDAP::NG 2.21 LTS on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and verification of LemonLDAP::NG 2.21.5 LTS on Microsoft Azure using cloudimg's pre configured virtual machine image from the Azure Marketplace.

LemonLDAP::NG is a WebSSO portal and access manager. It authenticates your users once, then acts as an identity provider for the applications they go on to use, and it is unusual in speaking CAS, SAML 2.0 and OpenID Connect at the same time, from the same server. That combination is what makes it the practical migration route for an organisation moving off a legacy CAS deployment: you can keep answering CAS for the applications that still need it while issuing OIDC tokens and SAML assertions to everything new, without running three products. It is free software published under the GNU General Public License, version 2 or later.

The LemonLDAP::NG sign in portal

The one thing to know about a stock LemonLDAP::NG install

A default LemonLDAP::NG installation selects its Demo authentication backend, and that backend is not a placeholder with a weak password — it accepts any username whose password is the same as the username, and the accounts it resolves (dwho, rtyler, msmith) include one that the shipped configuration grants the Manager, the full administration console. On a stock install, dwho / dwho is a working administrator.

This image does not ship that. At first boot the authentication, user and password backends are all moved onto a directory created on your own machine, the demo accounts are gone, and a per VM administrator is minted with a random password. The image is verified before capture and again on a freshly launched VM: dwho, rtyler and msmith are all refused, while the administrator in your credentials file signs in and reaches the Manager — so the check proves something in both directions rather than just failing everything.

Every secret is generated on your machine

That matters for an SSO server more than for most software. This one signs OpenID Connect ID tokens and SAML assertions with private keys, and every application that trusts this server trusts those keys. A key baked into the image would be shared by every customer who deployed it, and any of them could forge tokens against any other. So first boot mints, on your machine and nowhere else:

  • the directory database and its administrator password
  • the Manager administrator's password
  • the session cipher key — note that a stock configuration has no cipher key at all
  • the SAML signing key and certificate
  • the OpenID Connect signing key and its key ID
  • the TLS certificate nginx serves, and the machine's SSH host keys

Two VMs launched from this image were compared label by label: all fifteen of those values differ between them.

What's included:

  • LemonLDAP::NG 2.21.5-1 from the official vendor repository, GPG signature verified, pinned to that exact version and held so an unattended upgrade cannot move it
  • 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 your administrator and one ordinary demo user so you can prove a sign in works within a minute of the machine booting
  • CAS, SAML 2.0 and OpenID Connect issuers all enabled, each with per VM signing material
  • A self test OpenID Connect relying party and CAS service, both with loopback only callbacks, plus lemonldap-ng-sso-roundtrip.py, which drives a complete OIDC authorization code flow and a complete CAS ticket validation and checks what 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 only. There is deliberately no port 80 listener, so there is no plaintext path to a sign in form.

Why 2.21 and not the newest release

The vendor repository's newest release is in the 2.23 series, but 2.21 is the maintained LTS branch, and this image pins the current head of it. On 10 August 2026 the 2.21 branch received the same two security fixes that went into 2.23.3 on the same day — CVE-2026-19349 (rated high: a GitHub/LinkedIn authentication state token could be created as an SSO session and replayed as a session cookie) and CVE-2026-12804 (an unauthenticated open redirect in the SAML Common Domain Cookie endpoint). The branch also carries the completed fix for CVE-2025-59518 and a fix for a handler access control bypass that affected the vendor's default nginx configuration. There is no security fix outstanding against this pin.

Step 1: Deploy the Virtual Machine

Option A: Azure Portal

  1. In the Azure Portal, search the Marketplace for LemonLDAP::NG 2.21 LTS on Ubuntu 24.04 LTS by cloudimg and select Create.
  2. Choose your subscription, resource group and region.
  3. A Standard_B2s (2 vCPU, 4 GiB) is enough for the portal, the directory and the three issuers. Increase it if you expect heavy concurrent sign in traffic.
  4. Under Administrator account, choose SSH public key and supply your own key. The image ships no user account and no password of any kind; Azure creates your account at provisioning time.
  5. Under Inbound port rules, allow SSH (22) and HTTPS (443). Do not open anything else — nothing else is listening.
  6. Review and create.

Option B: Azure CLI

az vm create \
  --resource-group my-resource-group \
  --name my-lemonldap-ng \
  --image cloudimg:lemonldap-ng-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

az vm open-port --resource-group my-resource-group --name my-lemonldap-ng --port 22 --priority 1001
az vm open-port --resource-group my-resource-group --name my-lemonldap-ng --port 443 --priority 1002

Optional: pin your DNS name before the first boot

LemonLDAP::NG is virtual host driven, and the SAML entity ID and OpenID Connect issuer it publishes are built from the address it believes it answers on. Those are permanent federation identifiers — change one later and every service provider and relying party you registered has to be updated.

If you already know the DNS name this server will answer on, set it before the machine ever boots, with cloud init user data:

#cloud-config
write_files:
  - path: /etc/cloudimg/lemonldap-ng.conf
    permissions: '0644'
    content: |
      LLNG_HOSTNAME=sso.example.org
      LLNG_ADMIN_USER=llngadmin

Pass that with --custom-data cloud-init.yaml on az vm create. If you skip this, the server configures itself for the VM's public IP address, which is fine for evaluation and can be changed later from the Manager.

Step 2: Wait for the first boot to finish

First boot creates the directory, mints every key above and rewrites the configuration. It normally completes in under a minute. Connect and check:

ssh azureuser@<your-vm-ip>

# the sentinel appears only when first boot has finished successfully
sudo test -e /var/lib/cloudimg/lemonldap-ng-firstboot.done && echo "first boot complete"

# and the services it starts
systemctl is-active slapd lemonldap-ng-fastcgi-server nginx

Your credentials were written to a root only file on this machine:

sudo cat /root/lemonldap-ng-credentials.txt
PORTAL_URL=https://<your-vm-ip>/
MANAGER_URL=https://<your-vm-ip>/manager/
LLNG_ADMIN_USERNAME=llngadmin
LLNG_ADMIN_PASSWORD=<generated on this machine>
DEMO_USERNAME=demo
DEMO_PASSWORD=<generated on this machine>
LDAP_ADMIN_DN=cn=admin,dc=cloudimg,dc=local
LDAP_ADMIN_PASSWORD=<generated on this machine>
SELFTEST_OIDC_CLIENT_ID=cloudimg-selftest
SELFTEST_OIDC_CLIENT_SECRET=<generated on this machine>

Step 3: Sign in

Open https://<your-vm-ip>/ in a browser. The certificate is self signed and generated on your machine, so your browser will warn on first visit; replace it with a real certificate in Step 6.

Sign in with LLNG_ADMIN_USERNAME and LLNG_ADMIN_PASSWORD. You land on the portal menu, which lists the applications this server knows about.

The portal menu after signing in

Confirm the demo accounts really are gone — each of these must be refused:

# from the VM itself
for u in dwho rtyler msmith; do
  printf '%-8s -> ' "$u"
  sudo python3 /usr/local/sbin/llng-login-probe.py "$(grep -E '^PORTAL_URL=' /root/lemonldap-ng-credentials.txt | sed 's#.*https://##;s#/$##')" "$u" "$u"
done

Each returns "verdict": "LOGIN_REFUSED".

Step 4: Prove the single sign on actually works

A sign in form that renders proves very little. The image ships a prover that drives two complete federation round trips and checks what comes back — an OpenID Connect authorization code exchanged for an ID token whose RS256 signature is verified against this machine's own published JWKS, and a CAS service ticket validated back to a username:

C=/root/lemonldap-ng-credentials.txt
sudo python3 /usr/local/sbin/lemonldap-ng-sso-roundtrip.py \
  "$(sudo grep -E '^PORTAL_URL=' $C | sed 's#.*https://##;s#/$##')" \
  "$(sudo grep -E '^LLNG_ADMIN_USERNAME=' $C | cut -d= -f2-)" \
  "$(sudo grep -E '^LLNG_ADMIN_PASSWORD=' $C | cut -d= -f2-)" \
  "$(sudo grep -E '^SELFTEST_OIDC_CLIENT_ID=' $C | cut -d= -f2-)" \
  "$(sudo grep -E '^SELFTEST_OIDC_CLIENT_SECRET=' $C | cut -d= -f2-)"

You should see "id_token_signature_verifies_against_vm_jwks": true, then OIDC_SSO_ROUNDTRIP_OK and CAS_SSO_ROUNDTRIP_OK.

The three federation endpoints this server publishes are:

Protocol Endpoint
OpenID Connect discovery https://<host>/.well-known/openid-configuration
OpenID Connect JWKS https://<host>/oauth2/jwks
SAML 2.0 metadata https://<host>/saml/metadata
CAS https://<host>/cas/login, https://<host>/cas/serviceValidate

Step 5: Administer the server

The Manager is at https://<your-vm-ip>/manager/ and is restricted to members of the cloudimg-admins group — the ordinary demo user receives HTTP 403 there. It is where you register the applications that will trust this server.

The Manager, with the CAS, SAML and OpenID Connect branches

To register an OpenID Connect application, open OpenID Connect Relying Parties, add one, and set its client ID, client secret and allowed redirect URIs. SAML service providers and CAS applications are registered from their own branches in the same tree.

Registering an OpenID Connect relying party

Remove the self test relying party

The image registers one OpenID Connect relying party and one CAS service called cloudimg-selftest, so the server can prove itself on first boot. Both have callbacks on 127.0.0.1 and are not reachable from outside the machine, but once your own applications are registered you should delete them: open OpenID Connect Relying Parties, select cloudimg-selftest, remove it, do the same under CAS Applications, and save.

Step 6: Production hardening

Replace the self signed TLS certificate

sudo cp /path/to/fullchain.pem /etc/cloudimg/tls/llng.crt
sudo cp /path/to/privkey.pem   /etc/cloudimg/tls/llng.key
sudo chmod 0600 /etc/cloudimg/tls/llng.key
sudo nginx -t && sudo systemctl reload nginx

Point the server at your own directory

The bundled OpenLDAP directory is there so the machine is useful immediately. In production you will almost always point LemonLDAP::NG at your existing directory instead. In the Manager, open General Parameters → Authentication modules, and set the LDAP server, port, base DN and bind credentials to your own. The demo user can then be removed from the bundled directory, or the bundled directory disabled entirely once nothing uses it.

Move to a real hostname

If you deployed against an IP address and later move to a DNS name, change the portal URL in General Parameters → Portal, update the virtual host entry under Virtual Hosts, and re issue your TLS certificate for the new name. Remember that the SAML entity ID and the OpenID Connect issuer change with it, so every registered application must be updated too — which is why pinning the name before first boot is worth the effort.

A note on cookies and addresses

If you reach this server by IP address, the session cookie is deliberately left as a host only cookie. LemonLDAP::NG would otherwise try to set a cookie for a domain derived from the address, and browsers reject a domain cookie on an IP literal — the sign in would fail with no visible error. The same applies to a single label hostname. Once you move to a real multi label DNS name the cookie is scoped to your parent domain as normal, which is what makes single sign on work across app1.example.org and app2.example.org.

Step 7: Monitoring and patching

Unattended security upgrades are enabled. The LemonLDAP::NG packages themselves are held at 2.21.5-1 so an automatic upgrade cannot move you off the pinned LTS release; upgrade them deliberately when you choose to:

sudo apt-mark unhold lemonldap-ng lemonldap-ng-handler lemonldap-ng-fastcgi-server \
  liblemonldap-ng-common-perl liblemonldap-ng-handler-perl \
  liblemonldap-ng-manager-perl liblemonldap-ng-portal-perl lemonldap-ng-doc
sudo apt-get update && sudo apt-get install --only-upgrade lemonldap-ng

Useful checks:

# the portal is answering
curl -sk -o /dev/null -w '%{http_code}\n' -H "Host: <your-host>" https://127.0.0.1/

# off-box listening ports are exactly 22 and 443
sudo /usr/local/sbin/lemonldap-ng-port-check.sh

# service logs
sudo journalctl -u lemonldap-ng-fastcgi-server -n 50 --no-pager

Troubleshooting

The portal returns HTTP 500 "Unable to protect this server". The message names the Perl class that failed to load, and it is almost always a missing module after a manual package change. Reinstall the runtime dependencies the image ships with, then restart lemonldap-ng-fastcgi-server.

The sign in form accepts the password but nothing happens. Your browser is not storing the session cookie. Check the portal URL in the Manager matches the address you are actually using — see the note on cookies and addresses above.

The Manager returns 403 for a user you believe is an administrator. Manager access is granted to the cloudimg-admins group. Add the user to cn=cloudimg-admins,ou=groups,dc=cloudimg,dc=local, or change the rule under Virtual Hosts.

Everything returns 403 after changing the hostname. The access rules are keyed on the virtual host. If the host you are reaching does not match a configured virtual host the handler fails closed, which is intended. Update the virtual host entry in the Manager.

First boot did not finish. sudo journalctl -u lemonldap-ng-firstboot -n 80 --no-pager and sudo cat /var/log/cloudimg-firstboot.log will show where it stopped.

Support

cloudimg images are maintained and patched on a regular cycle. For image specific questions contact cloudimg support through the Azure Marketplace listing.

For questions about LemonLDAP::NG itself, the upstream project publishes documentation at lemonldap-ng.org — a local copy is also installed on this machine and is reachable from the Manager's help pane.

Licence

LemonLDAP::NG is free software licensed under the GNU General Public License, version 2 or later. The licence text that governs the packages on this image is installed at /usr/share/doc/liblemonldap-ng-portal-perl/copyright, and the full GPL text at /usr/share/common-licenses/GPL-2.

cloudimg charges for the packaging, testing and maintenance of this image, not for LemonLDAP::NG, which remains free software you may obtain and use independently. cloudimg is not affiliated with, endorsed by, or sponsored by the LemonLDAP::NG project or the OW2 consortium.