LDAP
(→Top level structure) |
(→How do I search LDAP ?: no access to formey required) |
||
| (81 intermediate revisions by 6 users not shown) | |||
| Line 1: | Line 1: | ||
| − | + | == LDAP in Production == | |
| − | + | This section is the short version of how LDAP works in production for mail. | |
| − | + | === server and process === | |
| + | * sanger is the host serving LDAP. Verify this is not out of date by checking | ||
| + | ** the MX record for wikimedia.org is mchenry | ||
| + | ** on mchenry, in <code>/etc/exim4/exim4.conf</code>, the variable <code>ldap_default_servers</code> is the ldap server | ||
| + | * opendj is the LDAP server process. Restart it with <code>/etc/init.d/opendj restart</code> | ||
| − | + | === configuration === | |
| + | * opendj is configured to listen on ports 4444, 8989, 1636, 1389. | ||
| + | * The list of IP addresses to listen on are defined by puppet and stored in /etc/default/opendj (this file is read by the init script) | ||
| + | * iptables forwards ports 389 and 636 to opendj on port 1389 and 1636. | ||
| + | ** examine the iptables rules with <code>iptables -nvL -t nat</code>. It should look like this: | ||
| + | root@sanger:~# iptables -nvL -t nat | ||
| + | Chain PREROUTING (policy ACCEPT 17402 packets, 1086K bytes) | ||
| + | pkts bytes target prot opt in out source destination | ||
| + | 0 0 REDIRECT tcp -- * * 0.0.0.0/0 127.0.0.1 tcp dpt:389 redir ports 1389 | ||
| + | 0 0 REDIRECT tcp -- * * 0.0.0.0/0 127.0.0.1 tcp dpt:636 redir ports 1636 | ||
| + | 1502 90120 REDIRECT tcp -- * * 0.0.0.0/0 208.80.152.187 tcp dpt:389 redir ports 1389 | ||
| + | 12 720 REDIRECT tcp -- * * 0.0.0.0/0 208.80.152.187 tcp dpt:636 redir ports 1636 | ||
| + | Chain POSTROUTING (policy ACCEPT 56949 packets, 4008K bytes) | ||
| + | pkts bytes target prot opt in out source destination | ||
| + | Chain OUTPUT (policy ACCEPT 56949 packets, 4008K bytes) | ||
| + | pkts bytes target prot opt in out source destination | ||
| + | 0 0 DNAT tcp -- * * 0.0.0.0/0 127.0.0.1 tcp dpt:389 to::1389 | ||
| + | 0 0 DNAT tcp -- * * 0.0.0.0/0 127.0.0.1 tcp dpt:636 to::1636 | ||
| + | 0 0 DNAT tcp -- * * 0.0.0.0/0 208.80.152.187 tcp dpt:389 to::1389 | ||
| + | 0 0 DNAT tcp -- * * 0.0.0.0/0 208.80.152.187 tcp dpt:636 to::1636 | ||
| + | === troubleshooting === | ||
| + | If mail is not getting delivered because LDAP is broken, you'll see errors like this in the primary mail server (mchenry as of 2012/03)<code>/var/log/exim4/mainlog: | ||
| + | 2012-03-29 18:00:00 H=lists.wikimedia.org [2620:0:861:1::2]:53845 I=[2620:0:860:2:219:b9ff:fedd:c027]:25 F=<> temporarily rejected RCPT <foobarbaz@wikimedia.org>: failed to bind the LDAP connection to server sanger.wikimedia.org:389 - ldap_bind() returned -1 | ||
| + | </code> | ||
| + | In this case you should try and restart opendj on sanger and check | ||
| + | * opendj is listening with <code>netstat -neapl | grep LISTEN\ </code> | ||
| + | * the iptables rules are loaded with <code>iptables -nvL -t nat</code> | ||
| − | + | == Installing/Configuring the server == | |
| − | == | + | === Install required packages === |
| − | + | ||
| − | + | ||
| − | + | <source lang=bash> | |
| + | apt-get install openjdk-6-jre ldap-utils opendj | ||
| + | </source> | ||
| − | == | + | ==== OpenDJ package information ==== |
| − | + | Basic package file layout: | |
| − | + | ;/etc/opendj/instance.loc | |
| + | :Points to the instance location | ||
| + | ;/usr/opendj | ||
| + | :Binaries only; only the package should ever modify this | ||
| + | ;/var/opendj/instance | ||
| + | :Actual instance, owned by opendj:opendj | ||
| + | ;/var/opendj/instance/config/schema | ||
| + | :Where custom schema goes | ||
| − | + | You can change the instance location by moving the directory, and updating /etc/opends/instance.loc. | |
| − | + | The service is run as opendj:opendj. It adds iptables rules on start to forward 389 to 1389 and 636 to 1636. It removes the rules on service stop. Right now the init script isn't doing checks to see if the rules are already added, so it may add them more than once, if start is run more than once. I'll fix this in the next package version. The rules can be flushed using: | |
| − | + | <source lang=bash> | |
| + | iptables -F -t nat | ||
| + | </source> | ||
| − | == | + | === Create a PKCS12 certificate === |
| + | It is easier to install a PKCS12 certificate in the directory server, than a PKCS11 cert/key; here's how to create one from the PKCS11 cert/key pair: | ||
| − | = | + | <source lang=bash> |
| + | openssl pkcs12 -export -in pkcs11.cert -inkey pkcs12.key -out pkcs12.p12 | ||
| + | </source> | ||
| − | ==== Install | + | Remember what you enter for the passkey, you'll need it later when adding it to the server. |
| + | |||
| + | === Initial instance configuration === | ||
| + | |||
| + | # Change user to opendj: su - opendj | ||
| + | #* All opendj commands will require you to do this | ||
| + | # Run /usr/opendj/setup | ||
| + | #* '''Ensure you use the FQDN for any hostnames requested!''' | ||
| + | #* Enable SSL support during the install | ||
| + | #* Install the PKCS12 certificate during the install (it's a pain later) | ||
| + | # Start the service: /etc/init.d/opendj start | ||
| + | # Ensure the service starts on boot: update-rc.d opendj defaults | ||
| + | |||
| + | === Add the CA trust to the truststore === | ||
<source lang=bash> | <source lang=bash> | ||
| − | + | su - opendj | |
| + | cd instance/config | ||
| + | keytool -importcert -trustcacerts -alias "wmf-ca" -file /etc/ssl/certs/wmf-ca.pem -keystore truststore -storepass `cat keystore.pin` | ||
</source> | </source> | ||
| − | ==== | + | === Change the administration connector's certificate provider === |
| + | |||
| + | ==== Using dsconfig interactively ==== | ||
| + | |||
| + | # Run dsconfig (as opendj) | ||
| + | # Select "Administration Connector" | ||
| + | # Select "View and edit the Administration Connector" | ||
| + | # Select "key-manager-provider" | ||
| + | # Select "Change it to the Key Manager Provider: PKCS12" | ||
| + | # Select "ssl-cert-nickname" | ||
| + | # Select "Change the value" | ||
| + | # Type in the certificate alias | ||
| + | #* If you didn't use "-name" when creating the pkcs12 file, this is "1"; otherwise, it is the value of "-name" | ||
| + | # Select "finish" | ||
| + | # Select "quit" | ||
| + | # Restart opendj | ||
| + | |||
| + | ==== Using dsconfig non-interactively ==== | ||
| + | |||
| + | <source lang=bash> | ||
| + | dsconfig set-administration-connector-prop \ | ||
| + | --set key-manager-provider:PKCS12 \ | ||
| + | --set ssl-cert-nickname:1 \ | ||
| + | --set trust-manager-provider:JKS \ | ||
| + | --hostname <hostname> \ | ||
| + | --port 4444 \ | ||
| + | --trustStorePath /var/opendj/instance/config/admin-truststore \ | ||
| + | --bindDN cn=Directory\ Manager \ | ||
| + | --bindPassword ****** \ | ||
| + | --no-prompt | ||
| + | </source> | ||
| + | |||
| + | === Add the sudoers schema === | ||
| + | |||
| + | <source lang=bash> | ||
| + | cp /usr/share/doc/sudo-ldap/schema.iPlanet /var/opendj/instance/config/schema/98sudo.ldif | ||
| + | /etc/init.d/opendj restart | ||
| + | </source> | ||
| + | |||
| + | * Note: this assumes the sudo-ldap package is installed. If it is not, you'll need to get this file from elsewhere. | ||
| + | * Additional note: This is also assumed to be handled by puppet. | ||
| + | |||
| + | === Disable anonymous read access === | ||
| + | |||
| + | This must be done on each server. | ||
| + | |||
| + | ==== Using dsconfig ==== | ||
| + | |||
| + | Change the following aci: | ||
| + | |||
| + | <source lang=text> | ||
| + | (targetattr!="userPassword||authPassword")(version 3.0; acl "Anonymous read access"; allow (read,search,compare) userdn="ldap:///anyone";) | ||
| + | </source> | ||
| + | |||
| + | to: | ||
| + | |||
| + | <source lang=text> | ||
| + | (targetattr!="userPassword||authPassword")(version 3.0; acl "Authenticated read access"; allow (read,search,compare) userdn="ldap:///all";) | ||
| + | </source> | ||
| + | |||
| + | This causes the directory server to require authentication for read access. | ||
| + | |||
| + | To do this, as opendj, use dsconfig: | ||
| + | |||
| + | # Select the "Access Control Handler" | ||
| + | # Select "View and edit the Access Control Handler" | ||
| + | # Select the "global-aci" | ||
| + | # Select "Remove one or more values" | ||
| + | # Select the aci to be removed | ||
| + | # Select "Add one or more values", add the new entry, hit enter | ||
| + | # Select "Use these values" | ||
| + | # Select "finish - apply..." | ||
| + | # quit | ||
| + | |||
| + | For more info, see the [https://www.opends.org/wiki/page/ManagingGlobalAcis OpenDS documentation on this]. | ||
| + | |||
| + | ==== Using ldapsearch/ldapmodify ==== | ||
| + | |||
| + | Put the current global ACIs into an ldif: | ||
| + | |||
| + | <source lang=bash> | ||
| + | ldapsearch -LLL -x -D 'cn=directory manager' -H ldaps://<servername>:636 -W -b 'cn=config' 'cn=Access Control Handler' ds-cfg-global-aci > global-aci.ldif | ||
| + | </source> | ||
| + | |||
| + | Add the following after the DN line: | ||
| + | |||
| + | <source lang=text> | ||
| + | changetype: modify | ||
| + | replace: ds-cfg-global-aci | ||
| + | </source> | ||
| + | |||
| + | Change the following aci: | ||
| + | |||
| + | <source lang=text> | ||
| + | (targetattr!="userPassword||authPassword")(version 3.0; acl "Anonymous read access"; allow (read,search,compare) userdn="ldap:///anyone";) | ||
| + | </source> | ||
| + | |||
| + | to: | ||
| + | |||
| + | <source lang=text> | ||
| + | (targetattr!="userPassword||authPassword")(version 3.0; acl "Authenticated read access"; allow (read,search,compare) userdn="ldap:///all";) | ||
| + | </source> | ||
| + | |||
| + | Submit the modifications: | ||
| + | |||
| + | <source lang=bash> | ||
| + | ldapmodify -x -D 'cn=directory manager' -H ldaps://<servername>:636 -W -f global-aci.ldif | ||
| + | </source> | ||
| + | |||
| + | === Create indexes === | ||
| + | |||
| + | To create the indexes, on each server run: | ||
| + | |||
| + | <source lang=bash> | ||
| + | su - opendj | ||
| + | create-nis-indexes "<basedn>" /var/tmp/indexes.cmds | ||
| + | dsconfig -n -X -h localhost -D "cn=directory manager" -w <passwd> -F /var/tmp/indexes.cmds | ||
| + | </source> | ||
| + | |||
| + | After creating the indexes, on each server you need to stop the server, rebuild the indexes, and start the server: | ||
| + | |||
| + | <source lang=bash> | ||
| + | /etc/init.d/opendj stop | ||
| + | su - opendj | ||
| + | rebuild-index --rebuildAll -b '<basedn>' | ||
| + | exit | ||
| + | /etc/init.d/opendj start | ||
| + | </source> | ||
| + | |||
| + | === Enable UID Unique Attribute plugin === | ||
| + | |||
| + | We want uid and uidnumber attributes to always be unique and for the server to reject any mod operations that would make them not unique. Run the following command on each server to enable this plugin and add the uidnumber attribute to it: | ||
| + | |||
| + | <source lang=bash> | ||
| + | dsconfig set-plugin-prop \ | ||
| + | --plugin-name UID\ Unique\ Attribute \ | ||
| + | --set enabled:true \ | ||
| + | --add type:uidnumber \ | ||
| + | --hostname <servername> \ | ||
| + | --port 4444 \ | ||
| + | --trustStorePath /var/opendj/instance/config/truststore \ | ||
| + | --bindDN cn=Directory\ Manager \ | ||
| + | --bindPassword ****** \ | ||
| + | --no-prompt | ||
| + | </source> | ||
| + | |||
| + | The above command will fail if the indexes haven't been added yet. | ||
| + | |||
| + | === Enable referential integrity === | ||
| + | |||
| + | When users are delete, we want references to them to be deleted as well. Run this command on each server to enable this: | ||
| + | |||
| + | <source lang=bash> | ||
| + | dsconfig set-plugin-prop \ | ||
| + | --plugin-name Referential\ Integrity \ | ||
| + | --set enabled:true \ | ||
| + | --hostname <servername> \ | ||
| + | --port 4444 \ | ||
| + | --trustStorePath /var/opendj/instance/config/truststore \ | ||
| + | --bindDN cn=Directory\ Manager \ | ||
| + | --bindPassword ****** \ | ||
| + | --no-prompt | ||
| + | </source> | ||
| + | |||
| + | === Enable membership retrieval for virtual static groups === | ||
| + | |||
| + | A number of features we want require virtual static groups. Run this command to enable it: | ||
| + | |||
| + | <source lang=bash> | ||
| + | dsconfig set-virtual-attribute-prop \ | ||
| + | --port 4444 \ | ||
| + | --hostname <servername> \ | ||
| + | --bindDN "cn=Directory Manager" \ | ||
| + | --bindPassword ****** \ | ||
| + | --name "Virtual Static member" \ | ||
| + | --set allow-retrieving-membership:true \ | ||
| + | --no-prompt | ||
| + | </source> | ||
| − | + | === Create a Basic Directory Information Tree (DIT) === | |
| − | + | ==== Top level structure ==== | |
Our aim is for a DIT that is as flat as possible. Adding hierarchy adds complexity, and isn't as flexible as one would first imagine. Using attributes to imply hierarchy is more effective. Below is a basic OU and automount hierarchy: | Our aim is for a DIT that is as flat as possible. Adding hierarchy adds complexity, and isn't as flexible as one would first imagine. Using attributes to imply hierarchy is more effective. Below is a basic OU and automount hierarchy: | ||
| Line 110: | Line 344: | ||
</source> | </source> | ||
| − | + | ==== Object classes and attributes for objects ==== | |
| − | + | ===== Users ===== | |
Objectclasses: | Objectclasses: | ||
| Line 121: | Line 355: | ||
* posixaccount | * posixaccount | ||
* shadowaccount | * shadowaccount | ||
| + | * ldappublickey | ||
Attributes: | Attributes: | ||
| Line 135: | Line 370: | ||
** loginshell | ** loginshell | ||
** description | ** description | ||
| + | ** sshpublickey | ||
In practice, loginshell should always be defined. | In practice, loginshell should always be defined. | ||
| − | + | ===== Groups ===== | |
Objectclasses: | Objectclasses: | ||
* top | * top | ||
| + | * groupofuniquenames | ||
* posixgroup | * posixgroup | ||
| Line 151: | Line 388: | ||
** gidnumber | ** gidnumber | ||
* Optional: | * Optional: | ||
| + | ** uniquemember | ||
** memberuid | ** memberuid | ||
** description | ** description | ||
| − | + | * Note: uniquemember should be used, not memberuid. uniquemember is a multi-value attributes that uses DNs for members. | |
| − | + | ===== Netgroups ===== | |
Objectclasses: | Objectclasses: | ||
| Line 180: | Line 418: | ||
In practice, it is preferred to have netgroups that only define user or host, never both. domainname is never defined. User netgroups are used for controlling access to sudo, ssh login, console login, etc. Host netgroups are used for controlling network access to nfs shares, ssh, etc. | In practice, it is preferred to have netgroups that only define user or host, never both. domainname is never defined. User netgroups are used for controlling access to sudo, ssh login, console login, etc. Host netgroups are used for controlling network access to nfs shares, ssh, etc. | ||
| − | + | ===== NisMap entries (autofs) ===== | |
Objectclasses: | Objectclasses: | ||
| Line 227: | Line 465: | ||
</source> | </source> | ||
| − | + | ===== NisObject entries (autofs) ===== | |
* Required: | * Required: | ||
| Line 272: | Line 510: | ||
</source> | </source> | ||
| − | + | ===== Sudo entries ===== | |
Objectclasses: | Objectclasses: | ||
| Line 295: | Line 533: | ||
In practice, most entries will define sudouser, sudohost, and sudocommand. cn=defaults,ou=sudoers should be added with sudooption attributes that should apply globally (like mailto address, etc.). | In practice, most entries will define sudouser, sudohost, and sudocommand. cn=defaults,ou=sudoers should be added with sudooption attributes that should apply globally (like mailto address, etc.). | ||
| − | + | ===== Security groups ===== | |
Objectclasses: | Objectclasses: | ||
| Line 310: | Line 548: | ||
** description | ** description | ||
| − | + | ===== Host entries ===== | |
Objectclasses: | Objectclasses: | ||
| Line 316: | Line 554: | ||
* top | * top | ||
* iphost | * iphost | ||
| + | * device | ||
Attributes: | Attributes: | ||
| Line 327: | Line 566: | ||
In practice, we should avoid using host entries. DNS is much more suited for this. The only real consideration for using host entries is naming backend systems that aren't in DNS. | In practice, we should avoid using host entries. DNS is much more suited for this. The only real consideration for using host entries is naming backend systems that aren't in DNS. | ||
| − | + | === Add a proxy agent === | |
Since we are requiring authentication by default, the clients need a user to do authenticated lookups. We create a proxyagent user for this: | Since we are requiring authentication by default, the clients need a user to do authenticated lookups. We create a proxyagent user for this: | ||
| Line 343: | Line 582: | ||
</source> | </source> | ||
| − | === | + | === Enable replication === |
<source lang=bash> | <source lang=bash> | ||
| − | + | su - opendj | |
| + | dsreplication enable \ | ||
| + | --host1 <server1> \ | ||
| + | --port1 4444 \ | ||
| + | --bindDN1 cn=Directory\ Manager \ | ||
| + | --bindPassword1 ****** \ | ||
| + | --host2 <server2> \ | ||
| + | --port2 4444 \ | ||
| + | --bindDN2 cn=Directory\ Manager \ | ||
| + | --bindPassword2 ****** \ | ||
| + | --replicationPort1 8989 \ | ||
| + | --secureReplication1 \ | ||
| + | --replicationPort2 8989 \ | ||
| + | --secureReplication2 \ | ||
| + | --baseDN <basedn> \ | ||
| + | --adminUID admin \ | ||
| + | --adminPassword ****** \ | ||
| + | --trustStorePath /var/opendj/instance/config/truststore \ | ||
| + | --no-prompt | ||
</source> | </source> | ||
| − | + | === Initialize replica === | |
| − | = | + | <source lang=bash> |
| + | su - opendj | ||
| + | dsreplication initialize \ | ||
| + | --hostSource <server1> \ | ||
| + | --portSource 4444 \ | ||
| + | --hostDestination <server2> \ | ||
| + | --portDestination 4444 \ | ||
| + | --baseDN <basedn> \ | ||
| + | --adminUID admin \ | ||
| + | --adminPassword ****** \ | ||
| + | --trustStorePath /var/opendj/instance/config/truststore \ | ||
| + | --no-prompt | ||
| + | </source> | ||
| − | + | === Install/configure phpldapadmin === | |
| − | + | == Installing/Configuring the client manually == | |
| − | + | === Install required packages === | |
<source lang=bash> | <source lang=bash> | ||
| Line 363: | Line 632: | ||
</source> | </source> | ||
| − | + | === Install the server certificate's CA === | |
# Install to /etc/ssl/certs/ldapca.crt | # Install to /etc/ssl/certs/ldapca.crt | ||
| Line 374: | Line 643: | ||
</source> | </source> | ||
| − | + | === Configure openldap's ldap.conf === | |
Add the following options to /etc/ldap/ldap.conf: | Add the following options to /etc/ldap/ldap.conf: | ||
| Line 381: | Line 650: | ||
BASE <basedn> | BASE <basedn> | ||
URI ldap://<servername>:389 | URI ldap://<servername>:389 | ||
| − | + | SSL start_tls | |
| + | TLS_CHECKPEER yes | ||
| + | TLS_REQCERT demand | ||
TLS_CACERTDIR /etc/ssl/certs | TLS_CACERTDIR /etc/ssl/certs | ||
| + | TLS_CACERTFILE /etc/ssl/certs/ldapca.crt | ||
TLS_CACERT /etc/ssl/certs/ldapca.crt | TLS_CACERT /etc/ssl/certs/ldapca.crt | ||
| + | SUDOERS_BASE ou=sudoers,<basedn> | ||
</source> | </source> | ||
| Line 389: | Line 662: | ||
* Note: Though we define the URI as ldap/389, we should always use encryption, so all clients should use StartTLS | * Note: Though we define the URI as ldap/389, we should always use encryption, so all clients should use StartTLS | ||
| − | + | === Configure libnss's ldap.conf === | |
<source lang=text> | <source lang=text> | ||
| − | + | uri ldap://<server1>:389 ldap://<server2>:389 | |
base <basedn> | base <basedn> | ||
binddn cn=proxyagent,ou=profile,<basedn> | binddn cn=proxyagent,ou=profile,<basedn> | ||
| Line 407: | Line 680: | ||
ssl start_tls | ssl start_tls | ||
pam_password clear | pam_password clear | ||
| − | |||
</source> | </source> | ||
| − | + | === Configure nss === | |
<source lang=text> | <source lang=text> | ||
| Line 430: | Line 702: | ||
</source> | </source> | ||
| − | + | The above works for clients with DNS access. For hosts without DNS access, the following line works better for hosts: | |
| − | ==== Configure autofs ==== | + | <source lang=text> |
| + | hosts: files ldap dns | ||
| + | </source> | ||
| + | |||
| + | === Configure pam === | ||
| + | |||
| + | common-auth: | ||
| + | |||
| + | <source lang=text> | ||
| + | # here are the per-package modules (the "Primary" block) | ||
| + | auth [success=2 default=ignore] pam_unix.so nullok_secure | ||
| + | auth [success=1 default=ignore] pam_ldap.so use_first_pass | ||
| + | # here's the fallback if no module succeeds | ||
| + | auth requisite pam_deny.so | ||
| + | # limit access to specific users | ||
| + | auth required pam_access.so | ||
| + | # prime the stack with a positive return value if there isn't one already; | ||
| + | # this avoids us returning an error just because nothing sets a success code | ||
| + | # since the modules above will each just jump around | ||
| + | auth required pam_permit.so | ||
| + | # and here are more per-package modules (the "Additional" block) | ||
| + | # end of pam-auth-update config | ||
| + | </source> | ||
| + | |||
| + | common-account: | ||
| + | |||
| + | <source lang=text> | ||
| + | # here are the per-package modules (the "Primary" block) | ||
| + | account [success=2 new_authtok_reqd=done default=ignore] pam_unix.so | ||
| + | account [success=1 default=ignore] pam_ldap.so | ||
| + | # here's the fallback if no module succeeds | ||
| + | account requisite pam_deny.so | ||
| + | # prime the stack with a positive return value if there isn't one already; | ||
| + | # this avoids us returning an error just because nothing sets a success code | ||
| + | # since the modules above will each just jump around | ||
| + | account required pam_permit.so | ||
| + | # and here are more per-package modules (the "Additional" block) | ||
| + | # end of pam-auth-update config | ||
| + | </source> | ||
| + | |||
| + | common-password: | ||
| + | |||
| + | <source lang=text> | ||
| + | # here are the per-package modules (the "Primary" block) | ||
| + | password [success=2 default=ignore] pam_unix.so obscure sha512 | ||
| + | password [success=1 user_unknown=ignore default=die] pam_ldap.so try_first_pass | ||
| + | # here's the fallback if no module succeeds | ||
| + | password requisite pam_deny.so | ||
| + | # prime the stack with a positive return value if there isn't one already; | ||
| + | # this avoids us returning an error just because nothing sets a success code | ||
| + | # since the modules above will each just jump around | ||
| + | password required pam_permit.so | ||
| + | # and here are more per-package modules (the "Additional" block) | ||
| + | # end of pam-auth-update config | ||
| + | </source> | ||
| + | |||
| + | common-session: | ||
| + | |||
| + | <source lang=text> | ||
| + | # here are the per-package modules (the "Primary" block) | ||
| + | session [default=1] pam_permit.so | ||
| + | # here's the fallback if no module succeeds | ||
| + | session requisite pam_deny.so | ||
| + | # prime the stack with a positive return value if there isn't one already; | ||
| + | # this avoids us returning an error just because nothing sets a success code | ||
| + | # since the modules above will each just jump around | ||
| + | session required pam_permit.so | ||
| + | # and here are more per-package modules (the "Additional" block) | ||
| + | session required pam_unix.so | ||
| + | session optional pam_ldap.so | ||
| + | # end of pam-auth-update config | ||
| + | </source> | ||
| + | |||
| + | common-session-noninteractive: | ||
| + | |||
| + | <source lang=text> | ||
| + | # here are the per-package modules (the "Primary" block) | ||
| + | session [default=1] pam_permit.so | ||
| + | # here's the fallback if no module succeeds | ||
| + | session requisite pam_deny.so | ||
| + | # prime the stack with a positive return value if there isn't one already; | ||
| + | # this avoids us returning an error just because nothing sets a success code | ||
| + | # since the modules above will each just jump around | ||
| + | session required pam_permit.so | ||
| + | # and here are more per-package modules (the "Additional" block) | ||
| + | session required pam_unix.so | ||
| + | session optional pam_ldap.so | ||
| + | # end of pam-auth-update config | ||
| + | </source> | ||
| + | |||
| + | === Configure autofs === | ||
| + | |||
| + | ==== Configure /etc/autofs_ldap_auth.conf ==== | ||
| + | |||
| + | Autofs's ldap configuration needs to be set to require tls, and authentication, and to use SASL PLAIN authentication with a DN and password. The following configuration provides that: | ||
| + | |||
| + | <source lang=xml> | ||
| + | <?xml version="1.0" ?> | ||
| + | <autofs_ldap_sasl_conf | ||
| + | usetls="yes" | ||
| + | tlsrequired="yes" | ||
| + | authrequired="yes" | ||
| + | authtype="PLAIN" | ||
| + | user="dn:cn=proxyagent,ou=profile,<basedn>" | ||
| + | secret="<proxyagentPassword>" | ||
| + | /> | ||
| + | </source> | ||
| + | |||
| + | Be sure to fill in <basedn> and <proxyagentPassword> with real values. | ||
| + | |||
| + | ==== Configuring overrides ==== | ||
nsswitch.conf is configured to use files, then ldap. It is possible, but not necessary to do this per map. It is required to do this at least for auto.master. If we want to pull all maps from ldap, the only line needed in /etc/auto.master is: | nsswitch.conf is configured to use files, then ldap. It is possible, but not necessary to do this per map. It is required to do this at least for auto.master. If we want to pull all maps from ldap, the only line needed in /etc/auto.master is: | ||
| Line 456: | Line 838: | ||
The above would still pull all auto.data entries, but would override the specific entry "overrideentry". | The above would still pull all auto.data entries, but would override the specific entry "overrideentry". | ||
| − | + | === Configure sudo === | |
sudo is configured via the nsswitch. As long as the sudo-ldap package is installed, and nsswitch.conf has ldap listed for sudoers, it will automatically pull from ldap. | sudo is configured via the nsswitch. As long as the sudo-ldap package is installed, and nsswitch.conf has ldap listed for sudoers, it will automatically pull from ldap. | ||
| − | + | == Troubleshooting == | |
| + | |||
| + | === Server === | ||
| + | |||
| + | ==== Service takes ages to start, won't answer requests ==== | ||
| + | |||
| + | Check to ensure the server's IP address is correct in /etc/hosts | ||
| + | |||
| + | ==== Where to find logs ==== | ||
| + | |||
| + | All LDAP server logs can be found here: | ||
| + | |||
| + | /var/opendj/instance/logs | ||
| + | |||
| + | === Client === | ||
==== Corrupt nscd cache ==== | ==== Corrupt nscd cache ==== | ||
| Line 481: | Line 877: | ||
Where <database> is passwd, group, or services. | Where <database> is passwd, group, or services. | ||
| + | |||
| + | ==== Debugging sudo-ldap ==== | ||
| + | |||
| + | Sudo will print ldap debugging information, if you add the following to /etc/ldap/ldap.conf: | ||
| + | |||
| + | <source lang=text> | ||
| + | SUDOERS_DEBUG 2 | ||
| + | </source> | ||
| + | |||
| + | ==== Debugging autofs ==== | ||
| + | |||
| + | Autofs will print debugging information to /var/log/syslog, if you add the following to /etc/default/autofs: | ||
| + | |||
| + | <source lang=text> | ||
| + | LOGGING="debug" | ||
| + | </source> | ||
| + | |||
| + | == Maintenance == | ||
| + | |||
| + | === Server === | ||
| + | |||
| + | ==== Change directory manager password ==== | ||
| + | |||
| + | On each server, run: | ||
| + | |||
| + | <source lang=bash> | ||
| + | su - opendj | ||
| + | ldappasswordmodify --authzID "dn:cn=Directory Manager" -N new -C old | ||
| + | </source> | ||
| + | |||
| + | Where new and old are files with the old and the new passwords. | ||
| + | |||
| + | Obviously, after running this command, you should shred the password files: | ||
| + | |||
| + | <source lang=bash> | ||
| + | shred -u new old | ||
| + | </source> | ||
| + | |||
| + | Alternatively, if you have forgotten the old password, you can shut down the server and then edit it directly in /var/opendj/instance/config/config.ldif (untested): | ||
| + | |||
| + | <pre> | ||
| + | su opendj - | ||
| + | encode-password -s SSHA512 -i | ||
| + | sed -i~ 's/^userPassword: .*$/userPassword: <new hash>/' config.ldif | ||
| + | </pre> | ||
| + | |||
| + | == Tips == | ||
| + | |||
| + | === Server === | ||
| + | |||
| + | ==== Non-interactive dsconfig ==== | ||
| + | |||
| + | You can get the non-interactive version of any dsconfig change by starting dsconfig like so: | ||
| + | |||
| + | <source lang=bash> | ||
| + | dsconfig --displayCommand | ||
| + | </source> | ||
| + | |||
| + | ==== Advanced dsconfig mode ==== | ||
| + | |||
| + | Not all options are available in dsconfig, unless you use advanced mode, you can do so as follows: | ||
| + | |||
| + | <source lang=bash> | ||
| + | dsconfig --advanced | ||
| + | </source> | ||
| + | |||
| + | ==== Connections via SSL or Non-SSL are allowed ==== | ||
| + | |||
| + | ldap, ldaps, and ldap with starttls are all enabled. It isn't necessary to use ldaps, but is recommended when doing binds, or when doing searches that involve sensitive information. | ||
| + | |||
| + | === LDIF === | ||
| + | |||
| + | ==== Adding/deleting objects vs adding/deleting attributes ==== | ||
| + | |||
| + | Remember, when you are adding a full object, you use "changetype: add"; when you are adding an attribute, you use "changetype: modify". Similarly, when you are deleting an object, you use "changetype: delete"; when you are deleting an attribute, you use "changetype: modify". | ||
| + | |||
| + | |||
| + | === How do I search LDAP ? === | ||
| + | |||
| + | On formey is the ldaplist command line utility which let you search in LDAP. See [[ldaplist]]. The command can also be run from any Labs instance, including bastion. | ||
| + | |||
| + | == Using LDAP (for svn access) == | ||
| + | See the [[Svn|SVN]] page for detail on how to interact with our ldap instance and add / remove users, keys, etc. | ||
[[Category:Software]] | [[Category:Software]] | ||
Latest revision as of 22:35, 22 October 2012
[edit] LDAP in Production
This section is the short version of how LDAP works in production for mail.
[edit] server and process
- sanger is the host serving LDAP. Verify this is not out of date by checking
- the MX record for wikimedia.org is mchenry
- on mchenry, in
/etc/exim4/exim4.conf, the variableldap_default_serversis the ldap server
- opendj is the LDAP server process. Restart it with
/etc/init.d/opendj restart
[edit] configuration
- opendj is configured to listen on ports 4444, 8989, 1636, 1389.
- The list of IP addresses to listen on are defined by puppet and stored in /etc/default/opendj (this file is read by the init script)
- iptables forwards ports 389 and 636 to opendj on port 1389 and 1636.
- examine the iptables rules with
iptables -nvL -t nat. It should look like this:
- examine the iptables rules with
root@sanger:~# iptables -nvL -t nat Chain PREROUTING (policy ACCEPT 17402 packets, 1086K bytes) pkts bytes target prot opt in out source destination 0 0 REDIRECT tcp -- * * 0.0.0.0/0 127.0.0.1 tcp dpt:389 redir ports 1389 0 0 REDIRECT tcp -- * * 0.0.0.0/0 127.0.0.1 tcp dpt:636 redir ports 1636 1502 90120 REDIRECT tcp -- * * 0.0.0.0/0 208.80.152.187 tcp dpt:389 redir ports 1389 12 720 REDIRECT tcp -- * * 0.0.0.0/0 208.80.152.187 tcp dpt:636 redir ports 1636 Chain POSTROUTING (policy ACCEPT 56949 packets, 4008K bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 56949 packets, 4008K bytes) pkts bytes target prot opt in out source destination 0 0 DNAT tcp -- * * 0.0.0.0/0 127.0.0.1 tcp dpt:389 to::1389 0 0 DNAT tcp -- * * 0.0.0.0/0 127.0.0.1 tcp dpt:636 to::1636 0 0 DNAT tcp -- * * 0.0.0.0/0 208.80.152.187 tcp dpt:389 to::1389 0 0 DNAT tcp -- * * 0.0.0.0/0 208.80.152.187 tcp dpt:636 to::1636
[edit] troubleshooting
If mail is not getting delivered because LDAP is broken, you'll see errors like this in the primary mail server (mchenry as of 2012/03)/var/log/exim4/mainlog:
2012-03-29 18:00:00 H=lists.wikimedia.org [2620:0:861:1::2]:53845 I=[2620:0:860:2:219:b9ff:fedd:c027]:25 F=<> temporarily rejected RCPT <foobarbaz@wikimedia.org>: failed to bind the LDAP connection to server sanger.wikimedia.org:389 - ldap_bind() returned -1
In this case you should try and restart opendj on sanger and check
- opendj is listening with
netstat -neapl | grep LISTEN\ - the iptables rules are loaded with
iptables -nvL -t nat
[edit] Installing/Configuring the server
[edit] Install required packages
apt-get install openjdk-6-jre ldap-utils opendj
[edit] OpenDJ package information
Basic package file layout:
- /etc/opendj/instance.loc
- Points to the instance location
- /usr/opendj
- Binaries only; only the package should ever modify this
- /var/opendj/instance
- Actual instance, owned by opendj:opendj
- /var/opendj/instance/config/schema
- Where custom schema goes
You can change the instance location by moving the directory, and updating /etc/opends/instance.loc.
The service is run as opendj:opendj. It adds iptables rules on start to forward 389 to 1389 and 636 to 1636. It removes the rules on service stop. Right now the init script isn't doing checks to see if the rules are already added, so it may add them more than once, if start is run more than once. I'll fix this in the next package version. The rules can be flushed using:
iptables -F -t nat
[edit] Create a PKCS12 certificate
It is easier to install a PKCS12 certificate in the directory server, than a PKCS11 cert/key; here's how to create one from the PKCS11 cert/key pair:
openssl pkcs12 -export -in pkcs11.cert -inkey pkcs12.key -out pkcs12.p12
Remember what you enter for the passkey, you'll need it later when adding it to the server.
[edit] Initial instance configuration
- Change user to opendj: su - opendj
- All opendj commands will require you to do this
- Run /usr/opendj/setup
- Ensure you use the FQDN for any hostnames requested!
- Enable SSL support during the install
- Install the PKCS12 certificate during the install (it's a pain later)
- Start the service: /etc/init.d/opendj start
- Ensure the service starts on boot: update-rc.d opendj defaults
[edit] Add the CA trust to the truststore
su - opendj cd instance/config keytool -importcert -trustcacerts -alias "wmf-ca" -file /etc/ssl/certs/wmf-ca.pem -keystore truststore -storepass `cat keystore.pin`
[edit] Change the administration connector's certificate provider
[edit] Using dsconfig interactively
- Run dsconfig (as opendj)
- Select "Administration Connector"
- Select "View and edit the Administration Connector"
- Select "key-manager-provider"
- Select "Change it to the Key Manager Provider: PKCS12"
- Select "ssl-cert-nickname"
- Select "Change the value"
- Type in the certificate alias
- If you didn't use "-name" when creating the pkcs12 file, this is "1"; otherwise, it is the value of "-name"
- Select "finish"
- Select "quit"
- Restart opendj
[edit] Using dsconfig non-interactively
dsconfig set-administration-connector-prop \
--set key-manager-provider:PKCS12 \
--set ssl-cert-nickname:1 \
--set trust-manager-provider:JKS \
--hostname <hostname> \
--port 4444 \
--trustStorePath /var/opendj/instance/config/admin-truststore \
--bindDN cn=Directory\ Manager \
--bindPassword ****** \
--no-prompt[edit] Add the sudoers schema
cp /usr/share/doc/sudo-ldap/schema.iPlanet /var/opendj/instance/config/schema/98sudo.ldif /etc/init.d/opendj restart
- Note: this assumes the sudo-ldap package is installed. If it is not, you'll need to get this file from elsewhere.
- Additional note: This is also assumed to be handled by puppet.
[edit] Disable anonymous read access
This must be done on each server.
[edit] Using dsconfig
Change the following aci:
(targetattr!="userPassword||authPassword")(version 3.0; acl "Anonymous read access"; allow (read,search,compare) userdn="ldap:///anyone";)
to:
(targetattr!="userPassword||authPassword")(version 3.0; acl "Authenticated read access"; allow (read,search,compare) userdn="ldap:///all";)
This causes the directory server to require authentication for read access.
To do this, as opendj, use dsconfig:
- Select the "Access Control Handler"
- Select "View and edit the Access Control Handler"
- Select the "global-aci"
- Select "Remove one or more values"
- Select the aci to be removed
- Select "Add one or more values", add the new entry, hit enter
- Select "Use these values"
- Select "finish - apply..."
- quit
For more info, see the OpenDS documentation on this.
[edit] Using ldapsearch/ldapmodify
Put the current global ACIs into an ldif:
ldapsearch -LLL -x -D 'cn=directory manager' -H ldaps://<servername>:636 -W -b 'cn=config' 'cn=Access Control Handler' ds-cfg-global-aci > global-aci.ldif
Add the following after the DN line:
changetype: modify replace: ds-cfg-global-aci
Change the following aci:
(targetattr!="userPassword||authPassword")(version 3.0; acl "Anonymous read access"; allow (read,search,compare) userdn="ldap:///anyone";)
to:
(targetattr!="userPassword||authPassword")(version 3.0; acl "Authenticated read access"; allow (read,search,compare) userdn="ldap:///all";)
Submit the modifications:
ldapmodify -x -D 'cn=directory manager' -H ldaps://<servername>:636 -W -f global-aci.ldif
[edit] Create indexes
To create the indexes, on each server run:
su - opendj create-nis-indexes "<basedn>" /var/tmp/indexes.cmds dsconfig -n -X -h localhost -D "cn=directory manager" -w <passwd> -F /var/tmp/indexes.cmds
After creating the indexes, on each server you need to stop the server, rebuild the indexes, and start the server:
/etc/init.d/opendj stop su - opendj rebuild-index --rebuildAll -b '<basedn>' exit /etc/init.d/opendj start
[edit] Enable UID Unique Attribute plugin
We want uid and uidnumber attributes to always be unique and for the server to reject any mod operations that would make them not unique. Run the following command on each server to enable this plugin and add the uidnumber attribute to it:
dsconfig set-plugin-prop \
--plugin-name UID\ Unique\ Attribute \
--set enabled:true \
--add type:uidnumber \
--hostname <servername> \
--port 4444 \
--trustStorePath /var/opendj/instance/config/truststore \
--bindDN cn=Directory\ Manager \
--bindPassword ****** \
--no-promptThe above command will fail if the indexes haven't been added yet.
[edit] Enable referential integrity
When users are delete, we want references to them to be deleted as well. Run this command on each server to enable this:
dsconfig set-plugin-prop \
--plugin-name Referential\ Integrity \
--set enabled:true \
--hostname <servername> \
--port 4444 \
--trustStorePath /var/opendj/instance/config/truststore \
--bindDN cn=Directory\ Manager \
--bindPassword ****** \
--no-prompt[edit] Enable membership retrieval for virtual static groups
A number of features we want require virtual static groups. Run this command to enable it:
dsconfig set-virtual-attribute-prop \
--port 4444 \
--hostname <servername> \
--bindDN "cn=Directory Manager" \
--bindPassword ****** \
--name "Virtual Static member" \
--set allow-retrieving-membership:true \
--no-prompt[edit] Create a Basic Directory Information Tree (DIT)
[edit] Top level structure
Our aim is for a DIT that is as flat as possible. Adding hierarchy adds complexity, and isn't as flexible as one would first imagine. Using attributes to imply hierarchy is more effective. Below is a basic OU and automount hierarchy:
dn: ou=people,<basedn> changetype: add objectClass: top objectClass: organizationalUnit ou: people dn: ou=group,<basedn> changetype: add objectClass: top objectClass: organizationalUnit ou: group dn: ou=netgroup,<basedn> changetype: add objectClass: top objectClass: organizationalUnit ou: netgroup dn: ou=sudoers,<basedn> changetype: add objectClass: top objectClass: organizationalUnit ou: sudoers dn: ou=hosts,<basedn> changetype: add objectClass: top objectClass: organizationalUnit ou: hosts dn: ou=profile,<basedn> changetype: add objectClass: top objectClass: organizationalUnit ou: profile dn: nisMapName=auto.master,<basedn> changetype: add objectClass: top objectClass: nisMap nisMapName: auto.master dn: nisMapName=auto.home,<basedn> changetype: add objectClass: top objectClass: nisMap nisMapName: auto.home dn: nisMapName=/home,nisMapName=auto.master,<basedn> changetype: add objectClass: top objectClass: nisObject cn: /home nisMapEntry: ldap:nisMapName=auto.home,<basedn> nisMapName: auto.master dn: cn=*,nisMapName=auto.home,<basedn> changetype: add nisMapEntry: <homedirserver>:/home/& objectClass: nisObject objectClass: top nisMapName: auto.home cn: *
[edit] Object classes and attributes for objects
[edit] Users
Objectclasses:
- top
- person
- inetorgperson
- posixaccount
- shadowaccount
- ldappublickey
Attributes:
- Required:
- sn
- cn
- uid
- uidnumber
- gidnumber
- homedirectory
- Optional:
- userpassword
- loginshell
- description
- sshpublickey
In practice, loginshell should always be defined.
[edit] Groups
Objectclasses:
- top
- groupofuniquenames
- posixgroup
Attributes:
- Required:
- cn
- gidnumber
- Optional:
- uniquemember
- memberuid
- description
- Note: uniquemember should be used, not memberuid. uniquemember is a multi-value attributes that uses DNs for members.
[edit] Netgroups
Objectclasses:
- top
- nisnetgroup
Attributes:
- Required:
- cn
- Optional:
- membernisnetgroup
- nisnetgrouptriple
- description
In practice, every entry should either have membernisnetgroup or nisnetgrouptriple.
nisnetgrouptriple attributes are defined as:
(host,user,domainname)
In practice, it is preferred to have netgroups that only define user or host, never both. domainname is never defined. User netgroups are used for controlling access to sudo, ssh login, console login, etc. Host netgroups are used for controlling network access to nfs shares, ssh, etc.
[edit] NisMap entries (autofs)
Objectclasses:
- top
- nismap
Attributes:
- Required:
- nismapname
- Optional:
- description
From the perspective of automount, nismaps are like auto.master, and the other files that define mounts. If you have an auto.master file that looks like this:
/home /etc/auto.home /data /etc/auto.data /scratch /etc/auto.scratch +auto.master
You would have nismaps that look like this:
dn: nisMapName=auto.master,<basedn> objectClass: top objectClass: nisMap nisMapName: auto.master dn: nisMapName=auto.home,<basedn> objectClass: top objectClass: nisMap nisMapName: auto.home dn: nisMapName=auto.data,<basedn> objectClass: top objectClass: nisMap nisMapName: auto.data dn: nisMapName=auto.scratch,<basedn> objectClass: top objectClass: nisMap nisMapName: auto.scratch
[edit] NisObject entries (autofs)
- Required:
- cn
- nismapentry
- nismapname
- Optional:
- description
Nisobjects exist inside of nismaps. From the perspective of automount, nisobjects are the entries inside of the files. So, the above auto.master file would require the following nisobject entries:
dn: nisMapName=/home,nisMapName=auto.master,<basedn> objectClass: top objectClass: nisObject cn: /home nisMapEntry: ldap:nisMapName=auto.home,<basedn> nisMapName: auto.master dn: nisMapName=/home,nisMapName=auto.master,<basedn> objectClass: top objectClass: nisObject cn: /home nisMapEntry: ldap:nisMapName=auto.home,<basedn> nisMapName: auto.master dn: nisMapName=/home,nisMapName=auto.master,<basedn> objectClass: top objectClass: nisObject cn: /home nisMapEntry: ldap:nisMapName=auto.home,<basedn> nisMapName: auto.master
An example of an entry you'd fine in auto.home would be:
dn: cn=*,nisMapName=auto.home,<basedn> nisMapEntry: <server>:/home/& objectClass: nisObject objectClass: top nisMapName: auto.home cn: *
[edit] Sudo entries
Objectclasses:
- top
- sudorole
Attributes:
- Required:
- cn
- Optional:
- sudouser
- sudohost
- sudocommand
- sudorunas
- sudorunasuser
- sudorunasgroup
- sudooption
- description
In practice, most entries will define sudouser, sudohost, and sudocommand. cn=defaults,ou=sudoers should be added with sudooption attributes that should apply globally (like mailto address, etc.).
[edit] Security groups
Objectclasses:
- top
- groupofnames
Attributes:
- Required:
- cn
- member
- optional
- description
[edit] Host entries
Objectclasses:
- top
- iphost
- device
Attributes:
- Required:
- cn
- iphostnumber
- Optional:
- description
In practice, we should avoid using host entries. DNS is much more suited for this. The only real consideration for using host entries is naming backend systems that aren't in DNS.
[edit] Add a proxy agent
Since we are requiring authentication by default, the clients need a user to do authenticated lookups. We create a proxyagent user for this:
dn: cn=proxyagent,ou=profile,<basedn> changetype: add objectclass: top objectclass: inetorgperson objectclass: person sn: agent givenName: proxy userpassword: <aPasswordGoesHere> cn: proxyagent
[edit] Enable replication
su - opendj dsreplication enable \ --host1 <server1> \ --port1 4444 \ --bindDN1 cn=Directory\ Manager \ --bindPassword1 ****** \ --host2 <server2> \ --port2 4444 \ --bindDN2 cn=Directory\ Manager \ --bindPassword2 ****** \ --replicationPort1 8989 \ --secureReplication1 \ --replicationPort2 8989 \ --secureReplication2 \ --baseDN <basedn> \ --adminUID admin \ --adminPassword ****** \ --trustStorePath /var/opendj/instance/config/truststore \ --no-prompt
[edit] Initialize replica
su - opendj dsreplication initialize \ --hostSource <server1> \ --portSource 4444 \ --hostDestination <server2> \ --portDestination 4444 \ --baseDN <basedn> \ --adminUID admin \ --adminPassword ****** \ --trustStorePath /var/opendj/instance/config/truststore \ --no-prompt
[edit] Install/configure phpldapadmin
[edit] Installing/Configuring the client manually
[edit] Install required packages
apt-get install ldap-utils sudo-ldap libpam-ldap libnss-ldap nss-updatedb libnss-db autofs5 autofs5-ldap nscd[edit] Install the server certificate's CA
- Install to /etc/ssl/certs/ldapca.crt
- Run:
pushd /etc/ssl/certs ln -s ldapca.crt $(openssl x509 -hash -noout -in ldapca.crt).0 popd
[edit] Configure openldap's ldap.conf
Add the following options to /etc/ldap/ldap.conf:
BASE <basedn> URI ldap://<servername>:389 SSL start_tls TLS_CHECKPEER yes TLS_REQCERT demand TLS_CACERTDIR /etc/ssl/certs TLS_CACERTFILE /etc/ssl/certs/ldapca.crt TLS_CACERT /etc/ssl/certs/ldapca.crt SUDOERS_BASE ou=sudoers,<basedn>
- Note: TLS_CACERTDIR is likely ignored, since gnutls doesn't support the directive, but for future compatibility, it should be defined.
- Note: Though we define the URI as ldap/389, we should always use encryption, so all clients should use StartTLS
[edit] Configure libnss's ldap.conf
uri ldap://<server1>:389 ldap://<server2>:389 base <basedn> binddn cn=proxyagent,ou=profile,<basedn> bindpw <proxyagentPasswordInTheClear> pam_filter objectclass=posixAccount nss_base_passwd ou=people,<basedn> nss_base_shadow ou=people,<basedn> nss_base_group ou=group,<basedn> nss_base_hosts ou=hosts,<basedn> nss_base_netgroup ou=netgroup,<basedn> tls_checkpeer yes tls_cacertfile /etc/ssl/certs/ldapca.crt tls_cacertdir /etc/ssl/certs ssl start_tls pam_password clear
[edit] Configure nss
passwd: files ldap group: files ldap shadow: files ldap hosts: files dns ldap networks: files protocols: db files services: db files ethers: db files rpc: db files netgroup: ldap automount: files ldap sudoers: files ldap
The above works for clients with DNS access. For hosts without DNS access, the following line works better for hosts:
hosts: files ldap dns
[edit] Configure pam
common-auth:
# here are the per-package modules (the "Primary" block) auth [success=2 default=ignore] pam_unix.so nullok_secure auth [success=1 default=ignore] pam_ldap.so use_first_pass # here's the fallback if no module succeeds auth requisite pam_deny.so # limit access to specific users auth required pam_access.so # prime the stack with a positive return value if there isn't one already; # this avoids us returning an error just because nothing sets a success code # since the modules above will each just jump around auth required pam_permit.so # and here are more per-package modules (the "Additional" block) # end of pam-auth-update config
common-account:
# here are the per-package modules (the "Primary" block) account [success=2 new_authtok_reqd=done default=ignore] pam_unix.so account [success=1 default=ignore] pam_ldap.so # here's the fallback if no module succeeds account requisite pam_deny.so # prime the stack with a positive return value if there isn't one already; # this avoids us returning an error just because nothing sets a success code # since the modules above will each just jump around account required pam_permit.so # and here are more per-package modules (the "Additional" block) # end of pam-auth-update config
common-password:
# here are the per-package modules (the "Primary" block) password [success=2 default=ignore] pam_unix.so obscure sha512 password [success=1 user_unknown=ignore default=die] pam_ldap.so try_first_pass # here's the fallback if no module succeeds password requisite pam_deny.so # prime the stack with a positive return value if there isn't one already; # this avoids us returning an error just because nothing sets a success code # since the modules above will each just jump around password required pam_permit.so # and here are more per-package modules (the "Additional" block) # end of pam-auth-update config
common-session:
# here are the per-package modules (the "Primary" block) session [default=1] pam_permit.so # here's the fallback if no module succeeds session requisite pam_deny.so # prime the stack with a positive return value if there isn't one already; # this avoids us returning an error just because nothing sets a success code # since the modules above will each just jump around session required pam_permit.so # and here are more per-package modules (the "Additional" block) session required pam_unix.so session optional pam_ldap.so # end of pam-auth-update config
common-session-noninteractive:
# here are the per-package modules (the "Primary" block) session [default=1] pam_permit.so # here's the fallback if no module succeeds session requisite pam_deny.so # prime the stack with a positive return value if there isn't one already; # this avoids us returning an error just because nothing sets a success code # since the modules above will each just jump around session required pam_permit.so # and here are more per-package modules (the "Additional" block) session required pam_unix.so session optional pam_ldap.so # end of pam-auth-update config
[edit] Configure autofs
[edit] Configure /etc/autofs_ldap_auth.conf
Autofs's ldap configuration needs to be set to require tls, and authentication, and to use SASL PLAIN authentication with a DN and password. The following configuration provides that:
<?xml version="1.0" ?> <autofs_ldap_sasl_conf usetls="yes" tlsrequired="yes" authrequired="yes" authtype="PLAIN" user="dn:cn=proxyagent,ou=profile,<basedn>" secret="<proxyagentPassword>" />
Be sure to fill in <basedn> and <proxyagentPassword> with real values.
[edit] Configuring overrides
nsswitch.conf is configured to use files, then ldap. It is possible, but not necessary to do this per map. It is required to do this at least for auto.master. If we want to pull all maps from ldap, the only line needed in /etc/auto.master is:
+auto.master
The above line tells automount to pull its maps from whichever service is configured in nss after files. If you wish to override LDAP, you can place entries above the + line. You can override individual entries for individual maps by doing something like the following:
- In auto.master:
/data /etc/auto.data +auto.master
- In auto.data:
overridenentry <server>:/export +auto.data
The above would still pull all auto.data entries, but would override the specific entry "overrideentry".
[edit] Configure sudo
sudo is configured via the nsswitch. As long as the sudo-ldap package is installed, and nsswitch.conf has ldap listed for sudoers, it will automatically pull from ldap.
[edit] Troubleshooting
[edit] Server
[edit] Service takes ages to start, won't answer requests
Check to ensure the server's IP address is correct in /etc/hosts
[edit] Where to find logs
All LDAP server logs can be found here:
/var/opendj/instance/logs
[edit] Client
[edit] Corrupt nscd cache
The nscd cache is a bdb database, and can (rarely) become corrupt when a system shuts down uncleanly. You can forcibly clear the cache by doing the following:
/etc/init.d/nscd stop rm -f /var/cache/nscd/* /etc/init.d/nscd start
[edit] Invalid nscd cache
To purge an invalid cache, you can do the following:
nscd -i <database>
Where <database> is passwd, group, or services.
[edit] Debugging sudo-ldap
Sudo will print ldap debugging information, if you add the following to /etc/ldap/ldap.conf:
SUDOERS_DEBUG 2
[edit] Debugging autofs
Autofs will print debugging information to /var/log/syslog, if you add the following to /etc/default/autofs:
LOGGING="debug"
[edit] Maintenance
[edit] Server
[edit] Change directory manager password
On each server, run:
su - opendj ldappasswordmodify --authzID "dn:cn=Directory Manager" -N new -C old
Where new and old are files with the old and the new passwords.
Obviously, after running this command, you should shred the password files:
shred -u new old
Alternatively, if you have forgotten the old password, you can shut down the server and then edit it directly in /var/opendj/instance/config/config.ldif (untested):
su opendj - encode-password -s SSHA512 -i sed -i~ 's/^userPassword: .*$/userPassword: <new hash>/' config.ldif
[edit] Tips
[edit] Server
[edit] Non-interactive dsconfig
You can get the non-interactive version of any dsconfig change by starting dsconfig like so:
dsconfig --displayCommand[edit] Advanced dsconfig mode
Not all options are available in dsconfig, unless you use advanced mode, you can do so as follows:
dsconfig --advanced[edit] Connections via SSL or Non-SSL are allowed
ldap, ldaps, and ldap with starttls are all enabled. It isn't necessary to use ldaps, but is recommended when doing binds, or when doing searches that involve sensitive information.
[edit] LDIF
[edit] Adding/deleting objects vs adding/deleting attributes
Remember, when you are adding a full object, you use "changetype: add"; when you are adding an attribute, you use "changetype: modify". Similarly, when you are deleting an object, you use "changetype: delete"; when you are deleting an attribute, you use "changetype: modify".
[edit] How do I search LDAP ?
On formey is the ldaplist command line utility which let you search in LDAP. See ldaplist. The command can also be run from any Labs instance, including bastion.
[edit] Using LDAP (for svn access)
See the SVN page for detail on how to interact with our ldap instance and add / remove users, keys, etc.