Thursday, January 17, 2019

389 Directory Server 1.3.x LDAP client authentication

389 Directory Server 1.3.x LDAP client authentication

Last time we did a multi-master replication setup, see 389 Directory Server 1.3.x Replication.

Setting up LDAP client authentication on RHEL7 is next on the list. Let's install and configure SSSD today.

[root@ldap01 ~]# yum -y install sssd-ldap
...

Since we don't allow anonymous binds (nsslapd-allow-anonymous-access: rootdse), we need a special proxy user with no password expiration for this to work.

dirsrv@ldap01 $ PAPW="$(pwmake 192)"
dirsrv@ldap01 $ echo -n "$PAPW" > /etc/dirsrv/.papw
dirsrv@ldap01 $ chmod 400 /etc/dirsrv/.papw
dirsrv@ldap01 $ ldapadd -D "cn=Directory Manager" -y /etc/dirsrv/.dmpw -x -ZZ
dn: cn=proxyagent,ou=Special Users,dc=unix,dc=mycompany,dc=com
objectclass: top
objectclass: organizationalRole
objectClass: simpleSecurityObject
cn: proxyagent
userPassword: $(pwdhash -s PBKDF2_SHA256 $(< /etc/dirsrv/.papw))
passwordExpirationTime: 20380119031407Z
^D
adding new entry "cn=proxyagent,ou=Special Users,dc=unix,dc=mycompany,dc=com"

With the proxyagent user in place we can configure SSSD now.

[root@ldap01 ~]# cat << EOF > /etc/sssd/sssd.conf
[sssd]
services = nss, pam
domains = LDAP

[nss]
filter_users = root
filter_groups = root
entry_cache_nowait_percentage = 75

[pam]
offline_credentials_expiration = 2
offline_failed_login_attempts = 3
offline_failed_login_delay = 5

[domain/LDAP]
id_provider = ldap
auth_provider = ldap
chpass_provider = ldap

ldap_uri = ldap://ldap01.mycompany.com,ldap://ldap02.mycompany.com
ldap_search_base = dc=unix,dc=mycompany,dc=com
ldap_default_bind_dn = cn=proxyagent,ou=Special Users,dc=unix,dc=mycompany,dc=com
ldap_default_authtok = $(< /etc/dirsrv/.papw)

ldap_tls_reqcert = demand
ldap_tls_cacert = /etc/openldap/certs/CA.crt
ldap_id_use_start_tls = true
enumerate = true
EOF

[root@ldap01 ~]# chmod 600 /etc/sssd/sssd.conf
[root@ldap01 ~]# authconfig --enablesssd --enablesssdauth --enablemkhomedir --update
[root@ldap01 ~]# systemctl enable sssd && systemctl start sssd

[root@ldap01 ~]# getent passwd smiddy6
smiddy6:x:1125:1001:Shrinkers Smiddy, smiddy6@mycompany.com:/home/smiddy6:/bin/bash
[root@ldap01 ~]# getent group uat
uat:*:1002:
[root@ldap01 ~]# ssh smiddy6@$(hostname -s)
smiddy6@ldap01's password:
Password expired. Change your password now.
WARNING: Your password has expired.
You must change your password now and login again!
Changing password for user smiddy6.
Current Password: ...

Very good.

Links

Thursday, January 10, 2019

389 Directory Server 1.3.x Replication

389 Directory Server 1.3.x Replication

Last time we added users and groups to our directory, see 389 Directory Server 1.3.x Users, Groups and ACIs.

Let's create some redundancy by using multi-master replication today. The outline is as following (first supplier is ldap01, second supplier will be ldap02):

  • Install a second VM with an empty userRoot, but with the same TLS and password policy settings
  • Enable the change log
  • Configure read-write replicas (replication settings and replication manager)
  • Set up replication agreements
  • Start the replication on the first supplier, pushing all changes to the second supplier

We have to create another 389-ds instance on a new VM, see 389 Directory Server 1.3.x on Red Hat Enterprise Linux 7.6. We just have to change a single line in setup.inf. Instead of /tmp/userRoot.ldif make it InstallLdifFile=none.

Done that? Fresh 389-ds instance ldap02 up and running? Same suffix (dc=unix,dc=mycompany,dc=com)? SSL key/certificate created and TLS enabled? Password policy (you can't set passwordAdminDN right now because the directory is empty, you'll have to do that later when both directory servers are in sync) in place? Good!

