We’re going to use Puppet to configure a pair of OpenLDAP servers with a master-slave replication.
This article is part of the Homelab Project with KVM, Katello and Puppet series.
Homelab
We have two CentOS 7 servers installed which we want to configure as follows:
ldap1.hl.local (10.11.1.11) – will be configured as an LDAP master
ldap2.hl.local (10.11.1.12) – will be configured as an LDAP slave
Both servers have SELinux set to enforcing mode.
See the image below to identify the homelab part this article applies to.
Configuration with Puppet
Puppet master runs on the Katello server. We use camptocamp-openldap Puppet module to configure OpenLDAP. Please see the module documentation for features supported and configuration options available.
See here (CentOS 7) and here (Debian) for blog posts on how to configure an OpenLDAP server manually.
Note that instructions below apply to both LDAP servers.
Firewall configuration to allow LDAPS access from homelab LAN:
firewall { ‘007 allow LDAPS’:
dport => [636],
source => ‘10.11.1.0/24’,
proto => tcp,
action => accept,
}
Ensure that the private key (which we created previously) in the PKCS#8 format is available.
file {‘/etc/pki/tls/private/hl.pem’:
ensure => file,
source => ‘puppet:///homelab_files/hl.pem’,
owner => ‘0’,
group => ‘ldap’,
mode => ‘0640’,
}
Configure the LDAP server (note how we bind to the SSL port):
class { ‘openldap::server’:
ldap_ifs => [‘127.0.0.1:389/’],
ldaps_ifs => [‘0.0.0.0:636/’],
ssl_cert => ‘/etc/pki/tls/certs/hl.crt’,
ssl_key => ‘/etc/pki/tls/private/hl.pem’,
}
Configure the database:
openldap::server::database { ‘dc=top’:
ensure => present,
directory => ‘/var/lib/ldap’,
suffix => ‘dc=top’,
rootdn => ‘cn=admin,dc=top’,
rootpw => ‘cGfSAyREZC5XnJa77iP+EdR8BrvZfUuo’,
}
Configure schemas:
openldap::server::schema { ‘cosine’:
ensure => present,
path => ‘/etc/openldap/schema/cosine.schema’,
}
openldap::server::schema { ‘inetorgperson’:
ensure => present,
path => ‘/etc/openldap/schema/inetorgperson.schema’,
require => Openldap::Server::Schema[“cosine”],
}
openldap::server::schema { ‘nis’:
ensure => present,
path => ‘/etc/openldap/schema/nis.ldif’,
require => Openldap::Server::Schema[“inetorgperson”],
}
Configure ACLs:
$homelab_acl = {
‘0 to attrs=userPassword,shadowLastChange’ => [
‘by dn=”cn=admin,dc=top” write’,
‘by dn=”cn=reader,dc=top” read’,
‘by self write’,
‘by anonymous auth’,
‘by * none’,
],
‘1 to dn.base=””‘ => [
‘by * read’,
],
‘2 to *’ => [
‘by dn=”cn=admin,dc=top” write’,
‘by dn=”cn=reader,dc=top” read’,
‘by self write’,
‘by users read’,
‘by anonymous auth’,
‘by * none’,
],
}
openldap::server::access_wrapper { ‘dc=top’ :
acl => $homelab_acl,
}
Base configuration:
file { ‘/root/.ldap_config.ldif’:
ensure => file,
source => ‘puppet:///homelab_files/ldap_config.ldif’,
owner => ‘0’,
group => ‘0’,
mode => ‘0600’,
notify => Exec[‘configure_ldap’],
}
exec { ‘configure_ldap’:
command => ‘ldapadd -c -x -D cn=admin,dc=top -w PleaseChangeMe -f /root/.ldap_config.ldif && touch /root/.ldap_config.done’,
path => ‘/usr/bin:/usr/sbin:/bin:/sbin’,
provider => shell,
onlyif => [‘test -f /root/.ldap_config.ldif’],
unless => [‘test -f /root/.ldap_config.done’],
}
Content of the file ldap_config.ldif can be seen below.
We create a read-only account cn=reader,dc=top for LDAP replication, we also create an LDAP user uid=tomas,ou=Users,dc=hl.local,dc=top to log into homelab servers.
dn: cn=reader,dc=top
objectClass: simpleSecurityObject
objectclass: organizationalRole
description: LDAP Read-only Access
userPassword: NrBn6Kd4rW8jmf+KWmfbTMFOkcC43ctF
dn: dc=hl.local,dc=top
o: hl.local
dc: hl.local
objectClass: dcObject
objectClass: organization
dn: ou=Users,dc=hl.local,dc=top
objectClass: organizationalUnit
ou: Users
dn: uid=tomas,ou=Users,dc=hl.local,dc=top
uid: tomas
uidNumber: 5001
gidNumber: 5001
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
userPassword: aBLnLxAUZAqwwII6fNUzizyOY/YAowtt
cn: Tomas
gn: Tomas
sn: Admin
mail: [email protected]
shadowLastChange: 16890
shadowMin: 0
shadowMax: 99999
shadowWarning: 14
shadowInactive: 3
loginShell: /bin/bash
homeDirectory: /home/guests/tomas
dn: ou=Groups,dc=hl.local,dc=top
objectClass: organizationalUnit
ou: Groups
dn: cn=tomas,ou=Groups,dc=hl.local,dc=top
gidNumber: 5001
objectClass: top
objectClass: posixGroup
cn: tomas
LDAP Master Server
Configure sync provider on the master node:
file { ‘/root/.ldap_syncprov.ldif’:
ensure => file,
source => ‘puppet:///homelab_files/ldap_syncprov.ldif’,
owner => ‘0’,
group => ‘0’,
mode => ‘0600’,
notify => Exec[‘configure_syncprov’],
}
exec { ‘configure_syncprov’:
command => ‘ldapadd -c -Y EXTERNAL -H ldapi:/// -f /root/.ldap_syncprov.ldif && touch /root/.ldap_syncprov.done && systemctl restart slapd’,
path => ‘/usr/bin:/usr/sbin:/bin:/sbin’,
provider => shell,
onlyif => [
‘test -f /root/.ldap_syncprov.ldif’,
‘test -f /root/.ldap_config.done’
],
unless => [‘test -f /root/.ldap_syncprov.done’],
}
Content of the file ldap_syncprov.ldif for the master server can be seen below.
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModulePath: /usr/lib64/openldap
olcModuleLoad: syncprov.la
dn: olcOverlay=syncprov,olcDatabase=hdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpSessionLog: 100
LDAP Slave Server
Configure replication on the slave node:
file { ‘/root/.ldap_replication.ldif’:
ensure => file,
source => ‘puppet:///homelab_files/ldap_replication.ldif’,
owner => ‘0’,
group => ‘0’,
mode => ‘0600’,
notify => Exec[‘configure_replication’],
}
exec { ‘configure_replication’:
command => ‘ldapadd -c -Y EXTERNAL -H ldapi:/// -f /root/.ldap_replication.ldif && touch /root/.ldap_replication.done && systemctl restart slapd’,
path => ‘/usr/bin:/usr/sbin:/bin:/sbin’,
provider => shell,
onlyif => [‘test -f /root/.ldap_config.done’],
unless => [‘test -f /root/.ldap_replication.done’],
}
Content of the file ldap_replication.ldif for the slave server is below. Note how we bind to the SSL port.
dn: olcDatabase=hdb,cn=config
changetype: modify
add: olcSyncRepl
olcSyncRepl: rid=001
provider=ldaps://ldap1.hl.local:636/
searchbase=”dc=hl.local,dc=top”
type=refreshAndPersist
retry=”60 10 300 +”
schemachecking=on
bindmethod=simple
binddn=”cn=reader,dc=top”
credentials=PleaseChangeMe
tls_reqcert=never
tls_cert=/etc/pki/tls/certs/hl.crt
tls_cacert=/etc/pki/tls/certs/hl.crt
tls_key=/etc/pki/tls/private/hl.pem
The Result
We should end up with the following LDAP structure:
Anything that gets created on the LDAP master should be automatically synced to the slave.
Debugging LDAP Issues
If you hit problems, try running the following to start the LDAP server in debug mode with logging to the console:
# slapd -h ldapi:/// -u ldap -d 255
The logs can be a difficult to parse, but with Google search and a bit of luck you should to be able to work out what is going on.
Configure All Homelab Servers to use LDAP Authentication
We use Puppet module sgnl05-sssd to configure SSSD.
Add the following to the main homelab environment manifest file /etc/puppetlabs/code/environments/homelab/manifests/site.pp so that it gets applied to all servers.
Note how SSSD is configured to use both LDAP servers for redundancy.
class {‘::sssd’:
ensure => ‘present’,
config => {
‘sssd’ => {
‘domains’ => ‘default’,
‘config_file_version’ => 2,
‘services’ => [‘nss’, ‘pam’],
},
‘domain/default’ => {
‘id_provider’ => ‘ldap’,
‘auth_provider’ => ‘ldap’,
‘cache_credentials’ => true,
‘default_shell’ => ‘/bin/bash’,
‘mkhomedir’ => true,
‘ldap_search_base’ => ‘dc=hl.local,dc=top’,
‘ldap_uri’ => ‘ldaps://ldap1.hl.local,ldaps://ldap2.hl.local’,
‘ldap_id_use_start_tls’ => false,
‘ldap_tls_reqcert’ => ‘never’,
‘ldap_default_bind_dn’ => ‘cn=reader,dc=top’,
‘ldap_default_authtok’ => ‘PleaseChangeMe’,
}
}
}
After Puppet applies the configuration above, we should be able to log into all homelab servers with the LDAP user.