- Openldap Tutorial – Practical Realtime Implementation and Integration
- Installation and configuration of openldap in Ubuntu
- Installing phpLDAPadmin – Web based LDAP Client
- Planning of LDAP DIT Structure and Config of Overlays ( access, ppolicy )
- OpenLDAP – Graylog LDAP Integration
- openLDAP – Linux Client LDAP Integration
- openLDAP – Basic Authentication using LDAP
- openLDAP – Self Service Password and Adhoc LDAP utilities
In this LDAP utilities section we will see how to provide a way for end users to manage their passwords using Self Service Password, Password unlock procedure, Automate Password expiry notification and LDAP backup automation.
Self Service Password
Self Service Password is a PHP application that allows users to change their password in an LDAP directory. This section will just be an excerpt on minimum configuration that is needed. For more details on self service password you can refer their site
Install SSP
In the LDAP server, create a file /etc/apt/sources.list.d/ltb-project.list and add the below content
deb [arch=amd64] https://ltb-project.org/debian/jessie jessie main
Import repository key:
wget -O - https://ltb-project.org/wiki/lib/RPM-GPG-KEY-LTB-project | sudo apt-key add -
Update package repositry
apt-get update
Install SSP
apt-get install self-service-password
SSP requires php mbstring, so install it,
apt-get install php7.0-mbstring
Webserver Configuration
Since we already have an existing webserver configuration which we created during phpldapadmin configuration, we can make use of that. We just need to update few things in the Virtual Host
Make sure the authz_ldap module is enabled,
a2enmod authz_ldap
<IfModule mod_ssl.c> <VirtualHost _default_:443> ServerAdmin [email protected] ServerName ldap.devopsideas.com DocumentRoot /var/www/html Alias /ssp /usr/share/self-service-password <Directory /usr/share/self-service-password> AuthType Basic AuthName " LDAP Auth" AuthBasicProvider ldap AuthLDAPURL "ldap://ldap.devopsideas.com:389/ou=people,dc=devopsideas,dc=com?uid" TLS AuthLDAPBindDN "cn=serverid,ou=service_ids,dc=devopsideas,dc=com" AuthLDAPBindPassword "<serverid_password>" Require ldap-group cn=ssp,ou=basic_authentication,ou=group,dc=devopsideas,dc=com </Directory> ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined SSLEngine on SSLCertificateFile /etc/ssl/certs/devopsideas.crt SSLCertificateKeyFile /etc/ssl/private/devopsideas.key <FilesMatch "\.(cgi|shtml|phtml|php)$"> SSLOptions +StdEnvVars </FilesMatch> <Directory /usr/lib/cgi-bin> SSLOptions +StdEnvVars </Directory> </VirtualHost> </IfModule>
We are creating an Alias for self service password. Then we have just enabled basic authentication for SSP and provided access to people who belong to ssp group ( Assuming you have already created a group called ssp and added users to it ).
General parameters config
Edit the configuration file /usr/share/self-service-password/conf/config.inc.php and update it with below values,
<?php # LDAP $ldap_url = "localhost"; $ldap_starttls = true; $ldap_binddn = "cn=sspadmin,id=service_ids,dc=devopsideas,dc=com"; $ldap_bindpw = "test"; $ldap_base = "ou=people,dc=devopsideas,dc=com"; $ldap_login_attribute = "cn"; $ldap_fullname_attribute = "givenName"; $ldap_filter = "(&(cn={login})(objectClass=inetOrgPerson))"; # Active Directory mode $ad_mode = false; $shadow_options['update_shadowLastChange'] = true; # Local password policy $pwd_min_length = 10; $pwd_max_length = 40; $pwd_min_lower = 1; $pwd_min_upper = 1; $pwd_min_digit = 1; $pwd_min_special = 1; $pwd_special_chars = "^a-zA-Z0-9"; $pwd_no_reuse = true; $pwd_diff_login = true; $pwd_complexity = 2; $pwd_show_policy = "onerror"; $pwd_show_policy_pos = "above"; $who_change_password = "user"; # Use standard change form? $use_change = true; ## Questions/answers $use_questions = false; $use_tokens = false; $crypt_tokens = true; $token_lifetime = "3600"; ## Mail # LDAP mail attribute $mail_attribute = "mail"; # Who the email should come from $mail_from = "[email protected]"; $mail_from_name = "devopsideas LDAP Password Reset"; # Notify users anytime their password is changed $mail_address_use_ldap = true; $notify_on_change = true; # PHPMailer configuration $mail_sendmailpath = '/usr/sbin/sendmail'; $mail_protocol = 'smtp'; $mail_smtp_debug = 0; $mail_debug_format = 'html'; $mail_smtp_host = 'smtp.gmail.com'; $mail_smtp_auth = true; $mail_smtp_user = '[email protected]'; $mail_smtp_pass = '<mail_password>'; $mail_smtp_port = 25; $mail_smtp_timeout = 30; $mail_smtp_keepalive = false; $mail_smtp_secure = 'tls'; $mail_contenttype = 'text/plain'; $mail_charset = 'utf-8'; $mail_priority = 3; $mail_newline = PHP_EOL; $use_sms = false; # Display help messages $show_help = true; # Language $lang ="en"; # Display menu on top $show_menu = true; # Logo $logo = "images/devopsideas-logo.png"; # Background image $background_image = "images/background.jpg"; # Debug mode $debug = true; # Encryption, decryption keyphrase $keyphrase = "<keyphrase>"; # Where to log password resets - Make sure apache has write permission # By default, they are logged in Apache log $reset_request_log = "/var/log/self-service-password"; # Invalid characters in login # Set at least "*()&|" to prevent LDAP injection # If empty, only alphanumeric characters are accepted $login_forbidden_chars = "*()&|"; # Use Google reCAPTCHA $use_recaptcha = false; ?>
You can refer this site for complete details on the variables used and their explanation. We have trimmed the configuration to reset the password just using the SSP application. You can enhance it to send sms, tokens etc for additional security.
In this example we will use a separate id (sspadmin) to do the bind and update the password. We need to update ACL to allow sspadmin to reset user password. We are doing this since using root DN (cn=admin,dc=devopsideas,dc=com) and passing passwords in file is not recommended.
First let’s create sspadmin id. Create a file named sspadminid.ldif and copy the below content.
dn: cn=sspadmin,ou=service_ids,dc=devopsideas,dc=com cn: sspadmin givenName: SSP Admin sn: id uid: sspadmin objectClass: top objectClass: inetOrgPerson objectClass: person userPassword: {SHA}qUqP5cyxm6YcTAhz05Hph5gvu9M=
Create the id by running the below command,
ldapadd -Z -W -D cn=admin,dc=devopsideas,dc=com -f sspadmin.ldif
Update LDAP ACL to allow password change for sspadmin id.
Create a file named acl.ldif with the below content
dn: olcDatabase={1}mdb,cn=config changetype: modify replace: olcAccess olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none olcAccess: {1}to attrs=shadowLastChange by self write by * read olcAccess: {2}to * by self write by group.exact="cn=sspadmin,ou=service_ids,dc=devopsideas,dc=com" write by users read by * none
Run the below command to make the changes,
ldapmodify -W -D cn=admin,cn=config -f acl.ldif
Verify by Changing password
Access the SSP application, https://<domain name>/ssp. You’ll be prompted for authentication.
Once authenticated, you’ll get the SSP page.
Try changing the password by entering a password that does not meet the password quality rules.
You’ll get a message as below,
Provide a valid credential now and you should be able to change your password. You’ll receive a mail regarding your password change if you had configured mail in the SSP config.
Now the end users have a way to manage their own passwords.
Password expiry notification
Create a file named passwdExpiration.sh with the below content
#!/bin/sh # LDAP host URI MY_LDAP_HOSTURI="localhost" # LDAP root DN MY_LDAP_ROOTDN="cn=sspadmin,ou=service_ids,dc=devopsideas,dc=com" MY_LDAP_ROOTPW="<sspadmin_password>" MY_LDAP_DEFAULTPWDPOLICYDN="cn=userPasswordPolicy,ou=pwpolicies,dc=devopsideas,dc=com" MY_LDAP_SEARCHBASE="ou=people,dc=devopsideas,dc=com" MY_LDAP_SEARCHFILTER="(&(cn=*)(objectClass=inetOrgPerson))" MY_LDAP_SEARCHSCOPE="one" MY_LDAP_SEARCHBIN="/usr/bin/ldapsearch -Z" MY_LDAP_NAME_ATTR=givenName MY_LDAP_LOGIN_ATTR=cn MY_LDAP_MAIL_ATTR=mail MY_MAIL_BODY="From: LDAP Password expiry Alert \n\n \ Hi %name,\n\n \ Your LDAP password is about to expire. Please change your password soon using Self Service Password Portal.\n\n \ Link --> https://ldap.devopsideas.com/ssp \n\n \ \n\n- devopsideas LDAP team." MY_MAIL_SUBJECT="Your LDAP account will expire soon. Please reset your password" MY_MAIL_BIN="mailx" MY_LOG_HEADER="`date +\"%b %e %T\"` `hostname` $0[$$]:" MY_GAWK_BIN="/usr/bin/gawk" getTimeInSeconds() { date=0 os=`uname -s` if [ "$1" ]; then date=`${MY_GAWK_BIN} 'BEGIN { \ if (ARGC == 2) { \ print mktime(ARGV[1]) \ } \ exit 0 }' "$1"` else if [ "${os}" = "SunOS" ]; then date=`/usr/bin/truss /usr/bin/date 2>&1 | nawk -F= \ '/^time\(\)/ {gsub(/ /,"",$2);print $2}'` else now=`date +"%Y %m %d %H %M %S" -u` date=`getTimeInSeconds "$now"` fi fi echo ${date} } tmp_dir="/tmp/$$.checkldap.tmp" result_file="${tmp_dir}/res.tmp.1" buffer_file="${tmp_dir}/buf.tmp.1" ldap_param="-LLL -h ${MY_LDAP_HOSTURI} -x" nb_users=0 nb_expired_users=0 nb_warning_users=0 if [ -d ${tmp_dir} ]; then echo "Error : temporary directory exists (${tmp_dir})" exit 1 fi mkdir ${tmp_dir} if [ ${MY_LDAP_ROOTDN} ]; then ldap_param="${ldap_param} -D ${MY_LDAP_ROOTDN} -w ${MY_LDAP_ROOTPW}" fi ${MY_LDAP_SEARCHBIN} ${ldap_param} -s ${MY_LDAP_SEARCHSCOPE} \ -b "${MY_LDAP_SEARCHBASE}" "${MY_LDAP_SEARCHFILTER}" \ "dn" > ${result_file} while read dnStr do if [ ! "${dnStr}" ]; then continue fi dn=`echo ${dnStr} | cut -d : -f 2` nb_users=`expr ${nb_users} + 1` ${MY_LDAP_SEARCHBIN} ${ldap_param} -s base -b "${dn}" \ ${MY_LDAP_NAME_ATTR} ${MY_LDAP_LOGIN_ATTR} ${MY_LDAP_MAIL_ATTR} pwdChangedTime pwdPolicySubentry \ > ${buffer_file} login=`grep -w "${MY_LDAP_LOGIN_ATTR}:" ${buffer_file} | cut -d : -f 2 \ | sed "s/^ *//;s/ *$//"` name=`grep -w "${MY_LDAP_NAME_ATTR}:" ${buffer_file} | cut -d : -f 2\ | sed "s/^ *//;s/ *$//"` mail=`grep -w "${MY_LDAP_MAIL_ATTR}:" ${buffer_file} | cut -d : -f 2 \ | sed "s/^ *//;s/ *$//"` pwdChangedTime=`grep -w "pwdChangedTime:" ${buffer_file} \ | cut -d : -f 2 | cut -c 1-15 | sed "s/^ *//;s/ *$//"` pwdPolicySubentry=`grep -w "pwdPolicySubentry:" ${buffer_file} \ | cut -d : -f 2 | sed "s/^ *//;s/ *$//"` if [ ! "${pwdChangedTime}" ]; then echo "${MY_LOG_HEADER} No password change date for ${login}" >&2 continue fi if [ ! "${pwdPolicySubentry}" -a ! "${MY_LDAP_DEFAULTPWDPOLICYDN}" ]; then echo "${MY_LOG_HEADER} No password policy for ${login}" >&2 continue fi ldap_search="${MY_LDAP_SEARCHBIN} ${ldap_param} -s base" if [ "${pwdPolicySubentry}" ]; then ldap_search="${ldap_search} -b ${pwdPolicySubentry}" else ldap_search="${ldap_search} -b ${MY_LDAP_DEFAULTPWDPOLICYDN}" fi ldap_search="$ldap_search pwdMaxAge pwdExpireWarning" pwdMaxAge=`${ldap_search} | grep -w "pwdMaxAge:" | cut -d : -f 2 \ | sed "s/^ *//;s/ *$//"` pwdExpireWarning=`${ldap_search} | grep -w "pwdExpireWarning:" | cut -d : -f 2 \ | sed "s/^ *//;s/ *$//"` MY_MAIL_DELAY=${MY_MAIL_DELAY:=$pwdExpireWarning} if [ "${pwdChangedTime}" ]; then s=`echo ${pwdChangedTime} | cut -c 13-14` m=`echo ${pwdChangedTime} | cut -c 11-12` h=`echo ${pwdChangedTime} | cut -c 9-10` d=`echo ${pwdChangedTime} | cut -c 7-8` M=`echo ${pwdChangedTime} | cut -c 5-6` y=`echo ${pwdChangedTime} | cut -c 1-4` currentTime=`getTimeInSeconds` pwdChangedTime=`getTimeInSeconds "$y $M $d $h $m $s"` diffTime=`expr ${currentTime} - ${pwdChangedTime}` fi expireTime=`expr ${pwdChangedTime} + ${pwdMaxAge}` if [ ${currentTime} -gt ${expireTime} ]; then nb_expired_users=`expr ${nb_expired_users} + 1` echo "${MY_LOG_HEADER} Password expired for ${login}" >&2 continue fi if [ "${mail}" -a "${name}" \ -a "${login}" -a "${diffTime}" -a "${pwdMaxAge}" ] then diffTime=`expr ${diffTime} + ${MY_MAIL_DELAY}` if [ ${diffTime} -gt ${pwdMaxAge} ]; then logmsg="${MY_MAIL_BODY}" logmsg=`echo ${logmsg} | sed "s/%name/${name}/; \ s/%login/${login}/"` echo "${logmsg}" | ${MY_MAIL_BIN} -s "${MY_MAIL_SUBJECT}" ${mail} >&2 echo "${MY_LOG_HEADER} Mail sent to user ${login} (${mail})" >&2 nb_warning_users=`expr ${nb_warning_users} + 1` fi fi done < ${result_file} echo "${MY_LOG_HEADER} --- Statistics ---" echo "${MY_LOG_HEADER} Users checked: ${nb_users}" echo "${MY_LOG_HEADER} Account expired: ${nb_expired_users}" echo "${MY_LOG_HEADER} Account in warning: ${nb_warning_users}" rm -rf ${tmp_dir} exit 0
The variables marked in blue are the one’s you need to update. MY_LDAP_DEFAULTPWDPOLICYDN variable defines the password policy that is to be checked for.
Sample script output,
$ ./passwdExpiry.sh Sep 25 13:24:38 ldap ./passwdExpiry.sh[32419]: --- Statistics --- Sep 25 13:24:38 ldap ./passwdExpiry.sh[32419]: Users checked: 2 Sep 25 13:24:38 ldap ./passwdExpiry.sh[32419]: Account expired: 0 Sep 25 13:24:38 ldap ./passwdExpiry.sh[32419]: Account in warning: 0
Run this script as part of cron on daily bases. Based on pwdExpireWarning set as part of ppolicy overlay, user will get notification when the password reaches the threshold period. In this example, we have set the warning period to 7 days before the password expires. Refer ppolicy to get a better context on this.
In the crontab entry, add the below content
00 12 * * * <path to script>/checkLdapPwdExpiration.sh 1>>/var/log/openldap/result.log 2>>/var/log/openldap/audit.log
The cron entry will run the script everyday once at 12 pm
Unlock password
For unlocking password, you just simply need to delete the pwdAccountLockedTime attribute which unlocks the account immediately.
dn: cn=<user id>,ou=people,dc=devopsideas,dc=com
changetype: modify
delete: pwdAccountLockedTime
Running ldap modify command with above content replacing the correct user id will remove the pwdAccountLockedTime attribute if set.
LDAP Backup
Backup is pretty straighforward in LDAP. Using slapcat command against a specific DB will create an LDIF file containing all the data.
Create a file named ldapbackup.sh with the below content
#!/bin/bash BACKUP_PATH=/root/backup SLAPCAT=/usr/sbin/slapcat #today=`date +%Y-%m-%d.%H:%M:%S` nice ${SLAPCAT} -n 0 > ${BACKUP_PATH}/config.ldif nice ${SLAPCAT} -n 1 > ${BACKUP_PATH}/devopsideas.com.ldif nice ${SLAPCAT} -n 2 > ${BACKUP_PATH}/access.ldif chmod 640 ${BACKUP_PATH}/*.ldif
Create a cron entry to run the backup once every day or based on your need. Make sure the path where you are storing the backup files are encrypted. You can make use of encrypted filesystem for this. Also once backed up, you can make use of this backup strategy to store the files in AWS S3.
With this we have successfully implemented all the use cases that was described in the scenario. This concludes the openLDAP series.
Feel free to provide your comments/suggestions if any in the comment section below or through mail ( [email protected]).