OpenLdap aka slapd

Cleaning this up, it was an email . . .

This is going to contain some notes, scripts, and howto's on installing and administering openldap.

I have copious notes and information on this, I will be filling it in as time permits.

The problem with openldap is the examples tend not to use cn=admin,cn=config as the base, and it's confusing

The ACL Nightmare

  • The second problem is usually the permission madness. If you want to use -Y EXTERNAL you better have it in there on every rule with “write.
  • See the script below for some examples 

Syntax Madness

  • If you resort to manually hacking an ldif in …/slapd/ make sure slapd is not running, and remove the CRC check “comments” at the top
  • Finally dumping entries has the most insane format on the planet. The rule is if the first character of the line is a space it is concatenated to the previous line with the space removed. So it breaks at column 72 or 80 or something no matter what….
...... something something-else foo="Someth
 ing Else", wow wassup

Which is fine but you also get weird unseen whitespace that bites you in the a**:
...... word1 word2 word3<a space char>
 word4 word5

it just so happens there is a SPACE after word3 and it works, if you remove the space you get:
...... word1 word2 word3word4 word5, bitch blows up with a cryptic error

When I manually create I never leave spaces at the end, and double them at the beginning of the continuation line
<code>
word word word
  word word

= "word word word word word word"

Lastly queries can return things you do not put in a modify/add like “StructuralObjectClass: …” i.e. Any items that are implied from the DN or system generated like modify timestamps can get returned but not added/updated with a tool. Basically there are two kinds of objects… Objects that identify something, and objects that can contain other objects. That would be a 'StructuralObject', dc=, o=, ou=, or even cn= but not uid= and it's all downhill from there… Most tools will help you.

All the syntax is just abjectly arcane.

Installation and Setup

I have a huge update for this thing. Will update this page as time permits.

What follows is my “replicate” script. Mis-named really, but it sets up openldap for a server pair, primary then secondary. It has an example of pretty much everything, but basically lets you install or reinstall slapd with sane defaults for permissions. Durring install just hit enter on the defaults, it will wipe it out with the info from the definitions at the top of the file. You can use it to create a single instance.

<rant>

I generally use o=Master as a base, and for a level2 item I will add a password which grants permission to the rest of the tree above… So for example:

dc=ksmith.com,o=Master
dc=cfns.net,o=Master
dc=casafiesta.org,o=Master

Represent the bases for a few of the domains in my tree. Each of these dn's has a password that allows blanket r/w access up the tree for that domain. Then I like to create a read-only dn:

cn=lookup,dc=ksmith.com,o=Master

goes up the tree (it can't read passwords, except it's own).

“Standards” would have one creating (3) Three databases with bases dc=com dc=net and dc=org, and build up from there. Or you could use a bottom: o=bottom (master) and build them up:

dc=ksmith,dc=com,o=Master
uid=keith,ou=People,dc=ksmith,dc=com,o=Master

As long as you are only 2 dc's deep it's not too bad. but now I have to deal with

dc=iwojimashipmates,dc=cfns,dc=net,o=Master

which is a seperate entity along with

dc=admin,dc=cfns,dc=net,o=Master

Now just type along with me, and untangle your fingers. And the rules start getting wierd to propogate up the tree so for me it's:

dc=admin.cfns.net,o=Master
dc=iwojimashipmates.cfns.net,o=Master

It's just a hack, but the defaults build these crazy ldap trees, which are a bitch to type, when it's all arbitrary anyway. I mean a tree with a “base” like

dc=mymachine,dc=mydomain,dc=com

Is fine. But I'm not ever going to expand my directory from dc=com out, or even dc=mydomain.. And the content is arbitrary, so you could set your base to:

dc=mymachine.mydomain.com  

So my base dn now doesn't have a bunch of “dc=”'s and commas to trip up my fingers, or use o= or, . . . I don't run ”.com“ so I don't need a huge tree above

</rant>

Have FUN!

The Script

There are a zillion artifacts in this stupid thing… I have a cleaned up update I will get in place here as time permits but the reference material is sound.

./replicate –master -p MYLLDAPPASSWORD