Let's start with the changelog, replica settings and replication manager on ldap01.

dirsrv@ldap01 $ ldapadd -D "cn=Directory Manager" -y /etc/dirsrv/.dmpw -x -ZZ
dn: cn=changelog5,cn=config
changetype: add
objectClass: top
objectClass: extensibleObject
cn: changelog5
nsslapd-changelogdir: /var/lib/dirsrv/slapd-$(hostname -s)/changelogdb
# Red Hat recommends setting the maximum changelog age to 7 days.
# the following attribute should match nsds5ReplicaPurgeDelay (604800s == 7d)
nsslapd-changelogmaxage: 7d

dn: cn=replica,cn=dc\=unix\,dc\=mycompany\,dc\=com,cn=mapping tree,cn=config
changetype: add
objectClass: top
objectClass: nsds5replica
objectClass: extensibleObject
cn: replica
nsDS5ReplicaRoot: dc=unix,dc=mycompany,dc=com
# set a unique ID for suppliers
nsDS5ReplicaId: 1
# 3 consumer/supplier (updateable)
nsDS5ReplicaType: 3
# 1: The replica writes to the changelog; this is the default for hubs and suppliers.
nsDS5Flags: 1
# the following attribute should match nsslapd-changelogmaxage (7d == 604800s)
nsDS5ReplicaPurgeDelay: 604800
nsDS5ReplicaBindDN: cn=Replication Manager,cn=config
^D

dirsrv@ldap01 $ RMPW="$(pwmake 192)"
dirsrv@ldap01 $ echo -n $RMPW > /etc/dirsrv/.rmpw
dirsrv@ldap01 $ chmod 400 /etc/dirsrv/.rmpw
dirsrv@ldap01 $ ldapadd -D "cn=Directory Manager" -xy /etc/dirsrv/.dmpw -ZZ
dn: cn=Replication Manager,cn=config
objectClass: top
objectClass: device
objectClass: simpleSecurityObject
cn: Replication Manager
userPassword: $(pwdhash -s PBKDF2_SHA256 $(< /etc/dirsrv/.rmpw))
passwordExpirationTime: 20380119031407Z
nsIdleTimeout: 0
^D

Let's do the same on ldap02. The only difference here is nsDS5ReplicaId which has to be unique.

dirsrv@ldap02 $ ldapadd -D "cn=Directory Manager" -y /etc/dirsrv/.dmpw -x -ZZ
dn: cn=changelog5,cn=config
changetype: add
objectClass: top
objectClass: extensibleObject
cn: changelog5
nsslapd-changelogdir: /var/lib/dirsrv/slapd-$(hostname -s)/changelogdb
nsslapd-changelogmaxage: 7d

dn: cn=replica,cn=dc\=unix\,dc\=mycompany\,dc\=com,cn=mapping tree,cn=config
changetype: add
objectClass: top
objectClass: nsds5replica
objectClass: extensibleObject
cn: replica
nsDS5ReplicaRoot: dc=unix,dc=mycompany,dc=com
# set a unique ID for suppliers!
nsDS5ReplicaId: 2
nsDS5ReplicaType: 3
nsDS5Flags: 1
nsDS5ReplicaPurgeDelay: 604800
nsDS5ReplicaBindDN: cn=Replication Manager,cn=config
^D

dirsrv@ldap02 $ RMPW="$(pwmake 192)"
dirsrv@ldap02 $ echo -n $RMPW > /etc/dirsrv/.rmpw
dirsrv@ldap02 $ chmod 400 /etc/dirsrv/.rmpw
dirsrv@ldap02 $ ldapadd -D "cn=Directory Manager" -xy /etc/dirsrv/.dmpw -ZZ
dn: cn=Replication Manager,cn=config
objectClass: top
objectClass: device
objectClass: simpleSecurityObject
cn: Replication Manager
userPassword: $(pwdhash -s PBKDF2_SHA256 $(< /etc/dirsrv/.rmpw))
passwordExpirationTime: 20380119031407Z
nsIdleTimeout: 0

