Configure LDAP / Active Directory¶
MFCloud supports an LDAP / AD directory as a second auth source alongside local accounts. Local accounts always try first — a directory outage cannot lock you out.
Domain controller-side setup (Windows AD)¶
Before touching the MFCloud UI, the domain needs:
- A dedicated low-privilege bind account — never point Service Bind DN at a Domain Admin. It only needs to read user/group objects, which any authenticated domain account can do by default.
- A trusted LDAPS certificate — if your CA is an internal/enterprise CA (the common case for AD Certificate Services), MFCloud's app container needs that CA's root cert in its trust store.
core/ldap_auth.pyalways validates withssl.CERT_REQUIRED— it will not fall back to an unverified connection.
Configure-MFCloudLDAP.ps1 automates both. Run it as Administrator on the domain controller (or any domain-joined host with the ActiveDirectory RSAT module):
.\Configure-MFCloudLDAP.ps1
# or with a specific account name / OU:
.\Configure-MFCloudLDAP.ps1 -ServiceAccountName svc_mfconsole -OUPath "OU=Service Accounts,DC=corp,DC=local"
It creates the service account (prompting for a password that meets the domain's complexity policy — it will not invent one for you), exports the local CA's root certificate to a .crt file next to the script, and prints the exact values to paste into the Directory Services form below.
Use the DC's FQDN, not its IP
The LDAPS certificate's SAN covers hostnames (e.g. dc1.corp.local), not IP addresses. ldaps://192.168.x.x:636 will fail hostname verification even with a fully trusted CA — always use the FQDN in LDAP Server URI.
Trusting the CA cert in mfcloud-master¶
Copy the exported .crt file into mfcloud-master/certs/ and wire it into the image build (already done in the shipped Dockerfile — this is what it looks like):
RUN dnf install -y ... ca-certificates && dnf clean all
COPY certs/your-ca.crt /etc/pki/ca-trust/source/anchors/your-ca.crt
RUN update-ca-trust extract
Then rebuild and restart app, worker, and beat so the new trust store takes effect. Without this step, "Test Connection" fails with a TLS verification error even though the account/URI/search base are all correct.
Quick steps¶
- Settings → Directory Services (Active Directory / LDAP).
- Toggle Enable directory authentication.
- Fill in:
- LDAP Server URI —
ldaps://dc1.corp.local:636(preferred) orldap://...+ Use TLS = Yes for StartTLS. - Search Base — your domain root, e.g.
DC=corp,DC=local. - Service Bind DN — a low-privilege service account, e.g.
CN=svc-mfcloud,OU=Service Accounts,DC=corp,DC=local. - Service Bind Password — the service account password.
- User Search Filter — defaults to
(sAMAccountName={username})for AD; for OpenLDAP use(uid={username}). - Default Domain (NetBIOS) — used when the operator types just
usernamewithoutDOMAIN\. - Default Role — the MFCloud role assigned when no group mapping matches.
- Use TLS / StartTLS — Yes (lab-only exception: No).
- 🔌 Test Connection — binds with the service account, confirms read access.
- Save Directory Settings.
After save, any sign-in form post follows this order:
1. Try LOCAL with the submitted username/password — wins if local user exists.
2. If local fails AND LDAP enabled → bind to LDAP as service account, look up
the user, then try a user bind with their submitted password.
3. On LDAP success → auto-provision/refresh a local mirror row in the users
table with auth_provider='ad'.
Group → Role mapping¶
By default an LDAP user lands in the Default Role. To map group membership to a specific role:
- In the same Directory Services card, + Add Mapping.
- Group DN — paste the full DN (e.g.
CN=Domain Admins,CN=Users,DC=corp,DC=local). - MFCloud Role — pick from the dropdown (Super Admin, Compute Admin, etc.).
- Priority — lower number checked first. Default 100 is fine; use 10 for "admin override" mappings.
- Save Group Mappings.
At login MFCloud walks the mappings in priority order; first match wins. Users in no mapped group fall through to the Default Role.
Removing LDAP¶
Untick Enable and Save. Existing mirrored users keep their local rows (so their session JWTs stay valid), but no new LDAP logins are attempted.
Audit¶
Failed binds are logged to journalctl -u app on the master with the LDAP error verbatim — easy to spot a stale service-account password.
Common gotcha: login silently fails after a successful Test Connection¶
If 🔌 Test Connection succeeds but real logins still return "Incorrect credentials," check the User Search Filter field for stray placeholder text. It's easy to end up with something like:
The trailing (default) makes the filter invalid LDAP syntax, so the directory server rejects the search with invalid matching assertion — ldap_auth.authenticate() catches that internally and just returns "no match," which looks identical to a wrong password from the login screen. Fix: the field must contain exactly (sAMAccountName={username}), nothing appended.
See also¶
- Configure OIDC / SSO — the third auth path.
- Default Ports — 389 and 636 for LDAP.