#!/bin/sh
#
#        Script: replicate
#         Title: Create replicated LDAP servers
#
# Last Modified: 2020-04-02 10:08:34
#
dbtype=mdb

rootpass=MYLDAPPASSWORD
#mirror1=prd-auth-1a
#mirror2=prd-auth-1b
#mirror1=qa-auth-01
#mirror2=qa-auth-02
mirror1=dev-auth-01
mirror2=dev-auth-02

BASE_DN="o=Master"
ROOT_DN="cn=Admin,$BASE_DN"
MANAGE_DN="cn=admin,cn=config"

#os=centos7
#os=debian9
os=ubuntu18

case "$os" in
  centos*)
    ldap_user=ldap
    ldap_group=ldap
    etcdir=/etc/openldap
    libdir=/usr/lib64/openldap
    vardir=/var/lib/ldap
    slapd_default=/etc/default/slapd
    dbnum=2
    ;;
  debian*|ubuntu*)
    ldap_user=openldap
    ldap_group=openldap
    etcdir=/etc/ldap
    libdir=/usr/lib/ldap
    vardir=/var/lib/ldap
    slapd_default=/etc/default/slapd
    dbnum=1
    ;;
esac
slapdir=$etcdir/slapd.d
cfgdir="$slapdir/cn=config"


#----------------------------------------------------------------------
# ADD_CONFIG_MIRROR
# Add Mirroring to Config
#----------------------------------------------------------------------
add_config_mirror() {

    pause "--- Remove syncprov, Ignore Errors"
# Kill it if it's there.
    $ldmod <<EOF_ADD_CONFIG_MIRROR1
dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config
changetype: delete
EOF_ADD_CONFIG_MIRROR1
    echo "--- End of error ignore"

# Now Fixup the mirror
    pause "--- Fixup Mirror\n"
    $ldmod <<EOF_ADD_CONFIG_MIRROR2
dn: cn=config
changetype: modify
replace: olcServerID
olcServerID: 1 ldap://${mirror1}
olcServerID: 2 ldap://${mirror2}

dn: olcDatabase={0}config,cn=config
changetype: modify
replace: olcSyncRepl
olcSyncRepl: rid=001 provider=ldap://${mirror1}
  binddn="cn=admin,cn=config" bindmethod=simple
  credentials=${rootpass} searchbase="cn=config" type=refreshAndPersist
  retry="5 6 30 +" timeout=1
olcSyncRepl: rid=002 provider=ldap://${mirror2}
  binddn="cn=admin,cn=config" bindmethod=simple
  credentials=${rootpass} searchbase="cn=config" type=refreshAndPersist
  retry="5 6 30 +" timeout=1
-
replace: olcMirrorMode
olcMirrorMode: TRUE

dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
EOF_ADD_CONFIG_MIRROR2
    [ $? -ne 0 ] && echo "Mirror fix failure"
}

#----------------------------------------------------------------------
# ADD_CONFIG_PASSWORD
# Add olcRootDN/olcRootPW and olcAccess to the cn=config database
#----------------------------------------------------------------------
add_config_password() {
    pause "--- Update cn=config database password/permissions"
    # First Update the config database
    $ldmod <<UPDATE_CONFIG_PASSWORD
dn: olcDatabase={0}config,cn=config
changetype: modify
replace: olcRootDN
olcRootDN: $MANAGE_DN
-
replace: olcRootPW
olcRootPW: $root_pw
-
replace: olcAccess
olcAccess: to *
   by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
   by dn="cn=admin,cn=config" write
UPDATE_CONFIG_PASSWORD
    # ---
}

#----------------------------------------------------------------------
# ADD_CONFIG_SYNC_MODULE
# Add Replication modules to cn=config
#----------------------------------------------------------------------
add_config_sync_module() {
    pause "--- Add Sync Module"
    $ldmod <<ADD_CONFIG_SYNC_MODULE
dn: cn=module,cn=config
changetype: add
objectClass: olcModuleList
cn: module
olcModulePath: $libdir
olcModuleLoad: syncprov
ADD_CONFIG_SYNC_MODULE
}