That's it for ldap02 for now, back to ldap01. Next on the list is the replication agreement. We need the clear-text Replication Manager password (see /etc/dirsrv/.rmpw) from ldap02 for this. Let's do a connection test, too, before we start the replication refresh.

dirsrv@ldap01 $ openssl s_client -connect ldap02.mycompany.com:389 -starttls ldap -CAfile /etc/openldap/certs/CA.crt < /dev/null
...
    Verify return code: 0 (ok)
...
dirsrv@ldap01 $ ldapadd -D "cn=Directory Manager" -xy /etc/dirsrv/.dmpw -ZZ
dn: cn=ReplicaAgreement,cn=replica,cn=dc\=unix\,dc\=mycompany\,dc\=com,cn=mapping tree,cn=config
objectClass: top
objectClass: nsds5ReplicationAgreement
cn: ReplicaAgreement
nsDS5ReplicaHost: ldap02.mycompany.com
nsDS5ReplicaPort: 389
nsDS5ReplicaBindDN: cn=Replication Manager,cn=config
nsDS5ReplicaCredentials: ***clear-text RMPW from ldap02***
nsDS5ReplicaBindMethod: SIMPLE
nsDS5ReplicaTransportInfo: TLS
nsDS5ReplicaRoot: dc=unix,dc=mycompany,dc=com
description: replication agreement between ldap01 and ldap02
nsDS5ReplicaUpdateSchedule: 0000-2359 0123456

dn: cn=ReplicaAgreement,cn=replica,cn=dc\=unix\,dc\=mycompany\,dc\=com,cn=mapping tree,cn=config
changetype: modify
replace: nsDS5BeginReplicaRefresh
nsDS5BeginReplicaRefresh: start
^D

dirsrv@ldap01 $ tail /var/log/dirsrv/slapd-$(hostname -s)/errors
[...] NSMMReplicationPlugin - repl5_tot_run - Finished total update of replica "agmt="cn=ReplicaAgreement" (ldap02:389)". Sent 207 entries.

dirsrv@ldap02 $ tail /var/log/dirsrv/slapd-$(hostname -s)/errors
[...] NSMMReplicationPlugin - multimaster_be_state_change - Replica dc=unix,dc=mycompany,dc=com is coming online; enabling replication

And do the same on ldap02. We're not starting a nsDS5BeginReplicaRefresh here because the sync was already done on ldap01.

dirsrv@ldap02 $ ldapadd -D "cn=Directory Manager" -xy /etc/dirsrv/.dmpw -ZZ
dn: cn=ReplicaAgreement,cn=replica,cn=dc\=unix\,dc\=mycompany\,dc\=com,cn=mapping tree,cn=config
objectClass: top
objectClass: nsds5ReplicationAgreement
cn: ReplicaAgreement
nsDS5ReplicaHost: ldap01.mycompany.com
nsDS5ReplicaPort: 389
nsDS5ReplicaBindDN: cn=Replication Manager,cn=config
nsDS5ReplicaCredentials: ***clear-text RMPW from ldap01 ***
nsDS5ReplicaBindMethod: SIMPLE
nsDS5ReplicaTransportInfo: TLS
nsDS5ReplicaRoot: dc=unix,dc=mycompany,dc=com
description: replication agreement between ldap02 and ldap01
nsDS5ReplicaUpdateSchedule: 0000-2359 0123456
^D

dirsrv@ldap02 $ ldapsearch -LLL -D "cn=Directory Manager" -xy /etc/dirsrv/.dmpw -ZZ \
  -b 'cn=ReplicaAgreement,cn=replica,cn=dc\=unix\,dc\=mycompany\,dc\=com,cn=mapping tree,cn=config' \
  nsds5replicaLastUpdateStatus
dn: cn=ReplicaAgreement,cn=replica,cn=dc\3Dunix\2Cdc\3Dmycompany\2Cdc\3Dcom,cn
 =mapping tree,cn=config
nsds5replicaLastUpdateStatus: Error (0) Replica acquired successfully: Increme
 ntal update succeeded

And that's it. Our directory is nicely replicated to another instance and all changes made on any server are transferred to the other now.

Read the next part at 389 Directory Server 1.3.x LDAP client authentication.

Links

Tuesday, January 8, 2019

389 Directory Server 1.3.x Users, Groups and ACIs

389 Directory Server 1.3.x Users, Groups and ACIs

