diff --git a/.gitignore b/.gitignore index d6de46a..0f0dfbc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ .swp pkg spec/fixtures +.kitchen/ +.kitchen.local.yml +.librarian +.tmp diff --git a/.kitchen.yml b/.kitchen.yml new file mode 100644 index 0000000..f677d66 --- /dev/null +++ b/.kitchen.yml @@ -0,0 +1,20 @@ +--- +driver: + name: vagrant + +provisioner: + name: puppet_apply + modules_path: .. + fileserver_config_path: puppet/fileserver.conf + files_path: puppet/files + puppet_debug: true + +platforms: + - name: debian-6.0.8 + - name: debian-7.2.0 + - name: ubuntu-12.04 + - name: centos-6.4 + +suites: + - name: master + - name: slave diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..28c0c5e --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.0.0-p451 diff --git a/Gemfile b/Gemfile index 462f94e..a0c1304 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,13 @@ -source :rubygems +source 'https://rubygems.org' + +ruby '2.0.0' gem 'rake', '~> 0.8.7' -gem 'puppet', '~> 2.7' +gem 'puppet', '~> 3.0' gem 'rspec-puppet', '~> 0.1.6' gem 'puppetlabs_spec_helper', '~> 0.4.1' + +gem 'test-kitchen', '~> 1.0' +gem 'kitchen-vagrant' +gem 'kitchen-puppet', github: 'neillturner/kitchen-puppet', branch: 'master' +gem 'librarian-puppet' diff --git a/Gemfile.lock b/Gemfile.lock index 98e5146..b435aea 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,19 +1,47 @@ +GIT + remote: git://github.com/neillturner/kitchen-puppet.git + revision: b8e7579962629b9b3c110d4569da0031a12df47d + branch: master + specs: + kitchen-puppet (0.0.9) + GEM - remote: http://rubygems.org/ + remote: https://rubygems.org/ specs: diff-lcs (1.2.4) facter (1.7.2) + hiera (1.3.2) + json_pure + highline (1.6.21) + json (1.8.1) + json_pure (1.8.0) + kitchen-vagrant (0.15.0) + test-kitchen (~> 1.0) + librarian (0.1.2) + highline + thor (~> 0.15) + librarian-puppet (1.0.2) + json + librarian (>= 0.1.2) metaclass (0.0.1) + mixlib-shellout (1.4.0) mocha (0.14.0) metaclass (~> 0.0.1) - puppet (2.7.22) - facter (~> 1.5) + net-scp (1.2.1) + net-ssh (>= 2.6.5) + net-ssh (2.9.0) + puppet (3.5.1) + facter (> 1.6, < 3) + hiera (~> 1.0) + json_pure + rgen (~> 0.6.5) puppetlabs_spec_helper (0.4.1) mocha (>= 0.10.5) rake rspec (>= 2.9.0) rspec-puppet (>= 0.1.1) rake (0.8.7) + rgen (0.6.6) rspec (2.13.0) rspec-core (~> 2.13.0) rspec-expectations (~> 2.13.0) @@ -24,12 +52,24 @@ GEM rspec-mocks (2.13.1) rspec-puppet (0.1.6) rspec + safe_yaml (1.0.3) + test-kitchen (1.2.1) + mixlib-shellout (~> 1.2) + net-scp (~> 1.1) + net-ssh (~> 2.7) + safe_yaml (~> 1.0) + thor (~> 0.18) + thor (0.19.1) PLATFORMS ruby DEPENDENCIES - puppet (~> 2.7) + kitchen-puppet! + kitchen-vagrant + librarian-puppet + puppet (~> 3.0) puppetlabs_spec_helper (~> 0.4.1) rake (~> 0.8.7) rspec-puppet (~> 0.1.6) + test-kitchen (~> 1.0) diff --git a/Modulefile b/Modulefile index fa0fa8a..8338cd9 100644 --- a/Modulefile +++ b/Modulefile @@ -6,6 +6,3 @@ license 'GPL v2' summary 'OpenLDAP module for Puppet.' description 'Manage OpenLDAP clients and server via Puppet' project_page 'https://github.com/torian/puppet-ldap' - -# Dependency -dependency 'puppetlabs/stdlib', '>= 4.1.0' diff --git a/Puppetfile b/Puppetfile new file mode 100644 index 0000000..68ef884 --- /dev/null +++ b/Puppetfile @@ -0,0 +1,3 @@ +forge "https://forge.puppetlabs.com" + +modulefile diff --git a/Puppetfile.lock b/Puppetfile.lock new file mode 100644 index 0000000..51949ef --- /dev/null +++ b/Puppetfile.lock @@ -0,0 +1,2 @@ +DEPENDENCIES + diff --git a/README.md b/README.md index b2de422..d6ad837 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,99 @@ Configure an OpenLdap slave: ], } +### Directory updates + +This module includes a puppet type and provider that aims to simply managing ldap entries via ldapmodify and ldapadd commands. + +In essence the mechanism it uses is described as follows: + +* Translate the puppet "ldapdn" resource into an in-memory ldif +* ldapsearch the existing dn to verify the current contents (if any) +* compare the results of the search with what should be the case +* work out which add/modify/delete commands are required to get to the desired state +* write out an appropriate ldif file +* execute it via an ldapmodify statement. + +Examples of usage are as follows: + +First you might like to set a root password: + +```puppet +ldapdn { "add manager password": + dn => "olcDatabase={2}hdb,cn=config", + attributes => ["olcRootPW: password"], + unique_attributes => ["olcRootPW"], + ensure => present, +} +``` + +`attributes` sets the attributes that you wish to set (be sure to separate key and value with ). +`unique_attributes` can be used to specify the behaviour of `ldapmodify` when there is an existing attribute with this name. If the attribute key is specified here, then `ldapmodify` will issue a replace, replacing the existing value (if any), whereas if the attribute key is not specified here, then `ldapmodify` will simply ensure the attribute exists with the value required, alongside other values if also specified (e.g. for `objectClass`). + +```puppet +$organizational_units = ["Groups", "People", "Programs"] +ldap::add_organizational_unit { $organizational_units: } + +define ldap::add_organizational_unit () { + ldapdn { "ou ${name}": + dn => "ou=${name},dc=example,dc=com", + attributes => [ "ou: ${name}", + "objectClass: organizationalUnit" ], + unique_attributes => ["ou"], + ensure => present, + } +} +``` + +In the above example, multiple groups are created. Notice in each case, that `objectClass` does not form part of the `unique_attributes`, so that (in future) more `objectClasses` may be added to each ou, without them being replaced. + +By default, all ldap commands are issued with the `-QY EXTERNAL` SASL auth mechanism. + +Here is how you can create a database in the first place: + +```puppet +ldapdn { "add database": + dn => "dc=example,dc=com", + attributes => ["dc: example", + "objectClass: top", + "objectClass: dcObject", + "objectClass: organization", + "o: example.com"], + unique_attributes => ["dc", "o"], + ensure => present +} +``` + +Additionally, you may need to specify alternative authentication options when managing resources: + +```puppet +ldapdn { "add database": + dn => "dc=example,dc=com", + attributes => ["dc: example", + "objectClass: top", + "objectClass: dcObject", + "objectClass: organization", + "o: example.com"], + unique_attributes => ["dc", "o"], + ensure => present, + auth_opts => ["-xD", "cn=admin,dc=example,dc=com", "-w", "somePassword"], +} +``` + +Sometimes you will want to ensure an attribute exists, but wont care about its subsequent value. An example of this is a password. + +```puppet +ldapdn { "add password": + dn => "cn=Geoff,ou=Staff,dc=example,dc=com", + attributes => ["olcUserPassword: {SSHA}somehash..."], + unique_attributes => ["olcUserPassword"], + indifferent_attributes => ["olcUserPassword"], + ensure => present +} +``` + +By specifying `indifferent_attributes, ensure => present` will ensure that if the key doesn't exist, it will create it with the desired password hash, but if the key does exist, it won't bother replacing it again. In this way you can keep passwords managed by something like phpldapadmin if you so wish. + Notes ----- @@ -136,6 +229,12 @@ Requirements * If enable_motd is enabled (enable_motd => true) you'll need [puppet-motd](https://github.com/torian/puppet-motd.git) +Testing +------- + +Unit tests: `rake spec` +Integration tests: `kitchen test` + TODO ---- diff --git a/files/convertschema.sh b/files/convertschema.sh new file mode 100644 index 0000000..fe2dc7e --- /dev/null +++ b/files/convertschema.sh @@ -0,0 +1,87 @@ +#!/bin/sh + +# Script to convert schema files to LDIF files +# +# OPTIONS: +# -s The input schema file to convert +# -l The resulting LDIF file +# -d A comma separated list of schema dependencies +# -sd The schema directory +# + +SCHEMAFILE="" +LDIFFILE="" +DEPS="" +SCHEMADIR="" + +while getopts "s:l:d:sd:" options; do + case $options in + s ) SCHEMAFILE=$OPTARG;; + l ) LDIFFILE=$OPTARG;; + d ) DEPS=$OPTARG;; + sd ) SCHEMADIR=$OPTARG;; + * ) echo "Specify input schema file with -s and output LDIF file with -l" + exit 1;; + esac +done + +# do we have -s? +if [ x${SCHEMAFILE} = "x" ]; then + echo "Please specify a input schema file with -s" + exit 1 +fi + +# do we have -l? +if [ x${LDIFFILE} = "x" ]; then + echo "Please specify an output LDIF file with -l" + exit 1 +fi + +# can we write to -l? +if [ -f ${LDIFFILE} ]; then + if [ ! -w ${LDIFFILE} ]; then + echo "Cannot write to ${LDIFFILE}" + exit 1 + fi +else + if [ ! -w `dirname ${LDIFFILE}` ]; then + echo "Cannot write to `dirname ${LDIFFILE}` to create ${LDIFFILE}" + exit 1 + fi +fi + +# do we have -sd? +if [ x${SCHEMADIR} = "x" ]; then + SCHEMADIR=/etc/ldap/schema +fi + +# Get the base name of the schema file +BASENAME=`basename ${SCHEMAFILE} | cut -d'.' -f1` + +# Create a temporary config file and directory to set the schemas to process +TEMPDIR=$(mktemp -d) || exit 1 +TEMPFILE=$(mktemp -p ${TEMPDIR}) || exit 1 + +# Add all of the dependencies +for dep in `echo ${DEPS} | sed s/,/\\\n/g`; do + if [ -f "${SCHEMADIR}/${dep}.schema" ]; then + echo "include ${SCHEMADIR}/${dep}.schema" >> ${TEMPFILE} + fi +done +echo "include ${SCHEMAFILE}" >> ${TEMPFILE} + +# Determine the index of the schema +SCHEMAINDEX=$(slapcat -f ${TEMPFILE} -F ${TEMPDIR} -n 0 | grep "${BASENAME},cn=schema") +SCHEMADN=$(echo "${SCHEMAINDEX}" | sed 's/dn: //g') + +# Convert the schema to LDIF format +slapcat -f ${TEMPFILE} -F ${TEMPDIR} -n 0 -H ldap:///${SCHEMADN} -l ${TEMPDIR}/cn=${BASENAME}.ldif || exit 1 + +# Remove index information +sed -e "s/{[0-9]*}${BASENAME}/${BASENAME}/g" \ + -e "/^structuralObjectClass:/d; /^entryUUID:/d; /^creatorsName:/d; /^createTimestamp:/d" \ + -e "/^entryCSN:/d; /^modifiersName:/d; /^creatorsName:/d; /^modifyTimestamp:/d" \ + ${TEMPDIR}/cn=${BASENAME}.ldif > ${LDIFFILE} + +# Cleanup resources +rm -rf -- "${TEMPDIR}" diff --git a/lib/puppet/provider/ldapdn/ldapdn.rb b/lib/puppet/provider/ldapdn/ldapdn.rb new file mode 100644 index 0000000..465f4c3 --- /dev/null +++ b/lib/puppet/provider/ldapdn/ldapdn.rb @@ -0,0 +1,362 @@ +require 'puppet/provider' +require 'tempfile' + +module PuppetLDAP + # This is shamelessly stolen from ActiveSuport 3.2 until Puppet moves to Ruby 1.9. It is required to prevent declared attributes being reordered (eg objectClass being moved from first position). + + class OrderedHash < ::Hash + # Returns true to make sure that this hash is extractable via Array#extract_options! + def extractable_options? + true + end + + # Hash is ordered in Ruby 1.9! + if RUBY_VERSION < '1.9' + + # In MRI the Hash class is core and written in C. In particular, methods are + # programmed with explicit C function calls and polymorphism is not honored. + # + # For example, []= is crucial in this implementation to maintain the @keys + # array but hash.c invokes rb_hash_aset() originally. This prevents method + # reuse through inheritance and forces us to reimplement stuff. + # + # For instance, we cannot use the inherited #merge! because albeit the algorithm + # itself would work, our []= is not being called at all by the C code. + + def initialize(*args, &block) + super + @keys = [] + end + + def self.[](*args) + ordered_hash = new + + if (args.length == 1 && args.first.is_a?(Array)) + args.first.each do |key_value_pair| + next unless (key_value_pair.is_a?(Array)) + ordered_hash[key_value_pair[0]] = key_value_pair[1] + end + + return ordered_hash + end + + unless (args.size % 2 == 0) + raise ArgumentError.new("odd number of arguments for Hash") + end + + args.each_with_index do |val, ind| + next if (ind % 2 != 0) + ordered_hash[val] = args[ind + 1] + end + + ordered_hash + end + + def initialize_copy(other) + super + # make a deep copy of keys + @keys = other.keys + end + + def []=(key, value) + @keys << key unless has_key?(key) + super + end + + def delete(key) + if has_key? key + index = @keys.index(key) + @keys.delete_at index + end + super + end + + def delete_if + super + sync_keys! + self + end + + def reject! + super + sync_keys! + self + end + + def reject(&block) + dup.reject!(&block) + end + + def keys + @keys.dup + end + + def values + @keys.collect { |key| self[key] } + end + + def to_hash + self + end + + def to_a + @keys.map { |key| [ key, self[key] ] } + end + + def each_key + return to_enum(:each_key) unless block_given? + @keys.each { |key| yield key } + self + end + + def each_value + return to_enum(:each_value) unless block_given? + @keys.each { |key| yield self[key]} + self + end + + def each + return to_enum(:each) unless block_given? + @keys.each {|key| yield [key, self[key]]} + self + end + + def each_pair + return to_enum(:each_pair) unless block_given? + @keys.each {|key| yield key, self[key]} + self + end + + alias_method :select, :find_all + + def clear + super + @keys.clear + self + end + + def shift + k = @keys.first + v = delete(k) + [k, v] + end + + def merge!(other_hash) + if block_given? + other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v } + else + other_hash.each { |k, v| self[k] = v } + end + self + end + + alias_method :update, :merge! + + def merge(other_hash, &block) + dup.merge!(other_hash, &block) + end + + # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not. + def replace(other) + super + @keys = other.keys + self + end + + def invert + OrderedHash[self.to_a.map!{|key_value_pair| key_value_pair.reverse}] + end + + def inspect + "#" + end + + private + def sync_keys! + @keys.delete_if {|k| !has_key?(k)} + end + end + end +end + +Puppet::Type.type(:ldapdn).provide :ldapdn do + desc "" + + commands :ldapmodifycmd => "/usr/bin/ldapmodify" + commands :ldapaddcmd => "/usr/bin/ldapadd" + commands :ldapsearchcmd => "/usr/bin/ldapsearch" + + def create + ldap_apply_work + end + + def destroy + ldap_apply_work + end + + def exists? + @work_to_do = ldap_work_to_do(parse_attributes) + + # This is a bit of a butchery of an exists? method which is designed to return yes or no, + # Whereas we are editing a multi-faceted record, and it might be in a semi-desired state. + # However, as I want to still use the ensure param, I will have to live within its rules + case resource[:ensure] + when :present + @work_to_do.empty? + when :absent + !@work_to_do.empty? + end + end + + def parse_attributes + ldap_attributes = PuppetLDAP::OrderedHash.new + Array(resource[:attributes]).flatten.each do |asserted_attribute| + key,value = asserted_attribute.split(':', 2) + ldap_attributes[key] = [] if ldap_attributes[key].nil? + ldap_attributes[key] << value.strip! + end + ldap_attributes + end + + def ldap_apply_work + @work_to_do.each do |modify_type, modifications| + modify_record = [] + modify_record << "dn: #{resource[:dn]}" + + modify_record << "changetype: modify" if modify_type == :ldapmodify + + modifications.each do |attribute, instructions| + add_type = "add" + instructions.each do |instruction| + case instruction.first + when :add + if add_type == "add" and modify_type == :ldapmodify + modify_record << "add: #{attribute}" + else + add_type = "add" + end + modify_record << "#{attribute}: #{instruction.last}" + modify_record << "-" if modify_type == :ldapmodify + when :delete + modify_record << "delete: #{attribute}" + modify_record << "-" + when :replace + modify_record << "replace: #{attribute}" if add_type == "add" + add_type = "replace" + end + end + end + + ldif = Tempfile.open("ldap_apply_work") + ldif_file = ldif.path + ldif.write modify_record.join("\n") + ldif.close + + cmd = case modify_type + when :ldapmodify + :ldapmodifycmd + when :ldapadd + :ldapaddcmd + end + + begin + command = [command(cmd), "-H", "ldapi:///", "-d", "0", "-f", ldif_file] + command += resource[:auth_opts] || ["-QY", "EXTERNAL"] + Puppet.debug("\n\n" + File.open(ldif_file, 'r') { |file| file.read }) + output = execute(command) + Puppet.debug(output) + rescue Puppet::ExecutionFailure => ex + raise Puppet::Error, "Ldap Modify Error:\n\n#{modify_record.join("\n")}\n\nError details:\n#{ex.message}" + end + end + end + + def ldap_work_to_do(asserted_attributes) + command = [command(:ldapsearchcmd), "-H", "ldapi:///", "-b", resource[:dn], "-s", "base", "-LLL", "-d", "0"] + command += resource[:auth_opts] || ["-QY", "EXTERNAL"] + begin + ldapsearch_output = execute(command) + Puppet.debug("ldapdn >>\n#{asserted_attributes.inspect}") + Puppet.debug("ldapsearch >>\n#{ldapsearch_output}") + rescue Puppet::ExecutionFailure => ex + if ex.message.scan '/No such object (32)/' + Puppet.debug("Could not find object: #{resource[:dn]}") + return {} if resource[:ensure] == :absent + work_to_do = PuppetLDAP::OrderedHash.new + asserted_attributes.each do |asserted_key, asserted_values| + key_work_to_do = [] + asserted_values.each do |asserted_value| + key_work_to_do << [ :add, asserted_value ] + end + work_to_do[asserted_key] = key_work_to_do + end + Puppet.debug("WorkToDo: { :ldapadd => #{work_to_do}}") + return { :ldapadd => work_to_do } + else + raise ex + end + end + + unique_attributes = resource[:unique_attributes] + unique_attributes = [] if unique_attributes.nil? + + indifferent_attributes = resource[:indifferent_attributes] + indifferent_attributes = [] if indifferent_attributes.nil? + + work_to_do = PuppetLDAP::OrderedHash.new + found_attributes = {} + found_keys = [] + + asserted_attributes.each do |asserted_key, asserted_value| + work_to_do[asserted_key] = [] + found_attributes[asserted_key] = [] + end + + ldapsearch_output.split(/\r?\n(?!\s)/).each do |line| + line.gsub!(/[\r\n] /, '') + line.gsub!(/\r?\n?$/, '') + current_key,current_value = line.split(/:+ /, 2) + found_keys << current_key + if asserted_attributes.key?(current_key) + Puppet.debug("search() #{current_key}: #{current_value}") + same_as_an_asserted_value = false + asserted_attributes[current_key].each do |asserted_value| + Puppet.debug("check() #{current_key}: #{current_value} <===> #{current_key}: #{asserted_value}") + same_as_an_asserted_value = true if asserted_value == current_value + same_as_an_asserted_value = true if asserted_value.clone.gsub(/^\{.*?\}/, "") == current_value.clone.gsub(/^\{.*?\}/, "") + end + if same_as_an_asserted_value + Puppet.debug("asserted and found: #{current_key}: #{current_value}") + work_to_do[current_key] << [ :delete ] if resource[:ensure] == :absent + found_attributes[current_key] << current_value.clone.gsub(/^\{.*?\}/, "") + else + Puppet.debug("not asserted: #{current_key}: #{current_value}") + work_to_do[current_key] << [ :replace ] if resource[:ensure] == :present \ + and unique_attributes.include?(current_key) \ + and !indifferent_attributes.include?(current_key) + end + end + end + + asserted_attributes.each do |asserted_key, asserted_values| + asserted_values.each do |asserted_value| + Puppet.debug("assert() #{asserted_key}: #{asserted_value}") + + if resource[:ensure] == :present + work_to_do[asserted_key] << [ :add, asserted_value ] unless found_attributes[ asserted_key ].include?(asserted_value.clone.gsub(/^\{.*?\}/, "")) \ + or (found_keys.include?(asserted_key) and indifferent_attributes.include?(asserted_key)) + end + end + end + + work_to_do.delete_if { |key, operations| operations.empty? } + + if work_to_do.empty? + Puppet.debug("conclusion: nothing to do") + {} + else + Puppet.debug("conclusion: work to do: #{work_to_do.inspect}") + { :ldapmodify => work_to_do } + end + end +end diff --git a/lib/puppet/type/ldapdn.rb b/lib/puppet/type/ldapdn.rb new file mode 100644 index 0000000..7cf7f41 --- /dev/null +++ b/lib/puppet/type/ldapdn.rb @@ -0,0 +1,47 @@ +Puppet::Type.newtype :ldapdn do + ensurable do + newvalue :present do + provider.create + end + + newvalue :absent do + provider.destroy + end + + defaultto :present + end + + autorequire(:service) do + %w{ldap slapd} + end + + @doc = "This type provides the capability to manage LDAP DN entries." + + newparam :name do + desc "The canonical name of the rule." + + isnamevar + + newvalues(/^.*$/) + end + + newparam :attributes, :array_matching => :all do + desc "Specify the attribute you want to ldapmodify" + end + + newparam :unique_attributes, :array_matching => :all do + desc "Specify the attribute that are unique in the dn" + end + + newparam :indifferent_attributes, :array_matching => :all do + desc "Specify the attributes you dont care about their subsequent values (e.g. passwords)" + end + + newparam :dn do + desc "Specify the value of the attribute you want to ldapmodify" + end + + newparam :auth_opts do + desc "Specify the options passed to ldapadd/ldapmodify for authentication. Defaults to -QY EXTERNAL." + end +end diff --git a/manifests/client.pp b/manifests/client.pp index d878fdc..0ed932d 100644 --- a/manifests/client.pp +++ b/manifests/client.pp @@ -210,14 +210,6 @@ group => $ldap::params::group, } - file { $ldap::params::prefix: - ensure => $ensure ? { - present => directory, - default => absent, - }, - require => Package[$ldap::params::package], - } - if($sudoers_base) { if(! $sudoers_filter) { fail('If sudoers_base attribute is set, you must define sudoers_filter') diff --git a/manifests/init.pp b/manifests/init.pp index 8793ea8..2111879 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -39,9 +39,16 @@ # class ldap($ensure = present) { - include stdlib include ldap::params + file { $ldap::params::prefix: + ensure => $ensure ? { + present => directory, + default => absent, + }, + require => Package[$ldap::params::package], + } + package { $ldap::params::package : ensure => $ensure, } diff --git a/manifests/params.pp b/manifests/params.pp index eb0c266..cf54e07 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -23,6 +23,8 @@ $ssl_prefix = '/etc/ssl/certs' $server_run = '/var/run/slapd' + $main_db_name = '{1}hdb' + case $::operatingsystemmajrelease { 5 : { @@ -49,31 +51,22 @@ } - $modules_base = [ 'back_bdb' ] + $modules_base = [ 'back_hdb' ] $schema_prefix = "${prefix}/schema" $schema_base = [ 'core', 'cosine', 'nis', 'inetorgperson', ] $index_base = [ - 'index objectclass eq', - 'index entryCSN eq', - 'index entryUUID eq', - 'index uidNumber eq', - 'index gidNumber eq', - 'index cn pres,sub,eq', - 'index sn pres,sub,eq', - 'index uid pres,sub,eq', - 'index displayName pres,sub,eq', + 'objectClass eq', + 'entryCSN eq', + 'entryUUID eq', + 'uidNumber eq', + 'gidNumber eq', + 'cn pres,sub,eq', + 'sn pres,sub,eq', + 'uid pres,sub,eq', + 'displayName pres,sub,eq', ] - # - # olcTLS* attributes are not defined here - # because they do have their own behavior - # according to the puppet module parameters - # - # olcTLSCACertificatePath = $ssl_ca - # olcTLSCertificateFile = $ssl_cert - # olcTLSCertificateKeyFile = $ssl_key - # $cnconfig_default_attrs = [ 'olcConfigFile', 'olcConfigDir', @@ -91,12 +84,16 @@ 'olcIndexSubstrIfAnyStep', 'olcIndexIntLen', 'olcLocalSSF', + 'olcLogLevel', 'olcPidFile', 'olcReadOnly', 'olcReverseLookup', 'olcSaslSecProps', 'olcSockbufMaxIncoming', 'olcSockbufMaxIncomingAuth', + 'olcTLSCACertificateFile', + 'olcTLSCertificateKeyFile', + 'olcTLSCertificateFile', 'olcTLSVerifyClient', 'olcThreads', 'olcToolThreads', @@ -117,6 +114,8 @@ $server_package = [ 'openldap-servers' ] $server_config = 'slapd.conf' + $main_db_name = '{2}hdb' + case $::operatingsystemmajrelease { 5 : { $service = 'ldap' @@ -156,38 +155,29 @@ /(?i:OVS)/ : { $schema_base = [ 'core', 'cosine', 'nis', 'inetorgperson', 'authldap' ] - $modules_base = [ 'back_bdb' ] + $modules_base = [ 'back_hdb' ] } default : { $schema_base = [ 'core', 'cosine', 'nis', 'inetorgperson', ] - $modules_base = [ ] + $modules_base = [ 'back_hdb' ] } } $server_run = '/var/run/openldap' $index_base = [ - 'index objectclass eq', - 'index entryCSN eq', - 'index entryUUID eq', - 'index uidNumber eq', - 'index gidNumber eq', - 'index cn pres,sub,eq', - 'index sn pres,sub,eq', - 'index uid pres,sub,eq', - 'index displayName pres,sub,eq', + 'objectClass eq', + 'entryCSN eq', + 'entryUUID eq', + 'uidNumber eq', + 'gidNumber eq', + 'cn pres,sub,eq', + 'sn pres,sub,eq', + 'uid pres,sub,eq', + 'displayName pres,sub,eq', ] - # - # olcTLS* attributes are not defined here - # because they do have their own behavior - # according to the puppet module parameters - # - # olcTLSCACertificatePath = $ssl_ca - # olcTLSCertificateFile = $ssl_cert - # olcTLSCertificateKeyFile = $ssl_key - # $cnconfig_default_attrs = [ 'olcConfigFile', 'olcConfigDir', @@ -205,12 +195,16 @@ 'olcIndexSubstrIfAnyStep', 'olcIndexIntLen', 'olcLocalSSF', + 'olcLogLevel', 'olcPidFile', 'olcReadOnly', 'olcReverseLookup', 'olcSaslSecProps', 'olcSockbufMaxIncoming', 'olcSockbufMaxIncomingAuth', + 'olcTLSCACertificateFile', + 'olcTLSCertificateKeyFile', + 'olcTLSCertificateFile', 'olcTLSVerifyClient', 'olcThreads', 'olcToolThreads', @@ -255,28 +249,19 @@ $ssl_prefix = '/etc/ssl/certs' $server_run = '/var/run/slapd' $schema_base = [ 'core', 'cosine', 'nis', 'inetorgperson', ] - $modules_base = [ 'back_bdb' ] + $modules_base = [ 'back_hdb' ] $index_base = [ - 'index objectclass eq', - 'index entryCSN eq', - 'index entryUUID eq', - 'index uidNumber eq', - 'index gidNumber eq', - 'index cn pres,sub,eq', - 'index sn pres,sub,eq', - 'index uid pres,sub,eq', - 'index displayName pres,sub,eq', + 'objectClass eq', + 'entryCSN eq', + 'entryUUID eq', + 'uidNumber eq', + 'gidNumber eq', + 'cn pres,sub,eq', + 'sn pres,sub,eq', + 'uid pres,sub,eq', + 'displayName pres,sub,eq', ] - # - # olcTLS* attributes are not defined here - # because they do have their own behavior - # according to the puppet module parameters - # - # olcTLSCACertificatePath = $ssl_ca - # olcTLSCertificateFile = $ssl_cert - # olcTLSCertificateKeyFile = $ssl_key - # $cnconfig_default_attrs = [ 'olcConfigFile', 'olcConfigDir', @@ -294,12 +279,16 @@ 'olcIndexSubstrIfAnyStep', 'olcIndexIntLen', 'olcLocalSSF', + 'olcLogLevel', 'olcPidFile', 'olcReadOnly', 'olcReverseLookup', 'olcSaslSecProps', 'olcSockbufMaxIncoming', 'olcSockbufMaxIncomingAuth', + 'olcTLSCACertificateFile', + 'olcTLSCertificateKeyFile', + 'olcTLSCertificateFile', 'olcTLSVerifyClient', 'olcThreads', 'olcToolThreads', diff --git a/manifests/server/builtin_schema.pp b/manifests/server/builtin_schema.pp new file mode 100644 index 0000000..57dd84d --- /dev/null +++ b/manifests/server/builtin_schema.pp @@ -0,0 +1,18 @@ +define ldap::server::builtin_schema() { + include ldap::server::convertschema + + # Create the LDIF file from the conversion script + exec { "convert_schema_${name}_to_ldif": + cwd => $ldap::params::schema_prefix, + creates => "${ldap::params::schema_prefix}/${name}.ldif", + command => "${ldap::params::prefix}/convertschema.sh -s ${name}.schema -l ${name}.ldif", + require => [Package[$ldap::params::server_package], File["${ldap::params::prefix}/convertschema.sh"]], + } + + exec { "load_schema_${name}": + cwd => $ldap::params::schema_prefix, + command => "/usr/bin/ldapadd -QY EXTERNAL -H ldapi:/// < ${name}.ldif", + unless => "/usr/bin/ldapsearch -QY EXTERNAL -H ldapi:/// -b 'cn=schema,cn=config' '(cn=*${name})' | grep 'numEntries: 1'", + require => Exec["convert_schema_${name}_to_ldif"], + } +} diff --git a/manifests/server/convertschema.pp b/manifests/server/convertschema.pp new file mode 100644 index 0000000..623e74d --- /dev/null +++ b/manifests/server/convertschema.pp @@ -0,0 +1,12 @@ +class ldap::server::convertschema() { + + # Upload the conversion script for the schemas + file { "${ldap::params::prefix}/convertschema.sh": + owner => 'root', + group => 'root', + mode => '0750', + source => 'puppet:///modules/ldap/convertschema.sh', + require => File[$ldap::params::prefix], + } + +} diff --git a/manifests/server/database.pp b/manifests/server/database.pp new file mode 100644 index 0000000..51b5bb1 --- /dev/null +++ b/manifests/server/database.pp @@ -0,0 +1,165 @@ +# == Define: ldap::server::database +# +# Puppet module to manage database configuration for +# **OpenLdap**. +# +# +# === Parameters +# +# [suffix] +# +# **Required** +# +# [rootpw] +# +# **Required** +# +# [rootdn] +# +# *Optional* (defaults to 'cn=admin,${suffix}') +# +# [index_inc] +# +# *Optional* (defaults to []) +# +# [syncprov] +# +# *Optional* (defaults to false) +# +# [syncprov_checkpoint] +# +# *Optional* (defaults to '100 10') +# +# [syncprov_sessionlog] +# +# *Optional* (defaults to *'100'*) +# +# [sync_binddn] +# +# *Optional* (defaults to *'false'*) +# +# [ensure] +# *Optional* (defaults to 'present') +# +# +# === Examples +# +# ldap::server::database { 'olcDatabase={1}hdb,cn=config': +# suffix => 'dc=foo,dc=bar', +# rootpw => '{SHA}iEPX+SQWIR3p67lj/0zigSWTKHg=', +# syncprov => true, +# sync_binddn => 'cn=sync,dc=foo,dc=bar', +# index_inc => [ +# 'index memberUid eq', +# 'index mail eq', +# 'index givenName eq,subinitial', +# ], +# } +# +# === Authors +# +# Emiliano Castagnari ecastag@gmail.com (a.k.a. Torian) +# +# +# === Copyleft +# +# Copyleft (C) 2012 Emiliano Castagnari ecastag@gmail.com (a.k.a. Torian) +# +# +define ldap::server::database( + $suffix, + $rootpw = false, + $rootdn = "cn=admin,${suffix}", + $index_inc = [], + $syncprov = false, + $syncprov_checkpoint = '100 10', + $syncprov_sessionlog = '100', + $sync_binddn = false, + $master = false, + $directory = "${ldap::params::db_prefix}/${name}", +) { + + require ldap + + $dn = "olcDatabase=${name},cn=config" + + if($master and $syncprov) { + $readable_by_sync = "by dn.base=\"${sync_binddn}\" read " + } else { + $readable_by_sync = "" + } + + File { + mode => '0600', + owner => $ldap::params::server_owner, + group => $ldap::params::server_group, + require => Package[$ldap::params::server_package], + } + + file { $directory: + ensure => directory, + } + + $main_database_options = [ + 'objectClass: olcDatabaseConfig', + 'objectClass: olcHdbConfig', + "olcDatabase: ${name}", + "olcDbDirectory: ${directory}", + "olcAccess: to dn.subtree=\"${suffix}\" attrs=userPassword,shadowLastChange ${readable_by_sync}by dn.base=\"gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth\" write by self write by anonymous auth by * none", + "olcAccess: to dn.subtree=\"${suffix}\" attrs=objectClass,entry,gecos,homeDirectory,uid,uidNumber,gidNumber,cn,memberUid ${readable_by_sync}by dn.base=\"gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth\" write by * read", + "olcAccess: to dn.subtree=\"${suffix}\" ${readable_by_sync}by dn.base=\"gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth\" write by self read by * read", + 'olcAccess: to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage', + 'olcDbCheckpoint: 512 30', + 'olcLastMod: TRUE', + "olcSuffix: ${suffix}", + "olcRootDN: ${rootdn}", + ] + + if($rootpw) { + $database_options = [$main_database_options, "olcRootPW: ${rootpw}"] + } else { + $database_options = $main_database_options + } + + $index_base = $ldap::params::index_base + $indices = split(inline_template("<%= (@index_base + @index_inc).map { |index| \"olcDbIndex: #{index}\" }.join(';') %>"),';') + + ldapdn { "${name} database config": + dn => $dn, + attributes => [$database_options, $indices], + unique_attributes => [ + 'olcAccess', + 'olcDatabase', + 'olcDbCheckpoint', + 'olcDbDirectory', + 'olcDbIndex', + 'olcLastMod', + 'olcSuffix', + 'olcRootDN', + 'olcRootPW', + ], + ensure => present, + require => [Class['ldap::server::generic'], File[$directory]], + } + + if($syncprov) { + ldapdn { "${name} syncprov_config": + dn => "olcOverlay={0}syncprov,${dn}", + attributes => [ + 'objectClass: olcOverlayConfig', + 'objectClass: olcSyncProvConfig', + 'olcOverlay: {0}syncprov', + "olcSpCheckpoint: ${syncprov_checkpoint}", + "olcSpSessionlog: ${syncprov_sessionlog}", + ], + unique_attributes => [ + 'olcOverlay', + 'olcSpCheckpoint', + 'olcSpSessionlog', + ], + ensure => present, + require => [Ldapdn["${name} database config"], Ldap::Server::Module['syncprov']], + } + } + +} diff --git a/manifests/server/generic.pp b/manifests/server/generic.pp new file mode 100644 index 0000000..8d5e6e2 --- /dev/null +++ b/manifests/server/generic.pp @@ -0,0 +1,237 @@ +# == Class: ldap::server::generic +# +# Puppet module to manage server configuration for +# **OpenLdap**. +# +# +# === Parameters +# +# [suffix] +# +# **Required** +# +# [schema_inc] +# +# *Optional* (defaults to []) +# +# [modules_inc] +# +# *Optional* (defaults to []) +# +# [cnconfig_attrs] +# Default cn=config attributes that needs to be changed +# upon runs. An array of attributes as key-value pairs. +# eg. ['olcConcurrency: 1'] +# *Optional* (defaults to []) +# +# [log_level] +# +# *Optional* (defaults to 0) +# +# [bind_anon] +# +# *Optional* (defaults to true) +# +# [ssl] +# +# *Requires*: ssl_{cert,ca,key} parameter +# *Optional* (defaults to false) +# +# [ssl_cert] +# +# *Optional* (defaults to false) +# +# [ssl_ca] +# +# *Optional* (defaults to false) +# +# [ssl_key] +# +# *Optional* (defaults to false) +# +# [enable_motd] +# Use motd to report the usage of this module. +# *Requires*: https://github.com/torian/puppet-motd.git +# *Optional* (defaults to false) +# +# [ensure] +# *Optional* (defaults to 'present') +# +# === Authors +# +# Emiliano Castagnari ecastag@gmail.com (a.k.a. Torian) +# +# +# === Copyleft +# +# Copyleft (C) 2012 Emiliano Castagnari ecastag@gmail.com (a.k.a. Torian) +# +# +class ldap::server::generic( + $suffix, + $schema_inc = [], + $modules_inc = [], + $cnconfig_attrs = [], + $log_level = '0', + $bind_anon = true, + $ssl = false, + $ssl_ca = false, + $ssl_cert = false, + $ssl_key = false, + $ensure = present) { + + require ldap + + file { ['/var/cache/local', '/var/cache/local/preseeding']: + ensure => directory, + owner => 'root', + group => 'root', + } + + file { "/var/cache/local/preseeding/slapd.seed": + ensure => present, + content => template("ldap/slapd.seed.erb"), + owner => 'root', + group => 'root', + } + + package { $ldap::params::server_package: + ensure => $ensure, + responsefile => "/var/cache/local/preseeding/slapd.seed", + } + + service { $ldap::params::service: + ensure => running, + enable => true, + pattern => $ldap::params::server_pattern, + require => [ + Package[$ldap::params::server_package], + ], + } + + ldapdn { "module config": + dn => "cn=module{0},cn=config", + attributes => [ + 'objectClass: olcModuleList', + 'cn: module{0}', + "olcModulePath: ${ldap::params::module_prefix}", + ], + unique_attributes => ['olcModulePath'], + ensure => present, + } + + ldap::server::module { $ldap::params::modules_base: } + ldap::server::module { $modules_inc: } + + ldap::server::builtin_schema { $ldap::params::schema_base: } + ldap::server::builtin_schema { $schema_inc: } + + ldapdn { "global confg": + dn => "cn=config", + attributes => [ + "olcArgsFile: ${ldap::params::server_run}/slapd.args", + "olcLogLevel: ${log_level}", + "olcPidFile: ${ldap::params::server_run}/slapd.pid", + ], + unique_attributes => $ldap::params::cnconfig_default_attrs, + ensure => present, + } + + ldapdn { "cnconfig_attrs": + dn => "cn=config", + attributes => $cnconfig_attrs, + unique_attributes => $ldap::params::cnconfig_default_attrs, + ensure => present, + } + + if(!$bind_anon) { + ldapdn { "disallow_bind_anon": + dn => "cn=config", + attributes => [ + 'olcDisallows: bind_anon', + ], + ensure => present, + } + } + + File { + mode => '0640', + owner => $ldap::params::server_owner, + group => $ldap::params::server_group, + } + + $msg_prefix = 'SSL enabled. You must specify' + $msg_suffix = '(filename). It should be located at puppet:///files/ldap' + + if($ssl) { + + if(!$ssl_ca) { fail("${msg_prefix} ssl_ca ${msg_suffix}") } + file { 'ssl_ca': + ensure => present, + source => "puppet:///files/ldap/${ssl_ca}", + path => "${ldap::params::ssl_prefix}/${ssl_ca}", + mode => '0644', + } + + if(!$ssl_cert) { fail("${msg_prefix} ssl_cert ${msg_suffix}") } + file { 'ssl_cert': + ensure => present, + source => "puppet:///files/ldap/${ssl_cert}", + path => "${ldap::params::ssl_prefix}/${ssl_cert}", + mode => '0644', + } + + if(!$ssl_key) { fail("${msg_prefix} ssl_key ${msg_suffix}") } + file { 'ssl_key': + ensure => present, + source => "puppet:///files/ldap/${ssl_key}", + path => "${ldap::params::ssl_prefix}/${ssl_key}", + } + + # Create certificate hash file + exec { 'Server certificate hash': + command => "ln -s ${ldap::params::ssl_prefix}/${ssl_cert} ${ldap::params::cacertdir}/$(openssl x509 -noout -hash -in ${ldap::params::ssl_prefix}/${ssl_cert}).0", + unless => "test -f ${ldap::params::cacertdir}/$(openssl x509 -noout -hash -in ${ldap::params::ssl_prefix}/${ssl_cert}).0", + provider => $::puppetversion ? { + /^3./ => 'shell', + /^2.7/ => 'shell', + /^2.6/ => 'posix', + default => 'posix' + }, + require => File['ssl_cert'], + path => [ "/bin", "/usr/bin", "/sbin", "/usr/sbin" ] + } + + ldapdn { "SSL config": + dn => "cn=config", + attributes => [ + "olcTLSCACertificateFile: ${ldap::params::ssl_prefix}/${ssl_ca}", + "olcTLSCertificateFile: ${ldap::params::ssl_prefix}/${ssl_cert}", + "olcTLSCertificateKeyFile: ${ldap::params::ssl_prefix}/${ssl_key}", + ], + unique_attributes => $ldap::params::cnconfig_default_attrs, + ensure => present, + require => [File['ssl_ca'], File['ssl_cert'], File['ssl_key'], Exec['Server certificate hash']], + notify => Service[$ldap::params::service], + } + + } + + # Additional configurations (for rc scripts) + case $::osfamily { + + 'Debian' : { + class { 'ldap::server::debian': ssl => $ssl } + } + + 'RedHat' : { + class { 'ldap::server::redhat': ssl => $ssl } + } + + #'Suse' : { + # class { 'ldap::server::suse': ssl => $ssl } + #} + + } + +} diff --git a/manifests/server/master.pp b/manifests/server/master.pp index 82c5565..0a72b60 100644 --- a/manifests/server/master.pp +++ b/manifests/server/master.pp @@ -32,8 +32,9 @@ # # [cnconfig_attrs] # Default cn=config attributes that needs to be changed -# upon runs -# *Optional* (defaults to {}) +# upon runs. An array of attributes as key-value pairs. +# eg. ['olcConcurrency: 1'] +# *Optional* (defaults to []) # # [log_level] # @@ -126,7 +127,7 @@ $schema_inc = [], $modules_inc = [], $index_inc = [], - $cnconfig_attrs = {}, + $cnconfig_attrs = [], $log_level = '0', $bind_anon = true, $ssl = false, @@ -146,119 +147,30 @@ motd::register { 'ldap::server::master': } } - package { $ldap::params::server_package: - ensure => $ensure - } - - service { $ldap::params::service: - ensure => running, - enable => true, - pattern => $ldap::params::server_pattern, - require => [ - Package[$ldap::params::server_package], - File["${ldap::params::prefix}/${ldap::params::server_config}"], - ] + class { 'ldap::server::generic': + suffix => $suffix, + schema_inc => $schema_inc, + modules_inc => $modules_inc, + cnconfig_attrs => $cnconfig_attrs, + log_level => $log_level, + bind_anon => $bind_anon, + ssl => $ssl, + ssl_ca => $ssl_ca, + ssl_cert => $ssl_cert, + ssl_key => $ssl_key, + ensure => $ensure, } - if (!empty($cnconfig_attrs)) { - - $cnconfig_default_attrs = $ldap::params::cnconfig_default_attrs - - file {"${ldap::params::prefix}/slapd.d/cn=config-update.ldif": - ensure => present, - content => template("ldap/${ldap::params::prefix}/slapd.d/cn=config-update.ldif.erb"), - require => Service[$ldap::params::service], - } - - exec{"/usr/bin/ldapmodify -Y EXTERNAL -H ldapi:/// -f ${ldap::params::prefix}/slapd.d/cn=config-update.ldif && rm -f ${ldap::params::prefix}/slapd.d/cn=config-update.ldif": - require => File["${ldap::params::prefix}/slapd.d/cn=config-update.ldif"], - } - - } - - File { - mode => '0640', - owner => $ldap::params::server_owner, - group => $ldap::params::server_group, - } - - file { "${ldap::params::prefix}/${ldap::params::server_config}": - ensure => $ensure, - content => template("ldap/${ldap::params::prefix}/${ldap::params::server_config}.erb"), - notify => Service[$ldap::params::service], - require => $ssl ? { - false => [ - Package[$ldap::params::server_package], - ], - true => [ - Package[$ldap::params::server_package], - File['ssl_ca'], - File['ssl_cert'], - File['ssl_key'], - ] - } - } - - $msg_prefix = 'SSL enabled. You must specify' - $msg_suffix = '(filename). It should be located at puppet:///files/ldap' - - if($ssl) { - - if(!$ssl_ca) { fail("${msg_prefix} ssl_ca ${msg_suffix}") } - file { 'ssl_ca': - ensure => present, - source => "puppet:///files/ldap/${ssl_ca}", - path => "${ldap::params::ssl_prefix}/${ssl_ca}", - mode => '0644', - } - - if(!$ssl_cert) { fail("${msg_prefix} ssl_cert ${msg_suffix}") } - file { 'ssl_cert': - ensure => present, - source => "puppet:///files/ldap/${ssl_cert}", - path => "${ldap::params::ssl_prefix}/${ssl_cert}", - mode => '0644', - } - - if(!$ssl_key) { fail("${msg_prefix} ssl_key ${msg_suffix}") } - file { 'ssl_key': - ensure => present, - source => "puppet:///files/ldap/${ssl_key}", - path => "${ldap::params::ssl_prefix}/${ssl_key}", - } - - # Create certificate hash file - exec { 'Server certificate hash': - command => "ln -s ${ldap::params::ssl_prefix}/${ssl_cert} ${ldap::params::cacertdir}/$(openssl x509 -noout -hash -in ${ldap::params::ssl_prefix}/${ssl_cert}).0", - unless => "test -f ${ldap::params::cacertdir}/$(openssl x509 -noout -hash -in ${ldap::params::ssl_prefix}/${ssl_cert}).0", - provider => $::puppetversion ? { - /^3./ => 'shell', - /^2.7/ => 'shell', - /^2.6/ => 'posix', - default => 'posix' - }, - require => File['ssl_cert'], - path => [ "/bin", "/usr/bin", "/sbin", "/usr/sbin" ] - } - - } - - # Additional configurations (for rc scripts) - case $::osfamily { - - 'Debian' : { - class { 'ldap::server::debian': ssl => $ssl } - } - - 'RedHat' : { - class { 'ldap::server::redhat': ssl => $ssl } - } - - #'Suse' : { - # class { 'ldap::server::suse': ssl => $ssl } - #} - + ldap::server::database { $ldap::params::main_db_name: + suffix => $suffix, + rootpw => $rootpw, + rootdn => $rootdn, + index_inc => $index_inc, + syncprov => $syncprov, + syncprov_checkpoint => $syncprov_checkpoint, + syncprov_sessionlog => $syncprov_sessionlog, + sync_binddn => $sync_binddn, + master => true, } } - diff --git a/manifests/server/module.pp b/manifests/server/module.pp new file mode 100644 index 0000000..dace933 --- /dev/null +++ b/manifests/server/module.pp @@ -0,0 +1,12 @@ +define ldap::server::module() { + include ldap + + ldapdn { "${name} module config": + dn => "cn=module{0},cn=config", + attributes => [ + "olcModuleLoad: ${name}" + ], + ensure => present, + require => Ldapdn['module config'], + } +} diff --git a/manifests/server/slave.pp b/manifests/server/slave.pp index 3d49c19..88c34cd 100644 --- a/manifests/server/slave.pp +++ b/manifests/server/slave.pp @@ -66,8 +66,9 @@ # # [cnconfig_attrs] # Default cn=config attributes that needs to be changed -# upon runs -# *Optional* (defaults to {}) +# upon runs. An array of attributes as key-value pairs. +# eg. ['olcConcurrency: 1'] +# *Optional* (defaults to []) # # [log_level] # OpenLdap server log level. @@ -176,7 +177,7 @@ $schema_inc = [], $modules_inc = [], $index_inc = [], - $cnconfig_attrs = {}, + $cnconfig_attrs = [], $log_level = '0', $bind_anon = true, $ssl = false, @@ -185,7 +186,7 @@ $ssl_key = false, $sync_type = 'refreshOnly', $sync_interval = '00:00:10:00', - $sync_base = '', + $sync_base = $suffix, $sync_filter = '(objectClass=*)', $sync_attrs = '*', $sync_scope = 'sub', @@ -198,121 +199,45 @@ motd::register { 'ldap::server::slave': } } - package { $ldap::params::server_package: - ensure => $ensure - } - - service { $ldap::params::service: - ensure => running, - enable => true, - pattern => $ldap::params::server_pattern, - require => [ - Package[$ldap::params::server_package], - File["${ldap::params::prefix}/${ldap::params::server_config}"], - ] - } - - - if (!empty($cnconfig_attrs)) { - - $cnconfig_default_attrs = $ldap::params::cnconfig_default_attrs - - file {"${ldap::params::prefix}/slapd.d/cn=config-update.ldif": - ensure => present, - content => template("ldap/${ldap::params::prefix}/slapd.d/cn=config-update.ldif.erb"), - require => Service[$ldap::params::service], - } - - exec{"/usr/bin/ldapmodify -Y EXTERNAL -H ldapi:/// -f ${ldap::params::prefix}/slapd.d/cn=config-update.ldif && rm -f ${ldap::params::prefix}/slapd.d/cn=config-update.ldif": - require => File["${ldap::params::prefix}/slapd.d/cn=config-update.ldif"], - } - - } - - File { - mode => '0640', - owner => $ldap::params::server_owner, - group => $ldap::params::server_group, + class { 'ldap::server::generic': + suffix => $suffix, + schema_inc => $schema_inc, + modules_inc => $modules_inc, + cnconfig_attrs => $cnconfig_attrs, + log_level => $log_level, + bind_anon => $bind_anon, + ssl => $ssl, + ssl_ca => $ssl_ca, + ssl_cert => $ssl_cert, + ssl_key => $ssl_key, + ensure => $ensure, } - file { "${ldap::params::prefix}/${ldap::params::server_config}": - ensure => $ensure, - content => template("ldap/${ldap::params::prefix}/${ldap::params::server_config}.erb"), - notify => Service[$ldap::params::service], - require => $ssl ? { - false => [ - Package[$ldap::params::server_package], - ], - true => [ - Package[$ldap::params::server_package], - File['ssl_ca'], - File['ssl_cert'], - File['ssl_key'], - ] - } + ldap::server::database { $ldap::params::main_db_name: + suffix => $suffix, + rootpw => $rootpw, + rootdn => $rootdn, + index_inc => $index_inc, + syncprov => $syncprov, + syncprov_checkpoint => $syncprov_checkpoint, + syncprov_sessionlog => $syncprov_sessionlog, + sync_binddn => $sync_binddn, } - $msg_prefix = 'SSL enabled. You must specify' - $msg_suffix = '(filename). It should be located at puppet:///files/ldap' - - if($ssl) { - - if(!$ssl_ca) { fail("${msg_prefix} ssl_ca ${msg_suffix}") } - file { 'ssl_ca': - ensure => present, - source => "puppet:///files/ldap/${ssl_ca}", - path => "${ldap::params::ssl_prefix}/${ssl_ca}", - mode => '0644', - } - - if(!$ssl_cert) { fail("${msg_prefix} ssl_cert ${msg_suffix}") } - file { 'ssl_cert': - ensure => present, - source => "puppet:///files/ldap/${ssl_cert}", - path => "${ldap::params::ssl_prefix}/${ssl_cert}", - mode => '0644', - } - - if(!$ssl_key) { fail("${msg_prefix} ssl_key ${msg_suffix}") } - file { 'ssl_key': - ensure => present, - source => "puppet:///files/ldap/${ssl_key}", - path => "${ldap::params::ssl_prefix}/${ssl_key}", - } - - # Create certificate hash file - exec { 'Server certificate hash': - command => "ln -s ${ldap::params::ssl_prefix}/${ssl_cert} ${ldap::params::cacertdir}/$(openssl x509 -noout -hash -in ${ldap::params::ssl_prefix}/${ssl_cert}).0", - unless => "test -f ${ldap::params::cacertdir}/$(openssl x509 -noout -hash -in ${ldap::params::ssl_prefix}/${ssl_cert}).0", - provider => $::puppetversion ? { - /^3./ => 'shell', - /^2.7/ => 'shell', - /^2.6/ => 'posix', - default => 'posix' - }, - require => File['ssl_cert'], - path => [ "/bin", "/usr/bin", "/sbin", "/usr/sbin" ] - } - - } - - # Additional configurations (for rc scripts) - case $::osfamily { - - 'Debian' : { - class { 'ldap::server::debian': ssl => $ssl } - } - - 'RedHat' : { - class { 'ldap::server::redhat': ssl => $ssl } - } - - #'Suse' : { - # class { 'ldap::server::suse': ssl => $ssl } - #} - + ldapdn { "syncrepl": + dn => "olcDatabase=${ldap::params::main_db_name},cn=config", + attributes => [ + "olcSyncrepl: rid=${sync_rid} provider=${sync_provider} bindmethod=simple timeout=0 network-timeout=0 binddn=\"${sync_binddn}\" credentials=\"${sync_bindpw}\" keepalive=0:0:0 starttls=no filter=\"${sync_filter}\" searchbase=\"${sync_base}\" scope=${sync_scope} attrs=\"${sync_attrs}\" schemachecking=off type=${sync_type} interval=${sync_interval} retry=undefined", + "olcLimits: dn.exact=\"${sync_binddn}\" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited", + "olcUpdateRef: ${sync_provider}", + ], + unique_attributes => [ + 'olcLimits', + 'olcSyncrepl', + 'olcUpdateRef', + ], + ensure => present, + require => Ldap::Server::Database[$ldap::params::main_db_name], } } - -# vim: ts=4 diff --git a/puppet/files/ldap/ca.key b/puppet/files/ldap/ca.key new file mode 100644 index 0000000..98ddd86 --- /dev/null +++ b/puppet/files/ldap/ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAunW2cXtVvB6S5JAQWfF+J1BS30MTDUa3b1cOrGBOBxtBsOwT +Tpmldl1kS73NiAUkmyNraK9ROb26JZMx05hSzzbjmlfkpKY3yAB6+mGJ0AcS+E8y +H8axQ2vBiWtshB8EQieP0Gc7UwIkYCr1zjkPMUm9uRmeGB+SY6FzSM48oW0aJDXy +sHslQtYFvQNAnpsGbRb+6zCVJPm1ZUZIz5krfC7MgqZ4C0nmSwXcTGNScidgp1Wh +bW57uHyS/kCjg+Oy9aNvrn2l3x2rzb27hgRDJfmJnvHzbc0DFnH+OVjc6+BS8YiB +XZ2RomoBRuKr0+I8xNP/wGGmkA1MSB55vjZjgwIDAQABAoIBAGwur9PXwz6KXp/O +Fi6ZNqCIsgxnVj51sSggUpsgpSTqY4rK7cjEzIOIXBC15vHsSfjY7BCo52+g7Mew +R1CFKEahihyaskX1Spf372ImBVd1Q0Rebq6REXgUpQ2eDi/57vaTDZXUz06yB3jk +dAlagb4PEsy6WEeWydCUc9biEbt2GxN4h0GBMaEO+kv2V06F9yP2dLPJN3fTVP2M +W7MlAiwyAeQ4qejROsdFsriIr81MLdurIN90QrbKXVlFaFmg8a+35z3zvguZcskg +76da0PgNzM973/sXXA51N10hoUsWy+iurvUtNQz4/9SZYgcDG/cfoRzMVgFYsN4J +Nn6jAkECgYEA7uLL8A8rjH/csTAYDuszf9p83Pre9/8Zumbni0zpyXfkOdAdGxZJ +NO64VgkNDfzpqZ+nC9FyzQVP6hVhJM7ARtLDUD1Jk4VGMv1cByX4o7M80P66ltET +K8nu4UDqxu9jVi0hD+AVzT/UJnVpI/BCOFkpwPHqn8fM5URNEKdezV0CgYEAx9Fp +1JDFzMah5EjxNFxwDShZ30xF53f+YRuNssupXlib6mUDiCSzwWhdStANVnYFrbaP +o0XFe+OB1u+kE16hHGr7+c7Y28tiLJVu12H3vv3CjyvfW2FgOPaKuOGj50HcOEls +L3wn4UsrSTHuXSzJhhomSPTUuatWTw4y+cKYBl8CgYB9JFppm8jYhXL7b5Jq3lPQ +OokGynVbvUuhFp9dniEq5Z+tmZRlyDr5e8UdbQhlt4RA094SltppBMtkeWa2fYjE +kUy4ECMnIL4xoABBzmZ+ezr00Ty2HTjJy7NUVUmohWeokWOsiVtidnmQ3BkENqy6 +EBuLdC/RHD3+LNWiT8ueEQKBgExJkmsaXrpAt7xNcPF5vTn4xt8u4p1tvYvblizF +/sLLd7N0n3WR9aMIsl2GsyDobGCXC53dXHlhMcgas/zFKyOLYOpN45N+wdudrbTD +bW/YuDgrGtprge4dinbthsMa7PX8ajZy41LrfPoz+vpNyB7PywAdoT3FwWzrUeDz +P1FnAoGAcfIq1VhcE8A+0OAPHDaPAQ1PZEIjxRS1naAY9N/iPzI5nkCDwF2dns1W +DsGgHTFhQZUraQgDHQWFNMBW2sbWUmwsL139bhNAJ9K1n6mo5wFqgeHVwLJPiFAJ +gcZBzOnDj+oNB+xTi6HPFumfz3uMqsQxb8N0jbajj2kY3yLINJY= +-----END RSA PRIVATE KEY----- diff --git a/puppet/files/ldap/ca.pem b/puppet/files/ldap/ca.pem new file mode 100644 index 0000000..21960ef --- /dev/null +++ b/puppet/files/ldap/ca.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEajCCA1KgAwIBAgIJAJfTO7cUrNPkMA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNV +BAYTAkdCMRAwDgYDVQQIEwdFbmdsYW5kMRIwEAYDVQQHEwlMaXZlcnBvb2wxDTAL +BgNVBAoTBE5vbmUxDTALBgNVBAsTBE5vbmUxEDAOBgNVBAMTB2Zvby5iYXIxGjAY +BgkqhkiG9w0BCQEWC2Zvb0BiYXIuY29tMCAXDTE0MDUxMzIzNDEwNVoYDzIyOTQw +OTIyMjM0MTA1WjB/MQswCQYDVQQGEwJHQjEQMA4GA1UECBMHRW5nbGFuZDESMBAG +A1UEBxMJTGl2ZXJwb29sMQ0wCwYDVQQKEwROb25lMQ0wCwYDVQQLEwROb25lMRAw +DgYDVQQDEwdmb28uYmFyMRowGAYJKoZIhvcNAQkBFgtmb29AYmFyLmNvbTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALp1tnF7VbwekuSQEFnxfidQUt9D +Ew1Gt29XDqxgTgcbQbDsE06ZpXZdZEu9zYgFJJsja2ivUTm9uiWTMdOYUs8245pX +5KSmN8gAevphidAHEvhPMh/GsUNrwYlrbIQfBEInj9BnO1MCJGAq9c45DzFJvbkZ +nhgfkmOhc0jOPKFtGiQ18rB7JULWBb0DQJ6bBm0W/uswlST5tWVGSM+ZK3wuzIKm +eAtJ5ksF3ExjUnInYKdVoW1ue7h8kv5Ao4PjsvWjb659pd8dq829u4YEQyX5iZ7x +823NAxZx/jlY3OvgUvGIgV2dkaJqAUbiq9PiPMTT/8BhppANTEgeeb42Y4MCAwEA +AaOB5jCB4zAdBgNVHQ4EFgQUoSs9QpGK8cxWGvYRXwa7Sx9puOMwgbMGA1UdIwSB +qzCBqIAUoSs9QpGK8cxWGvYRXwa7Sx9puOOhgYSkgYEwfzELMAkGA1UEBhMCR0Ix +EDAOBgNVBAgTB0VuZ2xhbmQxEjAQBgNVBAcTCUxpdmVycG9vbDENMAsGA1UEChME +Tm9uZTENMAsGA1UECxMETm9uZTEQMA4GA1UEAxMHZm9vLmJhcjEaMBgGCSqGSIb3 +DQEJARYLZm9vQGJhci5jb22CCQCX0zu3FKzT5DAMBgNVHRMEBTADAQH/MA0GCSqG +SIb3DQEBBQUAA4IBAQAXG/n4KTvwT6FNhF7O968g+QaQEMUqyVPIz96MsskCtYtq +rvUENANUYqln+KnjNuOEnpy0+S/l709LFyWMA5xW5RcnjBUXengVTNMbZUrUVXje +GOKRBSLO1cG9aSZY7bbbShpPgemyvEjCAqDJVWjcJk/VQeIQ5Wv7sWsn3fUkAvMl +bGRRDFQDd8J8UHpbXS2rScFmBjnoV457TO+Pm1Q4ce/5C7zGGLt88LJS5yBP97p1 +Jjd9rSTj1MHdNkFmD9nOur03CbNXPYWQ4ET4rU0DrTcha3NgedWGIWeup/OKpuao +KVQBwj+XFKmqPfp+hoQU+3Sbqjhw5J5pnb40/ayh +-----END CERTIFICATE----- diff --git a/puppet/files/ldap/ca.srl b/puppet/files/ldap/ca.srl new file mode 100644 index 0000000..ffd246b --- /dev/null +++ b/puppet/files/ldap/ca.srl @@ -0,0 +1 @@ +A792F799A476A268 diff --git a/puppet/files/ldap/master-ldap.client.pem b/puppet/files/ldap/master-ldap.client.pem new file mode 120000 index 0000000..7ffda6b --- /dev/null +++ b/puppet/files/ldap/master-ldap.client.pem @@ -0,0 +1 @@ +master-ldap.pem \ No newline at end of file diff --git a/puppet/files/ldap/master-ldap.csr b/puppet/files/ldap/master-ldap.csr new file mode 100644 index 0000000..98b1798 --- /dev/null +++ b/puppet/files/ldap/master-ldap.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICxDCCAawCAQAwfzELMAkGA1UEBhMCR0IxEDAOBgNVBAgTB0VuZ2xhbmQxEjAQ +BgNVBAcTCUxpdmVycG9vbDENMAsGA1UEChMETm9uZTENMAsGA1UECxMETm9uZTEQ +MA4GA1UEAxMHZm9vLmJhcjEaMBgGCSqGSIb3DQEJARYLZm9vQGJhci5jb20wggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDcjNqfynb71pLCRvpLsaDLLrwJ +rMS+YwCncIwX67bUVYUTHtLkX9DEqHzGTW0VEVl5iBCD1+cR2XwU6XqNP2dsZMZU +/yypfjw8OPqHjBDu2qsKAsbU4vCpY/zhvRwlxhUg/MdaEPjosJgHWRj7nt/WHYoo +SgCrowZ+RrTB+0yRNBtXVMXWBA1ZpOQn8Tx2d2iBC5PS345+x3eJY/1M4uuKJW/d +tGjGkOpRDmKpR2lGixTjtwvV0TAjeEaJhEzGEp1Hr6qYNKF/IT8nP2WQtLOcP2L2 +4xqUFYDGivHtUndMRpzOsuUIcPwfBVgQiHKD17CVZIJNDBOiP3fuqC9Tu8w3AgMB +AAGgADANBgkqhkiG9w0BAQUFAAOCAQEAW9h1GmsYdbctBOvGG7I+r/MUHUVEEBTR +qcD6of+NYv9Ynp7vK+PxeUmzcRnfKQxeUkCbeoOb2j0dj2/+3K1B72o980Pc6my/ +s0C//xBlIawBZ4cqCcf8nC9IbZrDQJzhOlp7qBaKInsUdpHCQ/U285+7u7+naEX7 +OG42Z/n9x8T7DV4O/ySLSNsaEs3dGEZQzJTb4RnKLAFCBYqTE/BFLWpnxiyVYc9m +GdedQbPKHMrTCZVHQjuk+d1Z5QW5AV+FGJ/D0mQO+zYRdgHAsSJaHVT3vJVLU+Gm +8gEcwDETS9iGCUriimAIafYbVjWMc7GKZ3p2R0EYomaJeoJjnHqV2Q== +-----END CERTIFICATE REQUEST----- diff --git a/puppet/files/ldap/master-ldap.key b/puppet/files/ldap/master-ldap.key new file mode 100644 index 0000000..f6fdaa1 --- /dev/null +++ b/puppet/files/ldap/master-ldap.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA3Izan8p2+9aSwkb6S7Ggyy68CazEvmMAp3CMF+u21FWFEx7S +5F/QxKh8xk1tFRFZeYgQg9fnEdl8FOl6jT9nbGTGVP8sqX48PDj6h4wQ7tqrCgLG +1OLwqWP84b0cJcYVIPzHWhD46LCYB1kY+57f1h2KKEoAq6MGfka0wftMkTQbV1TF +1gQNWaTkJ/E8dndogQuT0t+Ofsd3iWP9TOLriiVv3bRoxpDqUQ5iqUdpRosU47cL +1dEwI3hGiYRMxhKdR6+qmDShfyE/Jz9lkLSznD9i9uMalBWAxorx7VJ3TEaczrLl +CHD8HwVYEIhyg9ewlWSCTQwToj937qgvU7vMNwIDAQABAoIBAQDTJcq8xmJJUrFo +a/l+9EzjmRePD6fAOhpLM3X8BMAh4sondxAichT14fSrCT9Qty55Kqnc/5uKPDQN +9UVN6xDB9mzmvPHuRxu18DryAEfJ1PGBAQ2IjMgo88AMAjfrdguObFMSyEL0yvSR +vFwtsXnMjLVTQx4HNGmoBVtSh5tTOs91hz+L0iCXnqZwmQIQ956ZNVLts5PpvObu +4y3F8xu3cJBTaKGxPxvf41I4PSr660uogx/Lp15r54yGmxDrPdN66dLCJy/hYnZr +hlh4xq73sUL/IfYL6v9HuWLMF1sUaevt/gNT5BrLdffw/Z3qcuS8MBkuABhRpdtI +snPsLlqBAoGBAPRwQZuQNVT/WMro4GjR7dmCnqYIIB/ucySDo8qf1UvSSJthDkfA +RdK/TJC5HJ7NbTfS3AYrjJ73+vyrAvrHKGwDPUkkqSwz+7aQ18Ws4Yx8iP0kcgXU +EPqAAM4oTMT7gHO88r6J63bHD5g/soPPLT2jK61VSYrMVYqsqTddeAJBAoGBAOb7 +WapeSA/Lj5Rqn5mo9iQUIt+0At2qJB5sFY9Tw5VBJ2uCWmfEOGzJf8dOnugYNEHO +IUCUQDFfCbiUNiydytNTzqmWl+X8DUDiHNgGIlER+lIv0vA1d+KvATRCdAO7Q/+j +1gXN990odsxbt5fKVxyJZl91oH0RzSIv6OiqwsB3AoGBALjuDNTSn2a04MpysQTc +fiQnLDvUvekqj7Y6Wbq8fbSlXQUgwpsKHkk5LYX4ZmzGYjDvuEJ6xCKo3hw2jUSJ +VE2TjdQ+hjbJBVCz+Gq3RtTwivtpjVJXhJjgKrvNxXbqB96IPuZkld4A8A1xc29B +WKrfPfYfJkp6GOWXh1Y5wseBAoGAB45xgIFVGi8uo0xZrE06wDNocKkS7u2CAPzI +N8glvdxDzeCV1I82yVhONdb8m9su2nhD3jj7YKIbT06J9yuyVveG60Vh8sQsKtM6 +3lZVCRHtfoLSA8m8Ak53W55Q7U5TCynw9n+mfDW2rTjS6MRV1TkpgDX46h6K93NZ +y0LK3EECgYAWjjhJczbz8H+/3InYXw76Uo5hbzb6SmxvE+e03RhkuZe1Vi9qo5FN +7iAS+OkgDrMhOvoIOzUcLRs7h4/z8Y/S0Xne2XMS7CRkN1QN1vI/ht7hm3SH4qxN +tvdw0nlt13dF3hgnSR/sS0OPBKhMijGcBTGDKFsTNyh1cZIBTdgSKQ== +-----END RSA PRIVATE KEY----- diff --git a/puppet/files/ldap/master-ldap.pem b/puppet/files/ldap/master-ldap.pem new file mode 100644 index 0000000..ddcd2e9 --- /dev/null +++ b/puppet/files/ldap/master-ldap.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDejCCAmICCQCnkveZpHaiaDANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJH +QjEQMA4GA1UECBMHRW5nbGFuZDESMBAGA1UEBxMJTGl2ZXJwb29sMQ0wCwYDVQQK +EwROb25lMQ0wCwYDVQQLEwROb25lMRAwDgYDVQQDEwdmb28uYmFyMRowGAYJKoZI +hvcNAQkBFgtmb29AYmFyLmNvbTAeFw0xNDA1MTMyMzU1MTJaFw0yODAxMjAyMzU1 +MTJaMH8xCzAJBgNVBAYTAkdCMRAwDgYDVQQIEwdFbmdsYW5kMRIwEAYDVQQHEwlM +aXZlcnBvb2wxDTALBgNVBAoTBE5vbmUxDTALBgNVBAsTBE5vbmUxEDAOBgNVBAMT +B2Zvby5iYXIxGjAYBgkqhkiG9w0BCQEWC2Zvb0BiYXIuY29tMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3Izan8p2+9aSwkb6S7Ggyy68CazEvmMAp3CM +F+u21FWFEx7S5F/QxKh8xk1tFRFZeYgQg9fnEdl8FOl6jT9nbGTGVP8sqX48PDj6 +h4wQ7tqrCgLG1OLwqWP84b0cJcYVIPzHWhD46LCYB1kY+57f1h2KKEoAq6MGfka0 +wftMkTQbV1TF1gQNWaTkJ/E8dndogQuT0t+Ofsd3iWP9TOLriiVv3bRoxpDqUQ5i +qUdpRosU47cL1dEwI3hGiYRMxhKdR6+qmDShfyE/Jz9lkLSznD9i9uMalBWAxorx +7VJ3TEaczrLlCHD8HwVYEIhyg9ewlWSCTQwToj937qgvU7vMNwIDAQABMA0GCSqG +SIb3DQEBBQUAA4IBAQCM6s2aQbWpn0iJd0jmbWKFauMdhtQHUWhj4PpyhAZLK8AQ +f3Z5Lt2ZwDXaK50TaB7z16RbcCdFcmU6L0uONOMBDpzZ/B5lADuwv375HO4NbCRT +qn2gXl8h9ylXBfwworkno0m/fLVsbuvq4R1HSOG4MHrMUh1gdISKsO2iRfxPdXA3 +arXjWD6pIRbH4Ns8JhGNv+wC3y++5Whp5jHq3ndKbc1CdeXU22lwXwAJPDm+EmN0 +HkcZG6nET54bnOCjXsYQnAP8OXtM2yTdszIe0T4OA+uGO1XKLYyLKtRZnYB7XQ/D +61XfaCpsnFo53VulPxhauPLRufhODQg/x6cbxmVr +-----END CERTIFICATE----- diff --git a/puppet/fileserver.conf b/puppet/fileserver.conf new file mode 100644 index 0000000..2a9d580 --- /dev/null +++ b/puppet/fileserver.conf @@ -0,0 +1,3 @@ +[files] + path /tmp/kitchen/files + allow * diff --git a/puppet/master/manifests/site.pp b/puppet/master/manifests/site.pp new file mode 100644 index 0000000..be39594 --- /dev/null +++ b/puppet/master/manifests/site.pp @@ -0,0 +1,138 @@ +class { 'ldap::client': + uri => 'ldaps:///', + base => 'dc=foo,dc=bar', + ssl => true, + ssl_cert => 'master-ldap.client.pem', +} + +class { 'ldap::server::master': + suffix => 'dc=foo,dc=bar', + rootpw => 'password', + schema_inc => ['ppolicy'], + modules_inc => ['ppolicy', 'syncprov'], + index_inc => ['title pres'], + syncprov => true, + sync_binddn => 'cn=sync,dc=foo,dc=bar', + ssl => true, + ssl_ca => 'ca.pem', + ssl_cert => 'master-ldap.pem', + ssl_key => 'master-ldap.key', + cnconfig_attrs => ['olcConcurrency: 1'], + log_level => '4', + bind_anon => false, +} + +ldapdn { 'add database': + ensure => present, + dn => 'dc=foo,dc=bar', + attributes => [ + 'dc: foo', + 'objectClass: top', + 'objectClass: dcObject', + 'objectClass: organization', + 'o: Foo Dot Bar', + ], + unique_attributes => ['dc', 'o'], + require => Class['ldap::server::master'], +} + +ldapdn { "ou users": + dn => "ou=users,dc=foo,dc=bar", + attributes => [ + 'ou: users', + 'objectClass: organizationalUnit' + ], + unique_attributes => ["ou"], + ensure => present, + require => Ldapdn['add database'], +} + +ldapdn { "test user": + dn => "uid=testuser,ou=users,dc=foo,dc=bar", + attributes => [ + 'objectClass: top', + 'objectClass: person', + 'objectClass: organizationalPerson', + 'objectClass: inetOrgPerson', + 'cn: Joe Bloggs', + 'sn: Bloggs', + 'uid: testuser', + 'givenName: Joe', + 'mail: foo@bar.com', + 'userPassword: {ssha}YlANix4RcH5rySCWSmzoSzbvj2hzb21lc2FsdA==', # somepassword + ], + unique_attributes => [ + 'uid', + 'cn', + 'sn', + 'givenName', + 'mail', + 'userPassword', + ], + indifferent_attributes => ["userPassword"], + ensure => present, + require => Ldapdn['ou users'], +} + +# Secondary database + +ldap::server::database { '{2}hdb': + suffix => 'dc=doo,dc=dah', + rootpw => 'otherpassword', + index_inc => ['title pres'], + syncprov => true, + sync_binddn => 'cn=sync,dc=doo,dc=dah', + master => true, +} + +ldapdn { 'add secondary database': + ensure => present, + dn => 'dc=doo,dc=dah', + attributes => [ + 'dc: doo', + 'objectClass: top', + 'objectClass: dcObject', + 'objectClass: organization', + 'o: Doo Dot Dah', + ], + unique_attributes => ['dc', 'o'], + require => Ldap::Server::Database['{2}hdb'], +} + +ldapdn { "secondary - ou users": + dn => "ou=users,dc=doo,dc=dah", + attributes => [ + 'ou: users', + 'objectClass: organizationalUnit' + ], + unique_attributes => ["ou"], + ensure => present, + require => Ldapdn['add secondary database'], +} + +ldapdn { "secondary - test user": + dn => "uid=testuser,ou=users,dc=doo,dc=dah", + attributes => [ + 'objectClass: top', + 'objectClass: person', + 'objectClass: organizationalPerson', + 'objectClass: inetOrgPerson', + 'cn: Joe Bloggs', + 'sn: Bloggs', + 'uid: testuser', + 'givenName: Joe', + 'mail: foo@bar.com', + 'userPassword: {ssha}YlANix4RcH5rySCWSmzoSzbvj2hzb21lc2FsdA==', # somepassword + ], + unique_attributes => [ + 'uid', + 'cn', + 'sn', + 'givenName', + 'mail', + 'userPassword', + ], + indifferent_attributes => ["userPassword"], + ensure => present, + require => Ldapdn['secondary - ou users'], +} diff --git a/puppet/slave/manifests/site.pp b/puppet/slave/manifests/site.pp new file mode 100644 index 0000000..cf48e49 --- /dev/null +++ b/puppet/slave/manifests/site.pp @@ -0,0 +1,77 @@ +class { 'ldap::client': + uri => 'ldaps:///', + base => 'dc=foo,dc=bar', + ssl => true, + ssl_cert => 'master-ldap.client.pem', +} + +class { 'ldap::server::slave': + suffix => 'dc=foo,dc=bar', + rootpw => 'password', + schema_inc => ['ppolicy'], + modules_inc => ['ppolicy', 'syncprov'], + index_inc => ['title pres'], + sync_provider => 'ldapi:///', + sync_binddn => 'cn=sync,dc=foo,dc=bar', + sync_bindpw => 'foobar', + sync_rid => '123', + sync_updatedn => 'cn=admin,dc=foo,dc=bar', + ssl => true, + ssl_ca => 'ca.pem', + ssl_cert => 'master-ldap.pem', + ssl_key => 'master-ldap.key', + log_level => '4', + cnconfig_attrs => ['olcConcurrency: 1'], +} + +ldapdn { 'add database': + ensure => present, + dn => 'dc=foo,dc=bar', + attributes => [ + 'dc: foo', + 'objectClass: top', + 'objectClass: dcObject', + 'objectClass: organization', + 'o: Foo Dot Bar', + ], + unique_attributes => ['dc', 'o'], + require => Class['ldap::server::slave'], +} + +ldapdn { "ou users": + dn => "ou=users,dc=foo,dc=bar", + attributes => [ + 'ou: users', + 'objectClass: organizationalUnit' + ], + unique_attributes => ["ou"], + ensure => present, + require => Ldapdn['add database'], +} + +ldapdn { "test user": + dn => "uid=testuser,ou=users,dc=foo,dc=bar", + attributes => [ + 'objectClass: top', + 'objectClass: person', + 'objectClass: organizationalPerson', + 'objectClass: inetOrgPerson', + 'cn: Joe Bloggs', + 'sn: Bloggs', + 'uid: testuser', + 'givenName: Joe', + 'mail: foo@bar.com', + 'userPassword: {ssha}YlANix4RcH5rySCWSmzoSzbvj2hzb21lc2FsdA==', # somepassword + ], + unique_attributes => [ + 'uid', + 'cn', + 'sn', + 'givenName', + 'mail', + 'userPassword', + ], + indifferent_attributes => ["userPassword"], + ensure => present, + require => Ldapdn['ou users'], +} diff --git a/spec/fixtures/modules/ldap/manifests b/spec/fixtures/modules/ldap/manifests deleted file mode 120000 index 3510554..0000000 --- a/spec/fixtures/modules/ldap/manifests +++ /dev/null @@ -1 +0,0 @@ -../../../../../puppet-ldap/manifests \ No newline at end of file diff --git a/spec/fixtures/modules/ldap/templates b/spec/fixtures/modules/ldap/templates deleted file mode 120000 index b47afbc..0000000 --- a/spec/fixtures/modules/ldap/templates +++ /dev/null @@ -1 +0,0 @@ -../../../../../puppet-ldap/templates \ No newline at end of file diff --git a/templates/etc/default/slapd.erb b/templates/etc/default/slapd.erb index ab7f902..dc5cf48 100644 --- a/templates/etc/default/slapd.erb +++ b/templates/etc/default/slapd.erb @@ -1,7 +1,7 @@ # Default location of the slapd.conf file or slapd.d cn=config directory. If # empty, use the compiled-in default (/etc/ldap/slapd.d with a fallback to # /etc/ldap/slapd.conf). -SLAPD_CONF=/etc/ldap/slapd.conf +#SLAPD_CONF=/etc/ldap/slapd.conf # System account to run the slapd server under. If empty the server # will run as root. diff --git a/templates/etc/ldap/slapd.conf.erb b/templates/etc/ldap/slapd.conf.erb deleted file mode 120000 index a6581c5..0000000 --- a/templates/etc/ldap/slapd.conf.erb +++ /dev/null @@ -1 +0,0 @@ -../openldap/slapd.conf.erb \ No newline at end of file diff --git a/templates/etc/ldap/slapd.d b/templates/etc/ldap/slapd.d deleted file mode 120000 index abdc0b0..0000000 --- a/templates/etc/ldap/slapd.d +++ /dev/null @@ -1 +0,0 @@ -../openldap/slapd.d \ No newline at end of file diff --git a/templates/etc/openldap/slapd.conf.erb b/templates/etc/openldap/slapd.conf.erb deleted file mode 100644 index 939ea2e..0000000 --- a/templates/etc/openldap/slapd.conf.erb +++ /dev/null @@ -1,136 +0,0 @@ -############################################################################### -# << FILE MANAGED BY PUPPET >> -# Manual changes are likey to be overwritten -############################################################################### - -#allow bind_v2 -<% if @bind_anon == true then -%> -allow bind_anon_dn -<% end -%> - -# Schema and objectClass definitions -<% scope.lookupvar('ldap::params::schema_base').each do |schema| -%> -include <%= scope.lookupvar('ldap::params::schema_prefix') %>/<%= schema %>.schema -<% end -%> - -# Additional schemas -<% scope.lookupvar('schema_inc').each do |schema| -%> -include <%= scope.lookupvar('ldap::params::schema_prefix') %>/<%= schema %>.schema -<% end -%> - -pidfile <%= scope.lookupvar('ldap::params::server_run') %>/slapd.pid -argsfile <%= scope.lookupvar('ldap::params::server_run') %>/slapd.args - -# Read slapd.conf(5) for possible values -loglevel <%= @log_level %> - -modulepath <%= scope.lookupvar('ldap::params::module_prefix') %> -<% scope.lookupvar('ldap::params::modules_base').each do |mod| -%> -moduleload <%= mod %> -<% end -%> - -<% @modules_inc.each do |mod| -%> -moduleload <%= mod %> -<% end -%> -#moduleload syncprov - -<% if @ssl == true then -%> -TLSCACertificateFile <%= scope.lookupvar('ldap::params::ssl_prefix') %>/<%= @ssl_ca %> -TLSCertificateFile <%= scope.lookupvar('ldap::params::ssl_prefix') %>/<%= @ssl_cert %> -TLSCertificateKeyFile <%= scope.lookupvar('ldap::params::ssl_prefix') %>/<%= @ssl_key %> -#TLSVerifyClient allow -<% end -%> - -####################################################################### - -# FIXME: puppet -backend bdb - -# FIXME: puppet -database bdb -suffix "<%= @suffix %>" -directory <%= scope.lookupvar('ldap::params::db_prefix') %> -rootdn "<%= @rootdn %>" -rootpw "<%= @rootpw %>" - -####################################################################### -# << Syncprov - -<% if has_variable?('syncprov') and @syncprov == true then -%> -overlay syncprov -syncprov-checkpoint <%= @syncprov_checkpoint %> -syncprov-sessionlog <%= @syncprov_sessionlog %> -<% end -%> - -<% if has_variable?('sync_rid') then -%> -syncrepl rid=<%= @sync_rid %> - provider=<%= @sync_provider %> - type=<%= @sync_type %> - interval=<%= @sync_interval %> - searchbase="<% if @sync_base == '' then %><%= @suffix %><% else %><%= @sync_base %><% end %>" - filter="<%= @sync_filter %>" - attrs="<%= @sync_attrs %>" - scope=<%= @sync_scope %> - schemachecking=off - bindmethod=simple - binddn="<%= @sync_binddn %>" - credentials="<%= @sync_bindpw %>" - -updateref <%= @sync_provider %> -<% end -%> - -####################################################################### - -checkpoint 512 30 -dbconfig set_cachesize 0 2097152 0 -dbconfig set_lk_max_objects 1500 -dbconfig set_lk_max_locks 1500 -dbconfig set_lk_max_lockers 1500 - -####################################################################### - -lastmod on - -<% if @syncprov and @sync_binddn != false then -%> -limits dn.exact="<%= @sync_binddn%>" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited -<% end -%> - -<% scope.lookupvar('ldap::params::index_base').each do |idx| -%> -<%= idx %> -<% end -%> - -<% @index_inc.each do |idx| -%> -<%= idx %> -<% end -%> - -# users can authenticate and change their password -access to dn.subtree="<%= @suffix %>" attrs=userPassword,shadowLastChange, -<% if @syncprov and @sync_binddn != false then -%> - by dn.exact="<%= @sync_binddn %>" read -<% end -%> - by self write - by anonymous auth - by * none - -#access to dn.subtree="<%= @suffix %>" attrs=sambaNTPassword,sambaLMPassword,sambaPwdMustChange,sambaBadPasswordCount,sambaBadPasswordTime,sambaPwdLastSet -#<% if @syncprov and @sync_binddn != false then -%> -# by dn.exact="<%= @sync_binddn %>" read -#<% end -%> -# by self write -# by anonymous auth -# by * none - -## some attributes need to be readable anonymously so that 'id user' can answer correctly -access to dn.subtree="<%= @suffix %>" attrs=objectClass,entry,gecos,homeDirectory,uid,uidNumber,gidNumber,cn,memberUid -<% if @syncprov and @sync_binddn != false then -%> - by dn.exact="<%= @sync_binddn %>" read -<% end -%> - by * read - -access to dn.subtree="<%= @suffix %>" -<% if @syncprov and @sync_binddn != false then -%> - by dn.exact="<%= @sync_binddn %>" read -<% end -%> - by self read - by * read - diff --git a/templates/etc/openldap/slapd.d/cn=config-update.ldif.erb b/templates/etc/openldap/slapd.d/cn=config-update.ldif.erb deleted file mode 100644 index 0d1e620..0000000 --- a/templates/etc/openldap/slapd.d/cn=config-update.ldif.erb +++ /dev/null @@ -1,16 +0,0 @@ -dn: cn=config -changetype: modify -<% @cnconfig_attrs.each_pair do |key, value| -%> -<% if value != '' and @cnconfig_default_attrs.include?(key) -%> -replace: <%= key %> -<%= key %>: <%= value %> -- -<% elsif @cnconfig_attrs.include?(key) and value == '' -%> -delete: <%= key %> -- -<% else -%> -add: <%= key %> -<%= key %>: <%= value %> -- -<% end -%> -<% end -%> diff --git a/templates/slapd.seed.erb b/templates/slapd.seed.erb new file mode 100644 index 0000000..8890d38 --- /dev/null +++ b/templates/slapd.seed.erb @@ -0,0 +1,20 @@ +slapd slapd/password1 password +slapd slapd/internal/adminpw password +slapd slapd/password2 password +slapd slapd/allow_ldap_v2 boolean false +slapd slapd/password_mismatch note +slapd slapd/suffix_change boolean false +slapd slapd/fix_directory boolean true +slapd slapd/invalid_config boolean true +slapd slapd/slave_databases_require_updateref note +slapd shared/organization string <%= @suffix %> +slapd slapd/upgrade_slapcat_failure note +slapd slapd/dump_database_destdir string /var/backups/slapd-VERSION +slapd slapd/autoconf_modules boolean true +slapd slapd/purge_database boolean false +slapd slapd/no_configuration boolean false +slapd slapd/migrate_ldbm_to_bdb boolean true +slapd slapd/move_old_database boolean true +slapd slapd/dump_database select when needed +slapd slapd/upgrade_slapadd_failure note +slapd slapd/domain string <%= @suffix.gsub(',dc=', '.').gsub('dc=', '') %> diff --git a/test/integration/master/serverspec/slapd_spec.rb b/test/integration/master/serverspec/slapd_spec.rb new file mode 100644 index 0000000..c677888 --- /dev/null +++ b/test/integration/master/serverspec/slapd_spec.rb @@ -0,0 +1,257 @@ +require 'serverspec' + +include Serverspec::Helper::Exec +include Serverspec::Helper::DetectOS + +RSpec.configure do |c| + c.before :all do + c.path = '/sbin:/usr/sbin' + end +end + +describe "slapd master" do + describe "server-wide concerns" do + describe service('slapd') do + it { should be_enabled } + it { should be_running } + end + + describe port(389) do + it { should be_listening } + end + + # Can bind as system root user + describe command('ldapwhoami -H ldapi:/// -Y EXTERNAL') do + it { should return_stdout /dn:gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth/ } + end + + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=schema,cn=config" "(objectClass=olcSchemaConfig)" cn') do + %w{inetorgperson cosine nis core ppolicy}.each do |schema| + it { should return_stdout /#{schema}/ } + end + end + + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(objectClass=olcModuleList)" olcModuleLoad') do + %w{back_hdb ppolicy}.each do |mod| + it { should return_stdout /#{mod}/ } + end + end + + # Syncprov + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(objectClass=olcModuleList)" olcModuleLoad') do + it { should return_stdout /syncprov/ } + end + + # TLS + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(cn=config)"') do + let(:cert_path) do + case os[:family] + when 'RedHat' + '/etc/openldap/certs' + when 'Debian', 'Ubuntu' + '/etc/ssl/certs' + end + end + it { should return_stdout %r{olcTLSCACertificateFile: #{cert_path}/ca\.pem} } + it { should return_stdout %r{olcTLSCertificateFile: #{cert_path}/master-ldap\.pem} } + it { should return_stdout %r{olcTLSCertificateKeyFile: #{cert_path}/master-ldap\.key} } + end + describe command('ldapwhoami -H ldaps:/// -x -D cn=admin,dc=foo,dc=bar -w password') do + it { should return_stdout /dn:cn=admin,dc=foo,dc=bar/ } + end + + # Setting arbitrary config options + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(cn=config)" olcConcurrency') do + it { should return_stdout %r{olcConcurrency: 1} } + end + + # Log level + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(cn=config)" olcLogLevel') do + it { should return_stdout %r{olcLogLevel: 4} } + end + + let(:run_dir) do + case property[:os_by_host]['localhost'][:family] + when /redhat/i + '/var/run/openldap' + else + '/var/run/slapd' + end + end + + # PID file + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(cn=config)" olcPidFile') do + it { should return_stdout %r{olcPidFile: #{run_dir}/slapd.pid} } + end + + # Args file + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(cn=config)" olcArgsFile') do + it { should return_stdout %r{olcArgsFile: #{run_dir}/slapd.args} } + end + + # Bind Anon + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(cn=config)" olcDisallows') do + its(:stdout) { should include 'olcDisallows: bind_anon' } + end + end + + describe 'primary database' do + # Can bind as specified root user w/ password + describe command('ldapwhoami -H ldapi:/// -x -D cn=admin,dc=foo,dc=bar -w password') do + it { should return_stdout /dn:cn=admin,dc=foo,dc=bar/ } + end + + # Can bind as a created user + describe command('ldapwhoami -H ldapi:/// -x -D uid=testuser,ou=users,dc=foo,dc=bar -w somepassword') do + it { should return_stdout /dn:uid=testuser,ou=users,dc=foo,dc=bar/ } + end + + # Requested suffix exists in cn=config + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=foo,dc=bar)" olcSuffix') do + it { should return_stdout /olcSuffix: dc=foo,dc=bar/ } + end + + # Once created, the root org is readable by system root + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -s base -b "dc=foo,dc=bar"') do + it { should return_stdout /o: Foo Dot Bar/ } + end + + # Once created, the root org is readable by the DIT root user + describe command('ldapsearch -H ldapi:/// -x -D cn=admin,dc=foo,dc=bar -w password -s base -b "dc=foo,dc=bar"') do + it { should return_stdout /o: Foo Dot Bar/ } + end + + # Directory can be manipulated by ldapdn resources + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -s base -b "ou=users,dc=foo,dc=bar" "(objectClass=organizationalUnit)"') do + it { should return_stdout /ou: users/ } + end + + # Last-modified overlay is on + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=foo,dc=bar)" olcLastMod') do + it { should return_stdout /olcLastMod: TRUE/ } + end + + # DB performance tweaks are set + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=foo,dc=bar)" olcDbCheckpoint') do + it { should return_stdout /512 30/ } + end + + # Indices (default and specified) + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=foo,dc=bar)" olcDbIndex') do + [ + 'olcDbIndex: objectClass \s*eq', + 'olcDbIndex: entryCSN \s*eq', + 'olcDbIndex: entryUUID \s*eq', + 'olcDbIndex: uidNumber \s*eq', + 'olcDbIndex: gidNumber \s*eq', + 'olcDbIndex: cn \s*pres,sub,eq', + 'olcDbIndex: sn \s*pres,sub,eq', + 'olcDbIndex: uid \s*pres,sub,eq', + 'olcDbIndex: displayName \s*pres,sub,eq', + 'olcDbIndex: title \s*pres', + ].each do |index| + it { should return_stdout /#{index}/ } + end + end + + # ACLs + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=foo,dc=bar)" olcAccess | perl -p00e \'s/\r?\n //g\'') do + [ + /to \* by dn.base="gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth" manage/, + /to dn.subtree="dc=foo,dc=bar" attrs=userPassword,shadowLastChange by dn.base="cn=sync,dc=foo,dc=bar" read by dn.base="gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth" write by self write by anonymous auth by \* none/, + /to dn.subtree="dc=foo,dc=bar" attrs=objectClass,entry,gecos,homeDirectory,uid,uidNumber,gidNumber,cn,memberUid by dn.base="cn=sync,dc=foo,dc=bar" read by dn.base="gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth" write by \* read/, + /to dn.subtree="dc=foo,dc=bar" by dn.base="cn=sync,dc=foo,dc=bar" read by dn.base="gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth" write by self read by \* read/, + ].each do |entry| + it { should return_stdout entry } + end + end + + # Syncprov + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(objectClass=olcSyncProvConfig)" olcSpCheckpoint') do + it { should return_stdout /100 10/ } + end + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(objectClass=olcSyncProvConfig)" olcSpSessionlog') do + it { should return_stdout /100/ } + end + end + + describe 'secondary database' do + # Can bind as specified root user w/ password + describe command('ldapwhoami -H ldapi:/// -x -D cn=admin,dc=doo,dc=dah -w otherpassword') do + it { should return_stdout /dn:cn=admin,dc=doo,dc=dah/ } + end + + # Can bind as a created user + describe command('ldapwhoami -H ldapi:/// -x -D uid=testuser,ou=users,dc=doo,dc=dah -w somepassword') do + it { should return_stdout /dn:uid=testuser,ou=users,dc=doo,dc=dah/ } + end + + # Requested suffix exists in cn=config + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=doo,dc=dah)" olcSuffix') do + it { should return_stdout /olcSuffix: dc=doo,dc=dah/ } + end + + # Once created, the root org is readable by system root + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -s base -b "dc=doo,dc=dah"') do + it { should return_stdout /o: Doo Dot Dah/ } + end + + # Once created, the root org is readable by the DIT root user + describe command('ldapsearch -H ldapi:/// -x -D cn=admin,dc=doo,dc=dah -w otherpassword -s base -b "dc=doo,dc=dah"') do + it { should return_stdout /o: Doo Dot Dah/ } + end + + # Directory can be manipulated by ldapdn resources + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -s base -b "ou=users,dc=doo,dc=dah" "(objectClass=organizationalUnit)"') do + it { should return_stdout /ou: users/ } + end + + # Last-modified overlay is on + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=doo,dc=dah)" olcLastMod') do + it { should return_stdout /olcLastMod: TRUE/ } + end + + # DB performance tweaks are set + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=doo,dc=dah)" olcDbCheckpoint') do + it { should return_stdout /512 30/ } + end + + # Indices (default and specified) + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=doo,dc=dah)" olcDbIndex') do + [ + 'olcDbIndex: objectClass \s*eq', + 'olcDbIndex: entryCSN \s*eq', + 'olcDbIndex: entryUUID \s*eq', + 'olcDbIndex: uidNumber \s*eq', + 'olcDbIndex: gidNumber \s*eq', + 'olcDbIndex: cn \s*pres,sub,eq', + 'olcDbIndex: sn \s*pres,sub,eq', + 'olcDbIndex: uid \s*pres,sub,eq', + 'olcDbIndex: displayName \s*pres,sub,eq', + 'olcDbIndex: title \s*pres', + ].each do |index| + it { should return_stdout /#{index}/ } + end + end + + # ACLs + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=doo,dc=dah)" olcAccess | perl -p00e \'s/\r?\n //g\'') do + [ + /to \* by dn.base="gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth" manage/, + /to dn.subtree="dc=doo,dc=dah" attrs=userPassword,shadowLastChange by dn.base="cn=sync,dc=doo,dc=dah" read by dn.base="gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth" write by self write by anonymous auth by \* none/, + /to dn.subtree="dc=doo,dc=dah" attrs=objectClass,entry,gecos,homeDirectory,uid,uidNumber,gidNumber,cn,memberUid by dn.base="cn=sync,dc=doo,dc=dah" read by dn.base="gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth" write by \* read/, + /to dn.subtree="dc=doo,dc=dah" by dn.base="cn=sync,dc=doo,dc=dah" read by dn.base="gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth" write by self read by \* read/, + ].each do |entry| + it { should return_stdout entry } + end + end + + # Syncprov + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(objectClass=olcSyncProvConfig)" olcSpCheckpoint') do + it { should return_stdout /100 10/ } + end + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(objectClass=olcSyncProvConfig)" olcSpSessionlog') do + it { should return_stdout /100/ } + end + end +end diff --git a/test/integration/slave/serverspec/slapd_spec.rb b/test/integration/slave/serverspec/slapd_spec.rb new file mode 100644 index 0000000..2f4babd --- /dev/null +++ b/test/integration/slave/serverspec/slapd_spec.rb @@ -0,0 +1,177 @@ +require 'serverspec' + +include Serverspec::Helper::Exec +include Serverspec::Helper::DetectOS + +RSpec.configure do |c| + c.before :all do + c.path = '/sbin:/usr/sbin' + end +end + +describe "slapd slave" do + describe service('slapd') do + it { should be_enabled } + it { should be_running } + end + + describe port(389) do + it { should be_listening } + end + + # Can bind as system root user + describe command('ldapwhoami -H ldapi:/// -Y EXTERNAL') do + it { should return_stdout /dn:gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth/ } + end + + # Can bind as specified root user w/ password + describe command('ldapwhoami -H ldapi:/// -x -D cn=admin,dc=foo,dc=bar -w password') do + it { should return_stdout /dn:cn=admin,dc=foo,dc=bar/ } + end + + # Requested suffix exists in cn=config + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(objectClass=olcDatabaseConfig)" olcSuffix') do + it { should return_stdout /olcSuffix: dc=foo,dc=bar/ } + end + + # The root organisation can be created + describe command("echo \"dn: dc=foo,dc=bar\nobjectClass: dcObject\nobjectClass: organization\ndc: foo\no: Foo Dot Bar\" | ldapadd -H ldapi:/// -Y EXTERNAL") do + it { should return_stdout /adding new entry/ } + end + + # Once created, the root org is readable by system root + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -s base -b "dc=foo,dc=bar"') do + it { should return_stdout /o: Foo Dot Bar/ } + end + + # Once created, the root org is readable by the DIT root user + describe command('ldapsearch -H ldapi:/// -x -D cn=admin,dc=foo,dc=bar -w password -s base -b "dc=foo,dc=bar"') do + it { should return_stdout /o: Foo Dot Bar/ } + end + + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=schema,cn=config" "(objectClass=olcSchemaConfig)" cn') do + %w{inetorgperson cosine nis core ppolicy}.each do |schema| + it { should return_stdout /#{schema}/ } + end + end + + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(objectClass=olcModuleList)" olcModuleLoad') do + %w{back_hdb ppolicy}.each do |mod| + it { should return_stdout /#{mod}/ } + end + end + + # Last-modified overlay is on + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(objectClass=olcDatabaseConfig)" olcLastMod') do + it { should return_stdout /olcLastMod: TRUE/ } + end + + # DB performance tweaks are set + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(objectClass=olcDatabaseConfig)" olcDbCheckpoint') do + it { should return_stdout /512 30/ } + end + + # Indices (default and specified) + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(objectClass=olcDatabaseConfig)" olcDbIndex') do + [ + 'olcDbIndex: objectClass \s*eq', + 'olcDbIndex: entryCSN \s*eq', + 'olcDbIndex: entryUUID \s*eq', + 'olcDbIndex: uidNumber \s*eq', + 'olcDbIndex: gidNumber \s*eq', + 'olcDbIndex: cn \s*pres,sub,eq', + 'olcDbIndex: sn \s*pres,sub,eq', + 'olcDbIndex: uid \s*pres,sub,eq', + 'olcDbIndex: displayName \s*pres,sub,eq', + 'olcDbIndex: title \s*pres', + ].each do |index| + it { should return_stdout /#{index}/ } + end + end + + # ACLs + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=foo,dc=bar)" olcAccess | perl -p00e \'s/\r?\n //g\'') do + [ + /to \* by dn.base="gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth" manage/, + /to dn.subtree="dc=foo,dc=bar" attrs=userPassword,shadowLastChange by dn.base="gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth" write by self write by anonymous auth by \* none/, + /to dn.subtree="dc=foo,dc=bar" attrs=objectClass,entry,gecos,homeDirectory,uid,uidNumber,gidNumber,cn,memberUid by dn.base="gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth" write by \* read/, + /to dn.subtree="dc=foo,dc=bar" by dn.base="gidNumber=0\+uidNumber=0,cn=peercred,cn=external,cn=auth" write by self read by \* read/, + ].each do |entry| + it { should return_stdout entry } + end + end + + # Syncprov + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" "(objectClass=olcModuleList)" olcModuleLoad') do + it { should return_stdout /syncprov/ } + end + + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=foo,dc=bar)" olcSyncrepl | perl -p00e \'s/\r?\n //g\'') do + its(:stdout) { should include 'rid=123 provider=ldapi:/// bindmethod=simple timeout=0 network-timeout=0 binddn="cn=sync,dc=foo,dc=bar" credentials="foobar" keepalive=0:0:0 starttls=no filter="(objectClass=*)" searchbase="dc=foo,dc=bar" scope=sub attrs="*" schemachecking=off type=refreshOnly interval=00:00:10:00 retry=undefined' } + end + + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=foo,dc=bar)" olcLimits | perl -p00e \'s/\r?\n //g\'') do + its(:stdout) { should include 'dn.exact="cn=sync,dc=foo,dc=bar" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited' } + end + + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(olcSuffix=dc=foo,dc=bar)" olcUpdateRef | perl -p00e \'s/\r?\n //g\'') do + its(:stdout) { should include 'ldapi:///' } + end + + # TLS + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(cn=config)"') do + let(:cert_path) do + case os[:family] + when 'RedHat' + '/etc/openldap/certs' + when 'Debian', 'Ubuntu' + '/etc/ssl/certs' + end + end + it { should return_stdout %r{olcTLSCACertificateFile: #{cert_path}/ca\.pem} } + it { should return_stdout %r{olcTLSCertificateFile: #{cert_path}/master-ldap\.pem} } + it { should return_stdout %r{olcTLSCertificateKeyFile: #{cert_path}/master-ldap\.key} } + end + describe command('ldapwhoami -H ldaps:/// -x -D cn=admin,dc=foo,dc=bar -w password') do + it { should return_stdout /dn:cn=admin,dc=foo,dc=bar/ } + end + + # Directory can be manipulated by ldapdn resources + describe command('ldapsearch -H ldapi:/// -Y EXTERNAL -s base -b "ou=users,dc=foo,dc=bar" "(objectClass=organizationalUnit)"') do + it { should return_stdout /ou: users/ } + end + + # Setting arbitrary config options + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(cn=config)" olcConcurrency') do + it { should return_stdout %r{olcConcurrency: 1} } + end + + # Log level + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(cn=config)" olcLogLevel') do + it { should return_stdout %r{olcLogLevel: 4} } + end + + let(:run_dir) do + case property[:os_by_host]['localhost'][:family] + when /redhat/i + '/var/run/openldap' + else + '/var/run/slapd' + end + end + + # PID file + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(cn=config)" olcPidFile') do + it { should return_stdout %r{olcPidFile: #{run_dir}/slapd.pid} } + end + + # Args file + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(cn=config)" olcArgsFile') do + it { should return_stdout %r{olcArgsFile: #{run_dir}/slapd.args} } + end + + # Bind Anon + describe command('ldapsearch -H ldapi:/// -LLL -Y EXTERNAL -b "cn=config" "(cn=config)" olcDisallows') do + its(:stdout) { should_not include 'olcDisallows: bind_anon' } + end +end