#----------------------------------------------------------------------
# ADD_DATABASE
# Add a database to the config tree
# Use the same password as for cn=admin,cn=config
#----------------------------------------------------------------------
add_database() {
    pause "--- Add Database ($dbnum/$dbtype)"
    ldifpass="`slappasswd -h '{SSHA}' -s $rootpass`"
    ldapadd -v -Y EXTERNAL -H ldapi:/// <<ADD_DATABASE_EOF
dn: olcDatabase={$dbnum}${dbtype},cn=config
objectClass: olcDatabaseConfig
objectClass: olc${dbtype}Config
olcDatabase: {$dbnum}${dbtype}
olcDbDirectory: /var/lib/ldap
olcSuffix: $BASE_DN
olcLastMod: TRUE
olcLimits: {0}dn.exact="" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
olcRootDN: cn=admin,$BASE_DN
olcRootPW: $ldifpass
olcDbIndex: uid pres,eq
olcDbIndex: cn,sn,mail,givenname pres,eq,approx,sub
olcDbIndex: objectClass eq,pres
olcDbIndex: uidNumber eq
olcDbIndex: gidNumber eq
olcDbIndex: EntryUUID eq
olcDbIndex: ou eq
olcDbIndex: uniqueMember eq
olcDbIndex: memberUid eq
olcDbMaxSize: 107374182400
olcAccess: to dn.regex="(.+,)?(dc=[^,]+,$BASE_DN)\$" attrs=userPassword,shadowLastChange
   by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
   by dn.exact,expand="\$2" write
   by anonymous auth
   by dn="cn=admin,$BASE_DN" write
   by dn="cn=admin,cn=config" write
   by * none
olcAccess: to attrs=userPassword,shadowLastChange
   by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
   by self write
   by anonymous auth
   by dn="cn=admin,$BASE_DN" write
   by dn="cn=admin,cn=config" write
   by * none
olcAccess: to dn.regex=".*cn=([^,]+),ou=Personal,(dc=[^,]+,$BASE_DN)\$"
   by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
   by dn.exact,expand="uid=\$1,ou=People,\$2" write
   by dn.exact,expand="\$2" write
   by dn="cn=admin,$BASE_DN" write
   by dn="cn=admin,cn=config" write
olcAccess: to dn.regex="(.+,)?(dc=[^,]+,$BASE_DN)$"
   by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
   by self write
   by dn="cn=admin,$BASE_DN" write
   by dn="cn=admin,cn=config" write
   by dn.exact,expand="\$2" write
   by dn.subtree,expand="\$2" read
   by dn.exact,expand="cn=lookup,\$2" read
   by dn="cn=lookup,$BASE_DN" read
olcAccess: to *
   by self write
   by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
   by dn="cn=admin,$BASE_DN" write
   by dn="cn=admin,cn=config" write
   by dn="cn=lookup,$BASE_DN" read

ADD_DATABASE_EOF
    cat - > /dev/null <<ADD_DATABASE_EOF2
ADD_DATABASE_EOF2
}

#----------------------------------------------------------------------
# ADD_DATABASE_BASEDN
# Now add the base dn structure to it's database
#----------------------------------------------------------------------
add_database_basedn() {
    pause "Add $BASE_DN to Database Proper"
    $ldmod <<EOF_ADD_BASE_DN
dn: $BASE_DN
changetype: add
objectclass: organization
objectclass: top
o: Master
EOF_ADD_BASE_DN
}

