From 40d054794c0dffac2b39c3cda8ba0d528a36ce26 Mon Sep 17 00:00:00 2001 From: Scott Koranda Date: Sun, 20 May 2018 16:27:02 -0500 Subject: [PATCH] voPerson and refactor slapd entrypoint script Add voPerson LDAP schema to image and refactor the slapd entrypoint script so that additional schemas are checked for and added if necessary during each start. Additionally the TLS configuration is also managed during each start instead of only once at bootstrap. Also added synprov module to bootstrap. --- comanage-registry-slapd/Dockerfile | 1 + .../docker-slapd-entrypoint | 340 ++++++++++++++---- comanage-registry-slapd/voperson.ldif | 34 ++ 3 files changed, 306 insertions(+), 69 deletions(-) create mode 100644 comanage-registry-slapd/voperson.ldif diff --git a/comanage-registry-slapd/Dockerfile b/comanage-registry-slapd/Dockerfile index 494a22c..03729bd 100644 --- a/comanage-registry-slapd/Dockerfile +++ b/comanage-registry-slapd/Dockerfile @@ -36,6 +36,7 @@ ENV OLC_ROOT_PW ${OLC_ROOT_PW:-password} COPY eduperson.ldif /etc/ldap/schema/ COPY openssh-lpk.ldif /etc/ldap/schema/ +COPY voperson.ldif /etc/ldap/schema/ VOLUME [ "/var/lib/ldap", "/etc/ldap/slapd.d" ] diff --git a/comanage-registry-slapd/docker-slapd-entrypoint b/comanage-registry-slapd/docker-slapd-entrypoint index 2a83694..c258f4e 100755 --- a/comanage-registry-slapd/docker-slapd-entrypoint +++ b/comanage-registry-slapd/docker-slapd-entrypoint @@ -21,42 +21,76 @@ set -e -# Copy secrets and other deployment specific details into place. -if [[ -f "${SLAPD_CERT_FILE}" ]]; then - cp ${SLAPD_CERT_FILE} /etc/ldap/slapd.crt -fi - -if [[ -f "${SLAPD_PRIVKEY_FILE}" ]]; then - cp ${SLAPD_PRIVKEY_FILE} /etc/ldap/slapd.key -fi - -if [[ -f "${SLAPD_CHAIN_FILE}" ]]; then - cp ${SLAPD_CHAIN_FILE} /etc/ldap/slapd.ca.crt -fi - -if [[ -f "${OLC_ROOT_PW_FILE}" ]]; then - OLC_ROOT_PW=`cat ${OLC_ROOT_PW_FILE}` -fi - -# Only bootstrap the directory if it does not already exist. -if [[ ! -f /var/lib/ldap/data.mdb && ! -f /etc/ldap/slapd.d/cn=config.ldif ]]; then - SUFFIX="${OLC_SUFFIX:-dc=my,dc=org}" - ROOT_DN="${OLC_ROOT_DN:-cn=admin,dc=my,dc=org}" - ROOT_PW="${OLC_ROOT_PW:-password}" +ADDED_SCHEMAS="eduperson openssh-lpk voperson" +SCHEMA_DIR="/etc/ldap/schema" + +########################################## +# Add a hyphen to an LDIF file to indicate multiple ldapmodify entries. +# Globals: +# None +# Arguments: +# None +# Returns: +# None +########################################## +function add_hyphen() { + local file_name="$1" + if [[ -s $file_name ]]; then + echo "-" >> $file_name + fi +} + +########################################## +# Add additional schemas if necessary. +# Globals: +# ADDED_SCHEMAS +# SCHEMA_DIR +# Arguments: +# None +# Returns: +# None +########################################## +function add_schemas() { + local schema_name + for schema_name in ${ADDED_SCHEMAS}; do + if ! schema_installed $schema_name && schema_defined $schema_name; then + ldapmodify -Y EXTERNAL -H ldapi:/// -a \ + -f "$SCHEMA_DIR/$schema_name.ldif" > /dev/null 2>&1 + fi + done +} + +########################################## +# Bootstrap the directory. +# Globals: +# OLC_SUFFIX +# OLC_ROOT_DN +# OLC_ROOT_PW +# Arguments: +# None +# Returns: +# None +########################################## +function bootstrap() { + local suffix="${OLC_SUFFIX:-dc=my,dc=org}" + local root_dn="${OLC_ROOT_DN:-cn=admin,dc=my,dc=org}" + local root_pw="${OLC_ROOT_PW:-password}" # Parse the domain, rdn, and the value of rdn from the OLC_SUFFIX - DOMAIN=`echo ${SUFFIX} | sed -e 's/dc=//g' -e 's/,/./g'` - RDN=`echo ${SUFFIX} | sed -E -e 's/^([^=]+)=[^=,]+.*/\1/'` - RDN_VALUE=`echo ${SUFFIX} | sed -E -e 's/^[^=]+=([^=,]+).*/\1/'` + local domain=`echo ${suffix} | sed -e 's/dc=//g' -e 's/,/./g'` + local rdn=`echo ${suffix} | sed -E -e 's/^([^=]+)=[^=,]+.*/\1/'` + local rdn_value=`echo ${suffix} | sed -E -e 's/^[^=]+=([^=,]+).*/\1/'` # Parse the rdn and its value from the OLC_ROOT_DN - ADMIN_RDN=`echo ${ROOT_DN} | sed -E -e 's/^([^=]+)=[^=,]+.*/\1/'` - ADMIN_RDN_VALUE=`echo ${ROOT_DN} | sed -E -e 's/^[^=]+=([^=,]+).*/\1/'` + local admin_rdn=`echo ${root_dn} | sed -E -e 's/^([^=]+)=[^=,]+.*/\1/'` + local admin_rdn_value=`echo ${root_dn} | \ + sed -E -e 's/^[^=]+=([^=,]+).*/\1/'` # Create a temporary password and its hash that will be used to # bootstrap the OLC_SUFFIX. It is later replaced by the OLC_ROOT_PW hash. - OLC_ROOT_PW_TMP=`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1` - OLC_ROOT_PW_TMP_HASH=`/usr/sbin/slappasswd -s ${OLC_ROOT_PW_TMP}` + local olc_root_pw_tmp=`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | \ + fold -w 32 | head -n 1` + local olc_root_pw_tmp_hash=`/usr/sbin/slappasswd -s ${olc_root_pw_tmp}` # Copy over the distribution files created by Debian installation of slapd # so that we can start slapd. @@ -69,7 +103,7 @@ if [[ ! -f /var/lib/ldap/data.mdb && ! -f /etc/ldap/slapd.d/cn=config.ldif ]]; t chown -R openldap:openldap /etc/ldap/slapd.d # Start slapd listening only on socket. - slapd -h ldapi:/// -u openldap -g openldap > /dev/null 2>&1 + start_slapd_socket # Reconfigure slapd to look in /var/lib/ldap.dist for the default # directory created by the Debian slapd installation. @@ -86,31 +120,46 @@ EOF # Kill slapd and remove the directory created by the Debian installation # that was copied over and used to allow slapd to start initially. - kill -INT `cat /var/run/slapd/slapd.pid` - sleep 1 - + stop_slapd_socket rm -f /var/lib/ldap/* # Start slapd again listening only on socket. - slapd -h ldapi:/// -u openldap -g openldap > /dev/null 2>&1 + start_slapd_socket + + # Load the syncprov module. + cat < /tmp/modify.ldif +dn: cn=module{0},cn=config +changetype: modify +add: olcModuleLoad +olcModuleLoad: syncprov +EOF + + ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/modify.ldif > /dev/null 2>&1 + + rm -f /tmp/modify.ldif > /dev/null 2>&1 - # Configure the directory with the injected suffix but the temporary password. + # Configure the directory with the injected suffix but the temporary + # password. cat < /tmp/modify.ldif dn: olcDatabase={2}mdb,cn=config objectClass: olcDatabaseConfig objectClass: olcMdbConfig olcDatabase: {2}mdb olcDbDirectory: /var/lib/ldap -olcSuffix: ${SUFFIX} +olcSuffix: ${suffix} olcLastMod: TRUE -olcRootDN: ${ROOT_DN} -olcRootPW: ${OLC_ROOT_PW_TMP_HASH} -olcAccess: {0}to dn.base="${ROOT_DN}" by sockname.regex=/var/run/slapd/ldapi auth by users none by * none -olcAccess: {1}to attrs=userPassword,shadowLastChange by self auth by anonymous auth by * none +olcRootDN: ${root_dn} +olcRootPW: ${olc_root_pw_tmp_hash} +olcAccess: {0}to dn.base="${root_dn}" by sockname.regex=/var/run/slapd/ + ldapi auth by users none by * none +olcAccess: {1}to attrs=userPassword,shadowLastChange by self auth by an + onymous auth by * none olcAccess: {2}to * by * none olcDbCheckpoint: 512 30 olcDbIndex: objectClass eq,pres olcDbIndex: ou,cn,mail,surname,givenname eq,pres,sub +olcDbIndex: entryCSN eq +olcDbIndex: entryUUID eq olcDbMaxSize: 1073741824 EOF @@ -136,21 +185,22 @@ EOF # Create the actual contents of the directory and the admin DN # with the injected password hash. cat < /tmp/modify.ldif -dn: ${SUFFIX} +dn: ${suffix} objectClass: dcObject objectClass: organization -o: ${DOMAIN} -${RDN}: ${RDN_VALUE} +o: ${domain} +${rdn}: ${rdn_value} -dn: ${ROOT_DN} +dn: ${root_dn} objectClass: simpleSecurityObject objectClass: organizationalRole -${ADMIN_RDN}: ${ADMIN_RDN_VALUE} +${admin_rdn}: ${admin_rdn_value} description: LDAP administrator -userPassword: ${ROOT_PW} +userPassword: ${root_pw} EOF - ldapmodify -x -D ${ROOT_DN} -w ${OLC_ROOT_PW_TMP} -H ldapi:/// -a -f /tmp/modify.ldif > /dev/null 2>&1 + ldapmodify -x -D ${root_dn} -w ${olc_root_pw_tmp} -H ldapi:/// -a \ + -f /tmp/modify.ldif > /dev/null 2>&1 rm -f /tmp/modify.ldif > /dev/null 2>&1 @@ -165,52 +215,204 @@ EOF rm -f /tmp/modify.ldif > /dev/null 2>&1 - # Configure TLS if cert and key available. + # Add the syncprov overlay. + cat < /tmp/modify.ldif +dn: olcOverlay=syncprov,olcDatabase={2}mdb,cn=config +changetype: add +objectClass: olcSyncProvConfig +olcOverlay: syncprov +olcSpCheckpoint: 10 1 +EOF + + ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/modify.ldif > /dev/null 2>&1 + + rm -f /tmp/modify.ldif > /dev/null 2>&1 + + # Stop slapd. + stop_slapd_socket +} + +########################################## +# Configure TLS if necessary files exist. +# Globals: +# None +# Arguments: +# None +# Returns: +# None +########################################## +function configure_tls() { if [[ -f /etc/ldap/slapd.crt && -f /etc/ldap/slapd.key ]]; then - cat < /tmp/modify.ldif -dn: cn=config -changetype: modify + local ldif=/tmp/add.ldif + touch $ldif + + if ! tls_attribute_exists olcTLSCertificateFile; then + cat <> $ldif add: olcTLSCertificateFile olcTLSCertificateFile: /etc/ldap/slapd.crt -- +EOF + fi + + if ! tls_attribute_exists olcTLSCertificateKeyFile; then + add_hyphen $ldif + cat <> $ldif add: olcTLSCertificateKeyFile olcTLSCertificateKeyFile: /etc/ldap/slapd.key -- +EOF + fi + + if ! tls_attribute_exists olcTLSCipherSuite; then + add_hyphen $ldif + cat <> $ldif add: olcTLSCipherSuite olcTLSCipherSuite: SECURE256 -- +EOF + fi + + if ! tls_attribute_exists olcTLSProtocolMin; then + add_hyphen $ldif + cat <> $ldif add: olcTLSProtocolMin olcTLSProtocolMin: 3.2 EOF + fi + if [[ -f /etc/ldap/slapd.ca.crt ]]; then - cat <> /tmp/modify.ldif -- + if ! tls_attribute_exists olcTLSCACertificateFile; then + add_hyphen $ldif + cat <> $ldif add: olcTLSCACertificateFile olcTLSCACertificateFile: /etc/ldap/slapd.ca.crt EOF + fi fi - ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/modify.ldif > /dev/null 2>&1 - - rm -f /tmp/modify.ldif > /dev/null 2>&1 + if [[ -s $ldif ]]; then + cat < /tmp/modify.ldif +dn: cn=config +changetype: modify +EOF + cat $ldif >> /tmp/modify.ldif + ldapmodify -Y EXTERNAL -H ldapi:/// -c \ + -f /tmp/modify.ldif > /dev/null 2>&1 + rm -f /tmp/modify.ldif > /dev/null 2>&1 + rm -f $ldif > /dev/null 2>&1 + fi + fi +} + +########################################## +# Determine if TLS attribute already exists. +# Globals: +# None +# Arguments: +# None +# Returns: +# None +########################################## +function tls_attribute_exists() { + local attribute="$1" + ldapsearch -LLL -Y EXTERNAL -H ldapi:/// \ + -b cn=config -s base $attribute 2>/dev/null \ + | grep $attribute > /dev/null 2>&1 +} + +########################################## +# Determine if a schema is installed. +# Globals: +# None +# Arguments: +# schema name +# Returns: +# None +########################################## +function schema_installed() { + local schema_name="$1" + local filter="(&(cn={*}$schema_name)(objectClass=olcSchemaConfig))" + + ldapsearch -LLL -Y EXTERNAL -H ldapi:/// \ + -b cn=schema,cn=config $filter dn 2>/dev/null \ + | grep $schema_name > /dev/null 2>&1 +} + +########################################## +# Determine if a schema is defined. +# Globals: +# None +# Arguments: +# schema name +# Returns: +# None +########################################## +function schema_defined() { + local schema_name="$1" + + [[ -e "$SCHEMA_DIR/$schema_name.ldif" ]] +} + +########################################## +# Start slapd listening only on UNIX socket. +# Globals: +# None +# Arguments: +# None +# Returns: +# None +########################################## +function start_slapd_socket() { + slapd -h ldapi:/// -u openldap -g openldap > /dev/null 2>&1 +} + +########################################## +# Stop slapd. +# Globals: +# None +# Arguments: +# None +# Returns: +# None +########################################## +function stop_slapd_socket() { + kill -INT `cat /var/run/slapd/slapd.pid` + sleep 1 +} - fi # Done configuring TLS if cert and key available. +# Copy secrets and other deployment specific details into place. +if [[ -f "${SLAPD_CERT_FILE}" ]]; then + cp ${SLAPD_CERT_FILE} /etc/ldap/slapd.crt +fi - # Add eduPerson, eduMember, and openssh-lpk schemas. - ldapmodify -Y EXTERNAL -H ldapi:/// -a -f /etc/ldap/schema/eduperson.ldif 2>&1 - ldapmodify -Y EXTERNAL -H ldapi:/// -a -f /etc/ldap/schema/openssh-lpk.ldif 2>&1 +if [[ -f "${SLAPD_PRIVKEY_FILE}" ]]; then + cp ${SLAPD_PRIVKEY_FILE} /etc/ldap/slapd.key +fi - # Stop slapd. - kill -INT `cat /var/run/slapd/slapd.pid` - sleep 1 +if [[ -f "${SLAPD_CHAIN_FILE}" ]]; then + cp ${SLAPD_CHAIN_FILE} /etc/ldap/slapd.ca.crt +fi - # Fix user and group. - chown -R openldap:openldap /var/lib/ldap - chown -R openldap:openldap /etc/ldap/slapd.d +if [[ -f "${OLC_ROOT_PW_FILE}" ]]; then + OLC_ROOT_PW=`cat ${OLC_ROOT_PW_FILE}` +fi +# Only bootstrap the directory if it does not already exist. +if [[ ! -f /var/lib/ldap/data.mdb && \ + ! -f /etc/ldap/slapd.d/cn=config.ldif ]]; then + bootstrap fi +# Start slapd listening only on UNIX socket. +start_slapd_socket + +# Add extra schemas not included with Debian OpenLDAP. +add_schemas + +# Configure TLS. +configure_tls + +# Stop slapd listening on UNIX socket. +stop_slapd_socket + # Always set user and group in case external source of user and # group mappings to numeric UID and GID is being used, such as # COPY in of /etc/passwd. diff --git a/comanage-registry-slapd/voperson.ldif b/comanage-registry-slapd/voperson.ldif new file mode 100644 index 0000000..aeb5dce --- /dev/null +++ b/comanage-registry-slapd/voperson.ldif @@ -0,0 +1,34 @@ +dn: cn=voperson,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: voperson +olcAttributeTypes: {0}( 1.3.6.1.4.1.34998.3.3.1.1 NAME 'voPersonApplicationUID + ' DESC 'voPerson Application-Specific User Identifier' EQUALITY caseIgnoreMat + ch SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' ) +olcAttributeTypes: {1}( 1.3.6.1.4.1.34998.3.3.1.2 NAME 'voPersonAuthorName' DE + SC 'voPerson Author Name' EQUALITY caseIgnoreMatch SYNTAX '1.3.6.1.4.1.1466.1 + 15.121.1.15' ) +olcAttributeTypes: {2}( 1.3.6.1.4.1.34998.3.3.1.3 NAME 'voPersonCertificateDN' + DESC 'voPerson Certificate Distinguished Name' EQUALITY distinguishedNameMat + ch SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' ) +olcAttributeTypes: {3}( 1.3.6.1.4.1.34998.3.3.1.4 NAME 'voPersonCertificateIss + uerDN' DESC 'voPerson Certificate Issuer DN' EQUALITY distinguishedNameMatch + SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' ) +olcAttributeTypes: {4}( 1.3.6.1.4.1.34998.3.3.1.5 NAME 'voPersonExternalID' DE + SC 'voPerson Scoped External Identifier' EQUALITY caseIgnoreMatch SYNTAX '1.3 + .6.1.4.1.1466.115.121.1.15' ) +olcAttributeTypes: {5}( 1.3.6.1.4.1.34998.3.3.1.6 NAME 'voPersonID' DESC 'voPe + rson Unique Identifier' EQUALITY caseIgnoreMatch SYNTAX '1.3.6.1.4.1.1466.115 + .121.1.15' ) +olcAttributeTypes: {6}( 1.3.6.1.4.1.34998.3.3.1.7 NAME 'voPersonPolicyAgreemen + t' DESC 'voPerson Policy Agreement Indicator' EQUALITY caseIgnoreMatch SYNTAX + '1.3.6.1.4.1.1466.115.121.1.15' ) +olcAttributeTypes: {7}( 1.3.6.1.4.1.34998.3.3.1.8 NAME 'voPersonSoRID' DESC 'v + oPerson External Identifier' EQUALITY caseIgnoreMatch SYNTAX '1.3.6.1.4.1.146 + 6.115.121.1.15' ) +olcAttributeTypes: {8}( 1.3.6.1.4.1.34998.3.3.1.9 NAME 'voPersonStatus' DESC ' + voPerson Status' EQUALITY caseIgnoreMatch SYNTAX '1.3.6.1.4.1.1466.115.121.1. + 15' ) +olcObjectClasses: {0}( 1.3.6.1.4.1.34998.3.3.1 NAME 'voPerson' AUXILIARY MAY ( + voPersonApplicationUID $ voPersonAuthorName $ voPersonCertificateDN $ voPers + onCertificateIssuerDN $ voPersonExternalID $ voPersonID $ voPersonPolicyAgree + ment $ voPersonSoRID $ voPersonStatus ) )