Now that our password policy is in place (see 389 Directory Server 1.3.x Password Policy), it's time to add some users and groups to our directory.

Let's generate some random users to get started and three POSIX + organizational groups as well.

[root@ldap01 ~]# yum -y install words
...
dirsrv@ldap01 $ cat << EOF > genUsers.sh
#!/bin/bash

set -o errexit
set -o nounset
set -o pipefail

LDAP_GRP=(PROD UAT DEV)
declare -A LDAP_GRPID=([PROD]=1001 [UAT]=1002 [DEV]=1003)
NAMES=( $(egrep -v '[[:punct:]]' /usr/share/dict/words) )

NUMNAMES="${#NAMES[*]}"

RNDNR=( $(shuf -i 0-$NUMNAMES -n 400) )

mkdir -p People

for i in {0..200}; do
  GN="${NAMES[${RNDNR[$i]}]}"
  SN="${NAMES[${RNDNR[$i+100]}]}"
  UIDN="$(( 1001 + $i ))"
  UIDL="${SN:0:6}$(( $RANDOM % 99 ))"
  UIDL="${UIDL,,}"
  GRPRND="$(( $RANDOM % 3 ))"
  GRPL="${LDAP_GRP[$GRPRND]}"

  echo "Creating People/${UIDL}.ldif ..."
  cat << EOT > People/${UIDL}.ldif
dn: uid=$UIDL,ou=People,dc=unix,dc=mycompany,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
ou: $GRPL
sn: ${SN^}
cn: ${GN^} ${SN^}
uid: $UIDL
uidNumber: $UIDN
gidNumber: ${LDAP_GRPID[$GRPL]}
homeDirectory: /home/$UIDL
loginShell: /bin/bash
gecos: ${GN^} ${SN^}, $UIDL@mycompany.com
mail: $UIDL@mycompany.com
userPassword: changeme987

EOT
done
EOF
dirsrv@ldap01 $ chmod +x genUsers.ldif
dirsrv@ldap01 $ ./genUsers.sh
...
Creating People/smiddy6.ldif ...
...

dirsrv@ldap01 $ ldapadd -D "cn=Directory Manager" -y /etc/dirsrv/.dmpw -x -ZZ
dn: cn=prod,ou=Groups,dc=unix,dc=mycompany,dc=com
objectClass: top
objectClass: posixGroup
cn: prod
gidNumber: 1001

dn: cn=uat,ou=Groups,dc=unix,dc=mycompany,dc=com
objectClass: top
objectClass: posixGroup
cn: uat
gidNumber: 1002