#----------------------------------------------------------------------
# ADD_DATABASE_MIRROR
# Only on the Master
# Add Mirroring to Backend Database Config
#----------------------------------------------------------------------
add_database_mirror() {
#    pause "--- Updating Database ($dbnum/$dbtype): Remove olcOverlay (May Fail)"
#    # echo "$ldmod"
#    $ldmod <<EOF_ADD_DATABASE_MIRROR1
#dn: olcOverlay=syncprov,olcDatabase={$dbnum}${dbtype},cn=config
#changetype: delete
#EOF_ADD_DATABASE_MIRROR1

    pause "--- Updating Database ($dbnum/$dbtype): Add olcSyncRepl olcOverlay, Turn on Mirror Mode"
    $ldmod <<EOF_ADD_DATABASE_MIRROR2
dn: olcDatabase={$dbnum}${dbtype},cn=config
changetype: modify
replace: olcLimits
olcLimits: dn.exact="$MANAGE_DN" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
-
replace: olcSyncRepl
olcSyncRepl: rid=0${dbnum}1 provider=ldap://${mirror1}
  binddn="cn=admin,cn=config" bindmethod=simple
  credentials=${rootpass} searchbase="$BASE_DN" type=refreshAndPersist
  retry="5 6 30 +" timeout=1
olcSyncRepl: rid=0${dbnum}2 provider=ldap://${mirror2}
  binddn="cn=admin,cn=config" bindmethod=simple
  credentials=${rootpass} searchbase="$BASE_DN" type=refreshAndPersist
  retry="5 6 30 +" timeout=1
-
replace: olcMirrorMode
olcMirrorMode: TRUE

dn: olcOverlay=syncprov,olcDatabase={$dbnum}${dbtype},cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
EOF_ADD_DATABASE_MIRROR2

}

#----------------------------------------------------------------------
# ADD_DATABASE_PASSWORD
# Add olcRootDN/olcRootPW to the directory database
#----------------------------------------------------------------------
add_database_password() {
    pause "--- Update MDB database password/permissions"
    $ldmod <<UPDATE_BASE_EOF2
dn: olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: $base_dn
-
replace: olcRootDN
olcRootDN: $root_dn
-
replace: olcRootPW
olcRootPW: $root_pw
UPDATE_BASE_EOF2
    # ---
}

#----------------------------------------------------------------------
# ADD_SCHEMAS
#----------------------------------------------------------------------
add_schemas() {
    pause "--- Add Schema"
    for schema in collective duaconf dyngroup misc openldap ppolicy java \
        samba mozillaabpersonalpha evolutionperson openssh-lpk_openldap
    do
        ldif="$etcdir/schema/$schema.ldif"
        if [ -f "$ldif" ]
        then
            echo "--- Adding Schema: $schema"
            ldapadd -Y EXTERNAL -H ldapi:/// -f $ldif
        fi
    done

}

#----------------------------------------------------------------------
# CMD_ECHO
# Echo then run a command
#----------------------------------------------------------------------
cmd_echo() {
    echo "--- $*"
    eval "$*"
}

#----------------------------------------------------------------------
# PAUSE
# Echo any arguments, wait for a carriage return if debug is on
#----------------------------------------------------------------------
pause() {
    if [ "$debug" = "y" ]
    then
        /bin/echo -e "$* >\c"
        read line
    else
        echo "$*"
    fi
}

#----------------------------------------------------------------------
# CREATE_DIR
# Create/Re-Create LDAP Directory Tree
#----------------------------------------------------------------------
create_dir() {
    pause "--- Re-create $vardir"
    if [ "$vardir" = "" ]
    then
        echo "*** No VARDIR!, Aborting"
        exit 0
    fi
    rm -rf ${vardir}
    mkdir ${vardir}
    chmod 0700 ${vardir}
    chown ${ldap_user}:${ldap_group} ${vardir}
}


#----------------------------------------------------------------------
# INSTALL_SLAPD
# Purge SLAPD if installed
# Install a fresh SLAPD
#----------------------------------------------------------------------
install_slapd() {
    systemctl stop slapd
    case "$os" in
      debian*|ubuntu*)
        # Make sure ldap-utils is installed
        dpkg -l | grep ldap-utils > /dev/null 2>&1 || \
            apt-get -y install ldap-utils
        # Purge slapd if already installed
        dpkg -l | grep slapd > /dev/null 2>&1 && \
            apt-get -y purge slapd &&
            rm -r ${vardir}
        # Install slapd & remove some stuff
        DEBIAN_FRONTEND=noninteractive apt-get install slapd
        #rm -v "${cfgdir}/cn=module{0}.ldif"
        #rm -v "${cfgdir}/cn=schema/cn={1}cosine.ldif"
        #rm -v "${cfgdir}/cn=schema/cn={2}nis.ldif"
        #rm -v "${cfgdir}/cn=schema/cn={3}inetorgperson.ldif"
        ;;
      centos*)
        pause "remove openldap-servers"
        yum -y remove openldap-servers
        [ "$slapdir" != "" ] && rm -rf $slapdir/
        pause "installing openldap-servers"
        yum -y install openldap-servers openldap-clients
        ;;
    esac
    # Stop the server, clear out to only cn=config
    pause "--- Stopping slapd"
    systemctl stop slapd
    rm -v "${cfgdir}/olcDatabase={${dbnum}}"*
    create_dir

    # Update the default startup to fix -h ... issues
    # Restart the server.  Should be a blank cn=config
    # And add a password and permissions
    echo "--- Starting slapd"
    update_host  # Update host file
    systemctl start slapd
    add_config_password
    add_config_sync_module
    add_config_mirror
}

