Overview
These directions walk through the steps to configure and setup, from scratch, an out-of-the-box HPCC platform deployment using LDAP authentication.
The goal is for developers and other technical platform users to have available an LDAP Directory Server (DS) server they can use for testing and troubleshooting authentication and authorization features. These directions don't cover the steps needed for securing a production-grade DS instance and the communication between it and the platform components. Any deployment with this setup should be ephemeral, publicly unreachable, and not have access to sensitive data.
First we will set up a Docker container with a minimal 389ds LDAP Directory Server and a phpLdapAdmin (PLA) client to administer it. The PLA can be used to inspect your DS and perform any administrative tasks not supported by the ECL Watch client.
Then we'll cover the configuration changes to deploy a minimal HPCC platform, both non-containerized (bare-metal) and under Kubernetes, that uses 389ds for security.
Initial State
When the platform launches it ensures the LDAP DS is initialized with the base DN (distinguished name) and OUs (organizational units) needed. Among others, OUs for groups, users and ESP authorization resources are created. If present in the config, an HPCC Admin user is created, and added to the administrator's group.
If you need any other security settings beyond that, such as non-administrative users and groups, you'll need to create them using ECL Watch. The administrator account itself has the privileges to make these kinds of changes. You may see that the admin doesn't by default have all permissions, but it can add any it needs to the admin group.
This setup has been tested with ECL Watch authentication and authorization. It should also work for file and workunit scopes, but that hasn't been confirmed.
Your customized setup will be saved to a persistent volume, and you can separately maintain different versions as needed.
Docker LDAP Directory Service and Admin Setup
We'll run our 389ds and PLA in a Docker container launched from platform source: dockerfiles/examples/ldap.
These instructions create a persistent volume for the DS inside your directory, but you can customize the docker-compose file to use another location. All of the server state is stored in the mounted volume, so if you want to be able to easily switch between different configurations you can easily do so by switching the mounted directory at launch.
Mounted data volume: Throughout this document,
${HOME}/389dsrefers to the host directory mounted into the 389ds container as/data. This is where all server state (database, TLS certificates, configuration) is persisted between restarts. The default is${HOME}/389dsas set indocker-compose.yaml. If you change it there, substitute your chosen path everywhere${HOME}/389dsappears in the commands below.
Copy the
dockerfiles/examples/ldapfolder to a location outside of your repo, say${HOME}/ldap. Edit the file${HOME}/ldap/docker-compose.yaml, customizing the admin password and PVC mount point:- Change
DS_DM_PASSWORD: "<directory_manager_pw>"placeholder with the real password you want to use. This is the password that you will use in PLA to administer the DS, and that the HPCC platform will use to interact with it using LDAP. - Under volumes, replace
${HOME}/389dswith the location of your choice.
- Change
From
${HOME}/ldap, run:bashdocker compose up -dCreate the LDAP backend (suffix) on the Directory Server. This configures the server to manage the
dc=example,dc=comnaming context. This step should be done only once, the first time you run the container with a new directory mounted:bashdocker exec -i -t 389ds /usr/sbin/dsconf localhost backend create --suffix dc=example,dc=com --be-name userRootWhat this does: The
dsconf backend createcommand configures the LDAP server to manage a specific DN subtree (in this casedc=example,dc=com). This is a server-side configuration that tells 389 Directory Server "I will handle queries for this naming context," but it does not create any actual directory entries yet.Verify the backend was created successfully. You should see the response "The database was successfully created". You can also verify by checking that files were created in your mounted volume (
${HOME}/389dsor your configured path from${HOME}/ldap/docker-compose.yaml), or by running this command and confirming thatdc=example,dc=comis listed:bashdocker exec -i -t 389ds /usr/sbin/dsconf localhost backend suffix listNote: At this point, phpLDAPadmin will not yet be able to browse the directory tree because the base DN entry itself doesn't exist. Only the server configuration has been set up.
Initialize the LDAP directory structure by running HPCC Platform for the first time. Before using phpLDAPadmin to manage the directory, you must start the HPCC Platform at least once (see sections below for bare-metal or containerized setup). On first startup, the platform will:
- Detect that the base DN entry (
dc=example,dc=com) is missing - Automatically create the base DN entry and all necessary organizational units (OUs) for users, groups, and resources
- Initialize the HPCCAdministrators group if it is a relative path
- Add an admin user if present in the config
After the platform has initialized the directory structure, you can verify and manage your LDAP server using phpLDAPadmin at
http://localhost:8080. Login with:- username:
cn=Directory Manager - password:
<directory_manager_pw>(as configured in docker-compose.yaml)
You should now see the complete directory tree including
dc=example,dc=comand all HPCC-created organizational units.- Detect that the base DN entry (
Enable TLS on the Test Server (Optional)
By default the 389ds test instance runs on plain LDAP (port 389). If you need to test LDAPS (port 636) with TLS certificate validation, follow these steps after completing the initial setup above. This uses a self-signed CA and server certificate.
Note: This is only needed if you are testing the
ldapTLSValidationHPCC configuration feature. See LDAPCertificateValidation.md for details on that feature.
Step 1 — Generate Certificates
Run these commands on the host (outside Docker). The server certificate must include a Subject Alternative Name (SAN) matching the hostname HPCC uses to connect to 389ds. HPCC's bundled OpenLDAP uses OpenSSL, which requires a SAN — a CN-only certificate will be rejected with a hostname mismatch error even if the CN matches.
Choose a directory to store your certificates and substitute it for <ldap-certs-dir> throughout this section (e.g., ~/hpcc/ldap-certs).
# Create your certificate directory
mkdir -p <ldap-certs-dir> && cd <ldap-certs-dir>
# Generate the CA private key and self-signed CA certificate
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
-subj "/CN=LDAP-CA/O=Example/C=US"
# Generate the server private key
openssl genrsa -out server.key 2048
# Generate a CSR for the server with a SAN covering localhost
openssl req -new -key server.key -out server.csr \
-subj "/CN=localhost/O=Example/C=US" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
# Sign the server certificate with the CA, preserving the SAN
openssl x509 -req -days 3650 -in server.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-extfile <(echo "subjectAltName=DNS:localhost,IP:127.0.0.1") \
-out server.crt
# Verify the SAN is present
openssl x509 -in server.crt -noout -text | grep -A2 "Subject Alternative"Hostname match: The SAN in the server certificate must match the value HPCC uses to connect to 389ds. This value comes from the
netAddressof theHardware/Computerentry that theLDAPServerProcessinstance points to inenvironment.xml— not thenetAddresson theInstanceelement itself.
netAddress="127.0.0.1"→ SAN must includeIP:127.0.0.1(as in the example above). This is the recommended setting for local development. Do not usenetAddress="localhost"— OpenLDAP's DNS hostname matching has a known issue with the literal stringlocalhostand will report a hostname mismatch even when the cert includesDNS:localhostin its SAN. Using127.0.0.1triggers the IP address matching path, which works correctly.netAddress="."→ expands to the host machine's primary IP at runtime (e.g.10.0.0.98). The SAN must then cover that IP:subjectAltName=IP:10.0.0.98,DNS:localhostDo not change the existing shared
Hardware/Computerentry'snetAddressto accommodate LDAP — that entry is used by all platform components. Instead add a dedicatedComputerentry for the LDAP instance (see the bare-metal configuration section below for details).
CA cert file location: The
ldapCACertFilepath must be readable by thehpccuser. A file under your home directory may be inaccessible if the home directory has restricted permissions (drwxr-x---), even if the file itself is world-readable. Place the CA cert in a shared location:bashsudo mkdir -p /etc/HPCCSystems/certs sudo cp <ldap-certs-dir>/ca.crt /etc/HPCCSystems/certs/ldap-ca.crt sudo chown root:hpcc /etc/HPCCSystems/certs/ldap-ca.crt sudo chmod 640 /etc/HPCCSystems/certs/ldap-ca.crtThen set
ldapCACertFile="/etc/HPCCSystems/certs/ldap-ca.crt"inenvironment.xml.
Step 2 — Install the Certificate on 389ds
389ds does not read PEM files directly — it stores TLS material in an NSS certificate database. The CA cert is imported with certutil, and the server cert + private key must be bundled into a PKCS12 file first, then imported with pk12util.
Nickname: Choose a nickname for your server certificate (e.g.,
HPCC-389ds-cert). The-namevalue in theopenssl pkcs12command sets this nickname and must be used exactly in thedsconf rsa set --nss-cert-namecommand that follows. It is case-sensitive.
# Create the TLS subdirectory in the mounted data volume
mkdir -p ${HOME}/389ds/tls
# Copy certificates into the mounted data volume
cp <ldap-certs-dir>/server.crt <ldap-certs-dir>/server.key <ldap-certs-dir>/ca.crt ${HOME}/389ds/tls/
# Import the CA certificate into the NSS database
# The NSS database is password-protected — pwdfile.txt holds the PIN
docker exec 389ds certutil -A \
-d /etc/dirsrv/slapd-localhost \
-n "LDAP-CA" \
-t "CT,," \
-i /data/tls/ca.crt \
-f /etc/dirsrv/slapd-localhost/pwdfile.txt
# Bundle the server certificate and private key into a PKCS12 file
# The -name value becomes the NSS nickname — it must match --nss-cert-name below
openssl pkcs12 -export \
-in <ldap-certs-dir>/server.crt -inkey <ldap-certs-dir>/server.key \
-out <ldap-certs-dir>/server.p12 \
-passout pass:"" \
-name "HPCC-389ds-cert"
# Copy the bundle into the mounted volume
cp <ldap-certs-dir>/server.p12 ${HOME}/389ds/tls/
# Import the server certificate and private key into the NSS database
docker exec 389ds pk12util \
-i /data/tls/server.p12 \
-d /etc/dirsrv/slapd-localhost \
-W "" \
-k /etc/dirsrv/slapd-localhost/pwdfile.txt
# Verify both certificates appear in the NSS database
docker exec 389ds certutil -L -d /etc/dirsrv/slapd-localhost
# Expected output (or similar):
# Certificate Nickname Trust Attributes
# SSL,S/MIME,JAR/XPI
#
# LDAP-CA CT,,
# HPCC-389ds-cert u,u,u
# Tell 389ds which certificate to present for TLS
# Must match the -name value used in the openssl pkcs12 command above
docker exec 389ds dsconf localhost security rsa set \
--nss-cert-name "HPCC-389ds-cert"
# Enable TLS on the directory server
docker exec 389ds dsconf localhost security enable
# Restart the container for TLS to take effect
docker restart 389dsThe TLS configuration is persisted in the mounted volume (${HOME}/389ds). Subsequent docker compose up runs will have TLS enabled without repeating these steps.
Step 3 — Verify TLS is Working
openssl s_client -connect localhost:636 -CAfile <ldap-certs-dir>/ca.crt </dev/null 2>&1 | grep -E "subject|issuer|Verify return"A successful result shows Verify return code: 0 (ok), with subject matching your server cert CN and issuer matching your CA.
Note:
openssl s_clientwithout</dev/nullwill hang waiting for stdin. Always redirect stdin to avoid this.
Step 4 — Configure HPCC to Validate the Certificate
Copy ca.crt to a stable path accessible by the hpcc user on each HPCC node:
sudo mkdir -p /etc/HPCCSystems/certs
sudo cp <ldap-certs-dir>/ca.crt /etc/HPCCSystems/certs/ldap-ca.crt
sudo chown root:hpcc /etc/HPCCSystems/certs/ldap-ca.crt
sudo chmod 640 /etc/HPCCSystems/certs/ldap-ca.crtNote: A file under your home directory may be inaccessible to the
hpccuser if the home directory has restricted permissions (drwxr-x---), even if the file itself is world-readable. Use a shared location as shown above.
In environment.xml, add ldapTLSValidation and ldapCACertFile to the <ldap> element:
<ldap ldapTLSValidation="strict"
ldapCACertFile="/etc/HPCCSystems/certs/ldap-ca.crt"
... />For a Kubernetes deployment, deliver the CA cert via a Secret or ConfigMap and mount it into the HPCC pods, then set the path in your Helm values:
ldap:
ldapTLSValidation: "strict"
ldapCACertFile: "/etc/hpcc/certs/ldap-ca.crt"Use ldapTLSValidation="permissive" first to diagnose any issues without hard-failing, then switch to "strict" once connectivity is confirmed.
Enable Password Expiration on the Test Server (Optional)
By default, 389ds does not enforce a password expiration policy — passwords never expire. If you need to test HPCC's handling of expired 389ds passwords (tracked in GH#36810), you must explicitly enable password policy on the test server.
Note: This is only needed if you are testing password expiration behavior. It has no effect on any other part of the setup and can be skipped otherwise.
Enable the Global Password Policy
Run these commands against your running 389ds container:
# Turn on password expiration and set the max age (in seconds).
# 600 seconds (10 minutes) is short enough to observe expiration without a long wait,
# while still leaving enough time to complete a manual test pass. Use a realistic value
# (e.g. 7776000 = 90 days) for anything other than active testing.
docker exec 389ds dsconf localhost pwpolicy set --pwdexpire on --pwdmaxage 600
# Optional: control how long before expiration the server starts warning on bind,
# and how many post-expiration "grace" binds are allowed before lockout.
docker exec 389ds dsconf localhost pwpolicy set --pwdwarning 300 --pwdgracelimit 3
# Verify the current policy
docker exec 389ds dsconf localhost pwpolicy getUnits: 389ds password policy durations (
--pwdmaxage,--pwdwarning) are in seconds, unlike Active Directory'smaxPwdAge(100-nanosecond intervals). Don't reuse AD-style values here.
Force an Existing Test User's Password to Expire Immediately
The passwordExpirationTime attribute is computed from the password's last-changed time plus --pwdmaxage, so an already-set password won't reflect a newly-enabled or newly-changed policy until it is changed again. To make an existing user's password expire immediately for testing, either:
Set a very small
--pwdmaxage(as above) and wait for it to elapse, orReset the user's password, which causes 389ds to recompute
passwordExpirationTimeusing the current policy. You can do this via ECL Watch's "Reset Password" admin function, or directly against the test server:bashcat <<EOF | docker exec -i 389ds ldapmodify -x -D "cn=Directory Manager" -w <directory_manager_pw> dn: uid=<test-username>,ou=users,ou=ecl,dc=example,dc=com changetype: modify replace: userPassword userPassword: <new-password> EOF
Verifying the Policy Took Effect
Use phpLDAPadmin (or ldapsearch) to inspect a test user's entry and confirm the passwordExpirationTime operational attribute is now present:
docker exec 389ds ldapsearch -x -D "cn=Directory Manager" -w <directory_manager_pw> \
-b "dc=example,dc=com" "(uid=<test-username>)" passwordExpirationTimephpLDAPadmin note:
passwordExpirationTimeis an operational attribute, so it is hidden on a user entry's default view. Open the entry and click "Show internal attributes" (near the bottom of the page) to reveal it.
A user whose password has not yet expired will show a passwordExpirationTime value in generalized-time format (e.g. 20260101120000Z, in UTC/GMT). If the attribute is absent, double check that --pwdexpire is on and that the user's password was set (or reset) after the policy was enabled.
Scope of the Policy
The commands above set the global default policy (cn=config), which applies to all users unless a local (per-subtree) policy overrides it. This is sufficient for most test scenarios. See the 389ds password policy documentation for configuring local policies scoped to a specific OU if your testing requires different policies for different users.
Note:
cn=Directory Manager(the root DN / superuser) is exempt from password policy by design — it will never show apasswordExpirationTime, even with the policy enabled. Use a regular test user to validate expiration behavior.
Disabling Password Expiration
To turn expiration testing back off without rebuilding the server, disable the global policy:
docker exec 389ds dsconf localhost pwpolicy set --pwdexpire offNote: Disabling the policy stops new expirations from being computed, but does not retroactively clear a
passwordExpirationTimealready stored on a test user — resetting the user's password with the policy off does not clear it either (verified: the old value is left untouched). To remove a stalepasswordExpirationTimefrom a test user, delete the attribute directly:bashcat <<EOF | docker exec -i 389ds ldapmodify -x -D "cn=Directory Manager" -w <directory_manager_pw> dn: uid=<test-username>,ou=users,ou=ecl,dc=example,dc=com changetype: modify delete: passwordExpirationTime EOF
Managing the Test Server Instance
Assuming stock values from our example, the state for the Directory Server is stored ${HOME}/389ds. If you want to wipe out your server and start fresh, delete the contents of ${HOME}/389ds and begin again with the setup above from step 2.
If you'd like to create an additional server with different settings, then:
Copy the
dockerfiles/examples/ldapfolder to a location outside of your repo to a new unique location, say${HOME}/ldap-alternate. Edit the file${HOME}/ldap-alternate/docker-compose.yaml, customizing the admin password and PVC mount point:- Change
DS_DM_PASSWORD: "<directory_manager_pw>"placeholder with the real password you want to use. This is the password that you will use in PLA to administer the DS, and that the HPCC platform will use to interact with it using LDAP. - Under volumes, replace
${HOME}/389dswith another new unique folder to hold the server state, say${HOME}/389ds-alternate.
- Change
From
${HOME}/ldap-alternate, run:bashdocker compose up -d
Then continue with steps 3-5 above, substituting your new ldap-alternate and 389ds-alternate locations where appropriate. Now you have a second instance you can run instead of the first and its state is stored in the 389ds-alternate folder.
Bare-Metal Platform + Docker LDAP Setup
These directions assume you're starting with a vanilla configuration from a fresh install or build. If you're not, then adjust the component names to match your config.
Configure the Platform
Create LDAP Server Process, keeping the default name "ldapserver"
- Navigate to the Attributes tab and change these properties:
adminGroupName = HPCCAdministratorsserverType = 389DirectoryServermodulesBasedn = ou=SMC,ou=espservices,ou=eclsystemBasedn = ou=example,ou=comsystemCommonName = Directory ManagersystemPassword = <directory_manager_pw>systemUser = Directory Manager
- Navigate to the Instances tab:
- Add an instance on:
computer = ldap-localnetAddress = 127.0.0.1
- Add an instance on:
TLS note: If using LDAPS with certificate validation (
ldapTLSValidation), the address HPCC connects to is determined by thenetAddressof theComputerentry in theHardwaresection ofenvironment.xmlthat the LDAP instance'scomputerattribute points to — not thenetAddresson theInstanceelement itself. ThatHardware/ComputernetAddressmust match the CN or SAN in the server certificate.Important: Do not simply change the existing
Hardware/Computerentry'snetAddressfrom"."to"localhost"— that entry is shared by all platform components and changing it will break them (e.g. dfuserver). Instead, add a separateComputerentry in theHardwareblock dedicated to the LDAP instance:xml<!-- In the <Hardware> block — add this alongside the existing Computer entry --> <Computer computerType="linuxmachine" domain="localdomain" name="ldap-local" netAddress="127.0.0.1"/>Then point the
LDAPServerProcessinstance at it:xml<Instance computer="ldap-local" name="s1" netAddress="."/>This writes
ldapAddress="127.0.0.1"into the generated component config, matching theIP:127.0.0.1SAN in the server certificate. See Step 1 of the TLS setup for cert SAN requirements.- Navigate to the Attributes tab and change these properties:
In the component "Esp - myesp", navigate to the Authentication tab and change these properties:
ldapServer = ldapservermethod = ldap
Hand-edit the active
environment.xmlfile. Find the<LDAPServerProcess>element and add the attributehpccAdminSecretKey="myhpccadminsecretkey"
Notes
systemCommonNameandsystemUsermust match and they must match the name of the 'admin' account of the 389ds server. By default this is "Directory Manager".- The
modulesBasednis changed here to match the location in the DS where the SmcAccess permission is located. On initialization the secmgr creates the HPCC Admin and gives it SmcAccess to be able to login to ECL Watch. adminGroupNamevalue affects where the group is created in the DS hierarchy, so don't use the special values "Administrators" or "Directory Administrators" unless you know that's what you need. The suggested value "HPCCAdministrators" will work well for development and testing.systemBasednis the root of the DS tree used by the platform. It must match the backend suffix used in step #3 when configuring the 389ds server.systemPasswordmust match the 389dsDS_DM_PASSWORDconfigured in thedockerfiles/examples/ldap/docker-compose.yamlfile.
Add Secret for HPCC Admin User
Next you must add the myhpccadminsecretkey to your HPCC platform deployment. If there are concerns about storing these credentials on disk you should remove this secret directory when not in use, or manage it with an encryption/decryption utility such as gocryptfs
First note the root of your platform install location, which we'll refer to as <HPCC_ROOT>. Package installs will be rooted in / but dev installs are typically located at $HOME/runtime.
Create all directories in this path if they don't already exist:
<HPCC_ROOT>/opt/HPCCSystems/secrets/authn/myhpccadminsecretkeyInside
myhpccadminsecretkeycreate two text files with no extra whitespace and no terminal newline:- A file named
usernamecontaininghpcc_admin - A file named
passwordcontaining the password you want the hpcc admin user to have. We'll refer to it as<hpcc_admin_pw>
- A file named
Run and Use Platform
Start up the platform:
bashhpcc-init startLogin to ECL Watch at http://127.0.0.1/8010 using the credentials:
- user:
hpcc_admin - password:
<hpcc_admin_pw>(that you added to the secret above)
- user:
Initially your admin will only have SmcAccess, so you may see some access failure warnings, but you can navigate to the Topology | Security tab to customize the HPCCAdministrators permissions and add any other users, groups or permissions needed for testing. These warnings could also be due to permissions caching, and may be resolved after a platform restart.
Containerized Platform + Docker LDAP
This containerized deployment enables ldap auth for ECL Watch only, but it can be extended easily to other ESP services.
Create k8s Secrets for Platform
You'll need two k8s secrets, one for the HPCC administrator and another for the LDAP server administrator. Once created they'll persist in the Kubernetes backing store.
- Create secret for HPCC administrator:bash
kubectl create secret generic myhpccadminsecretkey --from-literal=username=hpcc_admin --from-literal=password=<hpcc_admin_pw> - Create secret for the LDAP server administrator:bash
kubectl create secret generic admincredssecretname --from-literal="username=Directory Manager" --from-literal=password=<directory_manager_pw>
Customize Platform Helm Values
We'll be using the customized Helm values file at helm/examples/ldap/hpcc-values.yaml. It will run all standard ESP services, but it uses ldap authentication only for the eclwatch service. Customize this file further as needed.
Notes
- The secrets values are case sensitive and must match what was created as k8s secrets. The supplied values work and match across instructions and files:
admincredsmountname: "admincredssecretname"
hpccadmincredsmountname: "myhpccadminsecretkey"systemBasednis the root of the DS tree used by the platform. It must match the backend suffix used in step #3 when configuring the 389ds server.- LDAP auth is only configured for the eclwatch service. Copy and paste the
authandldapsections from there to any other ESP service you want to use ldap.
Run and Use Platform
Run from the root of your platform source, or provide an absolute path to the Helm values file used below.
- Start up the platform:
helm install mycluster hpcc/hpcc -f helm/examples/ldap/hpcc-values.yaml- Login to ECL Watch at http://127.0.0.1/8010 using the credentials:
- user:
hpcc_admin - password:
<hpcc_admin_pw>Created in step Add secret for HPCC Admin user
- user:
Initially your admin will only have SmcAccess, so you may see some access failure warnings, but you can navigate to the Topology | Security tab to customize the HPCCAdministrators permissions and add any other users, groups or permissions needed for testing. These warnings could also be due to permissions caching, and may be resolved after a platform restart.