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 ) )