dn: cn=dev,ou=Groups,dc=unix,dc=mycompany,dc=com
objectClass: top
objectClass: posixGroup
cn: dev
gidNumber: 1003
^D
...
dirsrv@ldap01 $ cat People/*.ldif | ldapadd -D "cn=Directory Manager" -y /etc/dirsrv/.dmpw -x -ZZ
...

We should have 200 LDAP users and 3 LDAP groups now. Next we want some manager groups and ACIs so that our users are able to edit certain attributes. I'll show this only for the PROD group (which is for POSIX group prod with gid 1001).

dirsrv@ldap01 $ ldapsearch -D "cn=Directory Manager" -xy /etc/dirsrv/.dmpw -ZZ -LLL \
  -b "ou=People,dc=unix,dc=mycompany,dc=com" "(&(objectclass=posixAccount)(gidNumber=1001))" uid gidNumber
...
dn: uid=aeger10,ou=People,dc=unix,dc=mycompany,dc=com
uid: aeger10
gidNumber: 1001
...
dn: uid=smiddy6,ou=People,dc=unix,dc=mycompany,dc=com
uid: smiddy6
gidNumber: 1001
...

dirsrv@ldap01 $ ldapadd -D "cn=Directory Manager" -y /etc/dirsrv/.dmpw -x -ZZ
dn: cn=PROD Managers,ou=Groups,dc=unix,dc=mycompany,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: PROD Managers
description: People who can manage PROD entries
ou: Groups
uniqueMember: uid=smiddy6,ou=People,dc=unix,dc=mycompany,dc=com
^D
...

The LDAP user smiddy6 is now a group manager for the PROD group. Let's create an ACI so that the PROD Managers group can actually do something.

dirsrv@ldap01 $ ldapadd -D "cn=Directory Manager" -y /etc/dirsrv/.dmpw -x -ZZ
dn: ou=People,dc=unix,dc=mycompany,dc=com
changetype: modify
add: aci
aci: (targetattr = "gecos || loginShell || userPassword")(targetfilter ="(ou
 =PROD)")(version 3.0;acl "PROD Group Permissions";allow (write)(groupdn = "
 ldap:///cn=PROD Managers,ou=Groups,dc=unix,dc=mycompany,dc=com");)
^D
...

Let's check if those ACIs work.

dirsrv@ldap01 $ ldappasswd -D "uid=smiddy6,ou=People,dc=unix,dc=mycompany,dc=com" -xW -ZZ -AS
Old password: changeme987
Re-enter old password: changeme987
New password: xxx
Re-enter new password: xxx
Enter LDAP Password: changeme987

dirsrv@ldap01 $ ldapsearch -D "uid=smiddy6,ou=People,dc=unix,dc=mycompany,dc=com" -xLLL -W \
  -b "uid=smiddy6,ou=People,dc=unix,dc=mycompany,dc=com" -ZZ \
  -E '!1.3.6.1.4.1.42.2.27.9.5.2=:dn:uid=smiddy6,ou=People,dc=unix,dc=mycompany,dc=com' "(objectClass=*)"
...
entryLevelRights: v
attributeLevelRights: objectClass:rsc, ou:rsc, sn:rsc, cn:rsc, uid:rsc, uidNum
 ber:rsc, gidNumber:rsc, homeDirectory:rscwo, loginShell:rscwo, gecos:rscwo, s
 hadowLastChange:rsc, userPassword:wo

With those access controls in place users can change their own gecos, loginShell (r(ead), s(search), c(ompare), w(rite) and (o)bliterate) and userPassword (w(rite), (o)bliterate) attributes. PROD group managers (like smiddy6) should be able to change the same attributes for other PROD group members (like aeger10) as well.

dirsrv@ldap01 $ ldapsearch -D "uid=smiddy6,ou=People,dc=unix,dc=mycompany,dc=com" -xLLL -W -b "uid=aeger10,ou=People,dc=unix,dc=mycompany,dc=com" -ZZ -E '!1.3.6.1.4.1.42.2.27.9.5.2=:dn:uid=smiddy6,ou=People,dc=unix,dc=mycompany,dc=com' "(objectClass=*)"
...
entryLevelRights: v
attributeLevelRights: objectClass:rsc, ou:rsc, sn:rsc, cn:rsc, uid:rsc, uidNum
 ber:rsc, gidNumber:rsc, homeDirectory:rscwo, loginShell:rscwo, gecos:rscwo, s
 hadowLastChange:rsc, userPassword:wo
...

Looks good. LDAP user aeger10 on the other hand is not a group manager, so (s)he should have no rights to update any attributes for smiddy6.

dirsrv@ldap01 $ ldappasswd -D "uid=aeger10,ou=People,dc=unix,dc=mycompany,dc=com" -xW -ZZ -AS
...
dirsrv@ldap01 $ ldapsearch -D "uid=aeger10,ou=People,dc=unix,dc=mycompany,dc=com" -xLLL -W \
  -b "uid=smiddy6,ou=People,dc=unix,dc=mycompany,dc=com" -ZZ \
  -E '!1.3.6.1.4.1.42.2.27.9.5.2=:dn:uid=aeger10,ou=People,dc=unix,dc=mycompany,dc=com' "(objectClass=*)"
...
entryLevelRights: v
attributeLevelRights: objectClass:rsc, ou:rsc, sn:rsc, cn:rsc, uid:rsc, uidNum
 ber:rsc, gidNumber:rsc, homeDirectory:rsc, loginShell:rsc, gecos:rsc, shadowL
 astChange:rsc, userPassword:none

I call that a win.

Keep in mind that you don't want any user to be able to change the uidNumber attribute! Otherwise you'll end up with a bunch of LDAP users with uid 0 on your servers.

Read the next part at 389 Directory Server 1.3.x Replication.

Links

389 Directory Server 1.3.x LDAP client authentication

389 Directory Server 1.3.x LDAP client authentication Last time we did a multi-master replication setup, see 389 Directory Server 1.3.x Repl...