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

Saturday, December 29, 2018

389 Directory Server 1.3.x Password Policy

389 Directory Server 1.3.x Password Policy

Last time we installed 389-ds and did some basic configuration, see 389 Directory Server 1.3.x on Red Hat Enterprise Linux 7.6.

Let's implement a password policy now. We have the following requirements:

  • Password Minimum Length: 8
  • Maximum Password Failures: 100
  • Reset Password Failure Count After: 30 days
  • Password Must Change: yes (we want our users to change their password when they first bind to the directory server or when the password has been reset by the Manager DN)
  • Password Storage Scheme: we want the strongest, which is currently PBKDF2_SHA256 with 30,000 iterations

Let's enforce those rules:

dirsrv@ldap01 $ ldapmodify -D "cn=Directory Manager" -y /etc/dirsrv/.dmpw -x -ZZ
dn: cn=config
changetype: modify
replace: passwordCheckSyntax
passwordCheckSyntax: on
-
replace: passwordLockout
passwordLockout: on
-
replace: passwordMaxFailure
passwordMaxFailure: 100
-
replace: passwordMinCategories
passwordMinCategories: 1
-
replace: passwordMinLength
passwordMinLength: 8
-
replace: passwordResetFailureCount
passwordResetFailureCount: 2592000
-
replace: passwordMustChange
passwordMustChange: on
-
replace: passwordStorageScheme
passwordStorageScheme: PBKDF2_SHA256
-
replace: passwordTrackUpdateTime
passwordTrackUpdateTime: on
-
replace: passwordUnlock
passwordUnlock: off
-
replace: nsslapd-rootpwstoragescheme
nsslapd-rootpwstoragescheme: PBKDF2_SHA256
^D
modifying entry "cn=config"

This should make us comply (well mostly) with NIST's Special Publication 800-63 Digital Identity Guidelines.

If you have more strict password rules (like passwords can only be changed after xx hours), don't forget to add your directory admins to the passwordAdminDN role to bypass those rules:

dirsrv@ldap01 $ ldapmodify -D "cn=Directory Manager" -y /etc/dirsrv/.dmpw -x -ZZ
dn: cn=config
changetype: modify
replace: passwordAdminDN
passwordAdminDN: cn=Directory Administrators,ou=Groups,dc=unix,dc=mycompany,dc=com
^D

Read the next part here 389 Directory Server 1.3.x Users, Groups and ACIs.

Links

Friday, December 28, 2018

389 Directory Server 1.3.x on Red Hat Enterprise Linux 7.6

389 Directory Server 1.3.x on Red Hat Enterprise Linux 7.6

Well you know the saying, all good things must come to an end. So we're going to replace Oracle's DSEE with 389 Directory Server today.

To get started we need to install 389-ds and do a silent setup:

[root@ldap01~]# yum -y install 389-ds-base
Loaded plugins: ulninfo
Resolving Dependencies
--> Running transaction check
---> Package 389-ds-base.x86_64 0:1.3.8.4-18.el7_6 will be installed
...

[root@ldap01~]# cat << EOF > /etc/sudoers.d/dirsrv
dirsrv $(hostname -s) = (root) NOPASSWD: /bin/systemctl start dirsrv@$(hostname -s), \
  /bin/systemctl stop dirsrv@$(hostname -s), \
  /bin/systemctl restart dirsrv@$(hostname -s)
EOF
[root@ldap01~]# chmod 440 /etc/sudoers.d/dirsrv
[root@ldap01~]# visudo -c

[root@ldap01~]# pwmake 192 | tr -d '\n' > /etc/dirsrv/.dmpw
[root@ldap01~]# chown dirsrv:dirsrv /etc/dirsrv/.dmpw
[root@ldap01~]# chmod 400 /etc/dirsrv/.dmpw