#----------------------------------------------------------------------
# UPDATE_HOST
# In order for syncrepl to work . . .
# Our hostname used in the syncrepl parms, must either reside in
# /etc/hosts as the first item after our IP address or ...
# Invoking slapd -h '...'  one of the arguments must match the syncrepl
# provider URL *EXACTLY*, or and it barfs.
#----------------------------------------------------------------------
update_host() {
    host=`hostname -s`
    ip=`ip address | awk '/inet / && ! /127.0.0.1/ { print $2 ; exit }'`
    if grep " $host " /etc/hosts > /dev/null 2>&1
    then
        return
    fi
    echo "$ip $host" >> /etc/hosts
}

#----------------------------------------------------------------------
# UPDATE_SLAPD_DEFAULT
# Not used . . .  See UPDATE_HOST
#----------------------------------------------------------------------
update_slapd_default() {
    pause "--- Updating $slapd_default"
    cat $slapd_default | awk -v host=`hostname -s` '
/^SLAPD_SERVICES=/ {
    printf "SLAPD_SERVICES=\"ldapi:/// ldap://%s\"\n",host
    next
}
    {
    print $0
}
' > $slapd_default.new
    if diff $slapd_default $slapd_default.new
    then
        rm $slapd_default.new
    else
        mv $slapd_default $slapd_default.old
        mv $slapd_default.new $slapd_default
    fi
}


#----------------------------------------------------------------------
# UPDATE_SLAVE_FOLDERS
#----------------------------------------------------------------------
update_slave_folders() {
    pause "--- update_slave_folders"
    dbnum=1
    while [ -f "$slapdir/cn=config/olcDatabase={$dbnum}${dbtype}.ldif" ]
    do
        eval `cat "$slapdir/cn=config/olcDatabase={$dbnum}${dbtype}.ldif" | awk '
# /olcDbDirectory:/ { printf "db_folder=\"%s\"\n",$2 }
'` # `'
        mkdir $db_folder
        chown 0700 $db_folder
        chown ${ldap_user}:${ldap_group} $db_folder
        dbnum=`expr $dbnum + 1`
    done
}

#----------------------------------------------------------------------
# MAIN
#----------------------------------------------------------------------
echo "LDAP Replication Script"

debug=n
while [ "x$1" != "x" ]
do
    case "x$1" in
      x--debug)
        debug=y
        ;;
      x--master)
        function=master
        ;;
      x--slave)
        function=slave
        ;;
      x--password|x-p)
        shift
        rootpass="$1"
        ;;
    esac
    shift
done

if [ "$rootpass" = "" ]
then
    echo "Root Password Not Defined"
    exit 1
fi
# This is the LDAP Version of the password
root_pw=`slappasswd -s $rootpass`

# ldadd="ldapadd -v -h localhost -x -D cn=admin,cn=config -w $rootpass"
ldmod="ldapmodify -v -Y EXTERNAL -H ldapi:///"

case "$function" in
  master)
    install_slapd
    add_schemas
    add_database
    add_database_basedn
    add_database_mirror
    ;;
  slave)
    install_slapd
    ;;
esac
openldap.txt · Last modified: 2022-11-29 10:57 by ksadmin
CC Attribution-Noncommercial-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0