[root@ldap01~]# cat << EOF > /tmp/userRoot.ldif
dn: dc=unix,dc=mycompany,dc=com
objectClass: top
objectClass: domain
# https://tools.ietf.org/html/rfc4519
#  The 'dc' ('domainComponent' in RFC 1274) attribute type is a string
# holding one component, a label, of a DNS domain name.
# Examples: Valid values include "example" and "com" but not
# "example.com". The latter is invalid as it contains multiple
# domain components.
dc: unix
aci: (targetattr != "userPassword")(version 3.0;acl "Anonymous read-search acc
 ess"; allow (read, search, compare)(userdn = "ldap:///anyone");)
aci: (targetattr = "*")(version 3.0; acl "allow all Admin group"; allow(all) g
 roupdn = "ldap:///cn=Directory Administrators,ou=Groups,dc=mycompany,dc=com";
 )

dn: ou=Groups,dc=unix,dc=mycompany,dc=com
objectclass: top
objectclass: organizationalUnit
ou: Groups

dn: cn=Directory Administrators,ou=Groups,dc=unix,dc=mycompany,dc=com
objectClass: top
objectClass: groupOfUniqueNames
cn: Directory Administrators
ou: Groups
uniqueMember: cn=Directory Manager

dn: ou=People,dc=unix,dc=mycompany,dc=com
objectClass: top
objectClass: organizationalUnit
ou: People
aci: (targetattr = "gecos || loginShell || userPassword")(version 3.0;acl "All
 ow self entry modification";allow (write)(userdn = "ldap:///self");)

dn: ou=Special Users,dc=unix,dc=mycompany,dc=com
objectClass: top
objectClass: organizationalUnit
ou: Special Users
description: Special Administrative Accounts
EOF

[root@ldap01~]# cat << EOF > /tmp/setup.inf
[General]
FullMachineName=$(hostname -f)
StrictHostCheck=true
SuiteSpotGroup=dirsrv
SuiteSpotUserID=dirsrv

[slapd]
AddSampleEntries=No
InstallLdifFile=/tmp/userRoot.ldif
RootDN=cn=Directory Manager
RootDNPwd=$(pwdhash -s PBKDF2_SHA256 $(< /etc/dirsrv/.dmpw))
SchemaFile=/usr/share/dirsrv/data/60rfc4876.ldif
ServerIdentifier=$(hostname -s)
ServerPort=389
Suffix=dc=unix,dc=mycompany,dc=com
UseExistingMC=0
EOF

[root@ldap01~]# setup-ds.pl -s -f /tmp/setup.inf
...
Your new DS instance 'ldap01' was successfully created.
[root@ldap01~]# systemctl enable dirsrv@$(hostname -s)

We want a STARTTLS/LDAPS only server, so we need to create a CA, certificate and a key. See nginx on Solaris 11.3 SRU 19 with EC crypto and HTTP/2 support for how to do that with OpenSSL or How to create a SSL CA/certificate/key with pktool. I will just use the ECC files for now but I'm pretty sure I'll need all of them for older, non ECC clients.

Either way, you should have the following files now: CA.crt, server.crt, server.key and/or server-ecc.crt, server-ecc.key.

[root@ldap01~]# su -m dirsrv
dirsrv@ldap01 $ ldapadd -D "cn=Directory Manager" -xy /etc/dirsrv/.dmpw -h localhost -p 389
# enable the RSA cipher family,
# setting the NSS database security device,
# and the server certificate nickname
dn: cn=RSA,cn=encryption,cn=config
changetype: add
objectClass: top
objectClass: nsEncryptionModule
nsSSLPersonalitySSL: server-cert
nsSSLActivation: on
nsSSLToken: internal (software)
cn: RSA

# Enable TLS, tweak some defaults and sprinkle some security settings
dn: cn=config
changetype: modify
replace: nsslapd-security
nsslapd-security: on
-
# Preventing Directory Server to Start If the Certificate Has Been Expired
replace: nsslapd-validate-cert
nsslapd-validate-cert: on
-
# require simple binds to occur over a secure connection (SSL/TLS or Start TLS)
replace: nsslapd-require-secure-binds
nsslapd-require-secure-binds: on
-
# Disable anon binds except for the root dse
replace: nsslapd-allow-anonymous-access
nsslapd-allow-anonymous-access: rootdse
-
# A file descriptor is used whenever a client connects to the server
# and also for some server activities, such as index maintenance.
replace: nsslapd-maxdescriptors
nsslapd-maxdescriptors: 16384
-
replace: nsslapd-listen-backlog-size
nsslapd-listen-backlog-size: 256

# only accept TLS 1.2 connections
# leave the TLS1.0 default for older clients
# such as RHEL6, SLES11, Solaris 10!
dn: cn=encryption,cn=config
changetype: modify
replace: sslVersionMin
sslVersionMin: TLS1.2

dn: cn=config
changetype: modify
# Set a minimum SSF to disable insecure connections to a directory
# XXX changing the SSF has effects immediately!
# only change this at the very end or the ldapadd connection will drop!
replace: nsslapd-minssf
nsslapd-minssf: 128
^D

dirsrv@ldap01 $ sudo systemctl stop dirsrv@$(hostname -s)

dirsrv@ldap01 $ NSSPW="$(pwmake 192)"
dirsrv@ldap01 $ echo -n "$NSSPW" > /etc/dirsrv/.nsspw
dirsrv@ldap01 $ chmod 400 /etc/dirsrv/.nsspw
dirsrv@ldap01 $ certutil -d /etc/dirsrv/slapd-$(hostname -s)/ -N -f /etc/dirsrv/.nsspw
dirsrv@ldap01 $ chmod 600 /etc/dirsrv/slapd-$(hostname -s)/*.db
dirsrv@ldap01 $ echo -n "Internal (Software) Token:$NSSPW" > /etc/dirsrv/slapd-$(hostname -s)/pin.txt
dirsrv@ldap01 $ chmod 400 /etc/dirsrv/slapd-$(hostname -s)/pin.txt
dirsrv@ldap01 $ touch /etc/dirsrv/slapd-$(hostname -s)/pkcs11.txt
dirsrv@ldap01 $ chmod 600 /etc/dirsrv/slapd-$(hostname -s)/pkcs11.txt
dirsrv@ldap01 $ certutil -d /etc/dirsrv/slapd-$(hostname -s)/ -A -n "CA" -t "CT,," -f /etc/dirsrv/.nsspw -i .../CA.crt
dirsrv@ldap01 $ openssl pkcs12 -export -in .../server-ecc.crt -inkey .../server-ecc.key \
                  -out /tmp/servercert.p12 -name "server-cert" -passout file:/etc/dirsrv/.nsspw
dirsrv@ldap01 $ pk12util -i /tmp/servercert.p12 -d /etc/dirsrv/slapd-$(hostname -s)/ -k /etc/dirsrv/.nsspw -w /etc/dirsrv/.nsspw
pk12util: PKCS12 IMPORT SUCCESSFUL
dirsrv@ldap01 $ certutil -d /etc/dirsrv/slapd-$(hostname -s)/ -V -n "server-cert" -u V
certutil: certificate is valid

[root@ldap01~]# systemctl restart dirsrv@$(hostname -s)
[root@ldap01~]# cp .../CA.crt /etc/openldap/certs/
[root@ldap01~]# cat << EOF > /etc/openldap/ldap.conf
BASE dc=unix,dc=mycompany,dc=com
URI ldap://$(hostname -f)

TLS_CACERT /etc/openldap/certs/CA.crt
TLS_PROTOCOL_MIN 3.3
TLS_REQCERT demand
EOF

dirsrv@ldap01 $ openssl s_client -connect $(hostname -f):389 -starttls ldap -CAfile /etc/openldap/certs/CA.crt < /dev/null
...
    Verify return code: 0 (ok)
dirsrv@ldap01 $ openssl s_client -connect $(hostname -f):636 -CAfile /etc/openldap/certs/CA.crt < /dev/null
...
    Verify return code: 0 (ok)
dirsrv@ldap01 $ ldapsearch -LLL -D "cn=Directory Manager" -xy /etc/dirsrv/.dmpw \
                  -ZZ -s base -b 'cn=encryption,cn=config' sslVersionMin
dn: cn=encryption,cn=config
sslVersionMin: TLS1.2

And that was the first part. Short recap we have installed 389-ds on RHEL, we configured it with our chosen suffix, created a new SSL certificate/key and enabled TLS-only connections to the directory server. Also we did some LDAP client configuration in /etc/openldap/ldap.conf.

Read the next part here 389 Directory Server 1.3.x Password Policy.

Links

Friday, May 18, 2018

Puppetizing ntpd on Solaris 11.4

Puppetizing ntpd on Solaris 11.4

Last time we configured a Puppet master, see Puppet Master on Solaris 11.3 SRU 23.

Let's do some basic configuration with Puppet today. I'm doing this on Solaris 11.4 Beta with Puppet 5.3.3 so some paths on the master are slightly different. Keep this in mind when you're still on Solaris 11.3.

First things first, setup hiera.

root@master # cat /etc/puppetlabs/puppet/hiera.yaml
---
version: 5
defaults:
  datadir: /var/lib/hiera
  data_hash: yaml_data
hierarchy:
  - name: "Per-node data"
    path: "nodes/%{trusted.certname}.yaml"

  - name: "Per-net data"
    paths:
      - "net/%{::network_net0}.yaml"
      - "net/%{::network_ipmp0}.yaml"

  - name: "Other YAML hierarchy levels"
    path: "common.yaml"

root@master # cat /var/lib/hiera/common.yaml
---
classes:
  - ntp

# https://forge.puppetlabs.com/puppetlabs/ntp
ntp::enable: true
ntp::iburst_enable: true
# fix "ntpd[12345]: [ID 702911 daemon.warning] restrict default: KOD does nothing without LIMITED."
ntp::restrict:
  - default nomodify notrap nopeer noquery
  - -6 default nomodify notrap nopeer noquery
  - 127.0.0.1
  - -6 ::1
ntp::servers:
  - 10.1.2.3
  - 10.4.5.6

root@master # cat /var/lib/hiera/net/192.168.1.0.yaml
---
ntp::servers:
  - 192.168.1.1

I pulled a lot of hair out while figuring this out... when changing hiera.yaml you have to restart puppet!

Puppet master note: If you modify hiera.yaml between agent runs, you’ll have to restart your Puppet master for your changes to take effect.

And we're almost done. We just have to add one line to site.pp and restart puppet:master because we changed hiera.yaml.

root@master # cat /etc/puppetlabs/code/environments/production/manifests/site.pp
include(lookup('classes', Array[String], 'unique'))

root@master # svcadm restart puppet:master
root@master # tail /var/log/puppetlabs/puppet/puppet-master.log
2018-05-18 13:09:22 +0200 Puppet (notice): Starting Puppet master version 5.3.3

Let's hop on to the agent and see if it works.

# puppet agent --test --noop --server master.mycompany.com
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Loading facts
Info: Applying configuration version '1526649508'
Notice: /Stage[main]/Ntp::Config/File[/etc/inet/ntp.conf]/ensure: current_value 'absent', should be 'file' (noop)
Notice: Class[Ntp::Config]: Would have triggered 'refresh' from 1 event
Info: Class[Ntp::Config]: Scheduling refresh of Class[Ntp::Service]
Notice: Class[Ntp::Service]: Would have triggered 'refresh' from 1 event
Info: Class[Ntp::Service]: Scheduling refresh of Service[ntp]
Notice: /Stage[main]/Ntp::Service/Service[ntp]/ensure: current_value 'stopped', should be 'running' (noop)
Info: /Stage[main]/Ntp::Service/Service[ntp]: Unscheduling refresh on Service[ntp]
Notice: Class[Ntp::Service]: Would have triggered 'refresh' from 1 event
Notice: Class[Ntp]: Would have triggered 'refresh' from 2 events
Notice: Stage[main]: Would have triggered 'refresh' from 3 events
Notice: Applied catalog in 1.90 seconds

# svcadm enable puppet:agent
# svcs ntp
STATE          STIME    FMRI
online         15:19:34 svc:/network/ntp:default
# ntpq -pn
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*192.168.1.1     LOCAL(0)         2 u   58   64  377    1.010    0.017   0.037

Neat, we have puppetized NTP with a few lines of code.

Links

Puppet Master on Solaris 11.3 SRU 23

Puppet Master on Solaris 11.3 SRU 23

I wasn't really happy with Puppet on Solaris 11.3. But SRU 23 had the following highlight.

Puppet has been updated to 3.8.6 (Bug 22264785). Along with the Puppet update, Facter has been updated to 2.4.6 (Bug 22308383).

Let's give Puppet another try. We'll start with configuring the Puppet master.

# pkg install --no-backup-be system/management/puppet
           Packages to install:        14
...
root@master # svccfg -s puppet:master setprop config/server=$(uname -n).mycompany.com
root@master # svccfg -s puppet:master setprop config/bindaddress=0.0.0.0
root@master # svccfg -s puppet:master refresh
root@master # svcadm enable puppet:master
root@master # tail /var/log/puppet/puppet-master.log
2017-10-19 09:05:16 +0200 Puppet (notice): Starting Puppet master version 3.8.6

We'll configure a client as well.

# pkg install --no-backup-be system/management/puppet
...
# svccfg -s puppet:agent setprop config/server=master.mycompany.com
# svccfg -s puppet:agent refresh

# puppet agent --test --server master.mycompany.com
...
Notice: Finished catalog run in 0.10 seconds

Let's sign the client certificate on the master.

root@master # puppet cert list
  "agent.mycompany.com" (SHA256) XX:XX:XX:XX:...

root@master # puppet cert sign agent.mycompany.com
Signing Certificate Request for:
  "agent.mycompany.com" (SHA256) XX:XX:XX:XX:...
Notice: Signed certificate request for agent.mycompany.com
Notice: Removing file Puppet::SSL::CertificateRequest agent.mycompany.com at '/etc/puppet/ssl/certificate_requests/agent.mycompany.com.pem'

The puppet agent should now be able to communicate with our puppet master.

# puppet agent --test
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Loading facts
Info: Caching catalog for agent.mycompany.com
Info: Applying configuration version '1526560515'
Notice: Applied catalog in 0.20 seconds

# svcadm enable puppet:agent

And now our Puppet master is pulling the strings.

Read the next part at Puppetizing ntpd on Solaris 11.4.

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