Ldap Nodes

Version 5 (Darin Perusich, 01/06/2012 10:40 am)

1 1
# Storing Node Information in LDAP
2 1
3 1
By default, puppetmasterd looks for nodes in its normal manifests,
4 1
but you can additionally or instead have it look in LDAP. This
5 1
works especially well if you are already storing your host
6 1
information in LDAP.
7 1
8 1
I've only used [OpenLDAP](http://www.openldap.org) to do this, but
9 1
it should work just as well with
10 1
[Fedora Directory Server](http://directory.fedora.redhat.com/wiki/Features)
11 1
(confirmed working with Fedora Directory by Larry Ludwig) or
12 1
[Sun's Directory Server](http://www.sun.com/software/products/directory_srvr/home_directory.xml),
13 1
although you'll have to translate the schema to work with them.
14 1
15 1
This guide will go through what it takes to modify an existing
16 1
OpenLDAP setup; please check
17 1
[OpenLDAP's documentation](http://www.openldap.org/doc/admin/quickstart.html)
18 1
to get to that point.
19 1
20 1
NOTE: You can use node entries in your manifests together with LDAP
21 1
nodes. External or LDAP nodes will be used before node entries. You
22 1
cannot however use LDAP nodes and external nodes together. You must
23 1
use one of these two types.
24 1
25 1
# Why You'd Do This
26 1
27 1
There are multiple benefits to storing nodes in LDAP instead of
28 1
using Puppet's built-in node support:
29 1
30 1
-   Other applications can easily get access to the same data
31 1
-   All attributes on the LDAP nodes are assigned as variables in
32 1
    the Puppet configuration, just like Facts, so you can easily
33 1
    configure attributes for individual classes
34 1
-   It is straightforward to allow other applications to modify
35 1
    this data to configure nodes (e.g., as part of a deployment
36 1
    process), which is easier to support than generating Puppet
37 1
    configurations
38 1
39 1
# Prerequisites
40 1
41 1
-   [ruby-ldap](http://ruby-ldap.sourceforge.net)
42 1
43 1
# Pre-Puppet Ruby/LDAP Validation
44 1
45 1
You can run the following tests to make sure that the Ruby-LDAP
46 1
Library and your LDAP software are configured properly:
47 1
48 1
    ruby -rldap -e 'puts :installed'
49 1
50 1
If this returns installed then you can try:
51 1
52 1
    ruby -rpuppet -e 'p Puppet.features.ldap?'
53 1
54 1
These are basically doing the same thing, so they should either
55 1
both succeed or both fail, and if they both succeed, then LDAP
56 1
nodes should work.
57 1
58 1
# Node Attributes
59 1
60 1
As mentioned, every attribute returned by LDAP nodes or parent
61 1
nodes will be assigned as a variable in Puppet configurations
62 1
during compilation. Attributes with multiple values will be created
63 1
as arrays. PuppetVar attribute allows to pass variables to a node.
64 1
Keep in mind variables cannot have space in the name/value pair or
65 1
quotes. As an example, take the following simple LDAP nodes:
66 1
67 1
    dn: cn=basenode,ou=Hosts,dc=madstop,dc=com
68 1
    objectClass: device
69 1
    objectClass: ipHost
70 1
    objectClass: puppetClient
71 1
    objectClass: top
72 1
    cn: basenode
73 1
    environment: production
74 1
    ipHostNumber: 192.168.0.1
75 1
    description: The base node
76 1
    puppetClass: baseclass
77 1
    puppetVar: config_exim=true
78 1
    puppetVar: config_exim_trusted_users=lludwig,lak,joe
79 1
    
80 1
    
81 1
    dn: cn=testserver,ou=Hosts,dc=madstop,dc=com
82 1
    objectClass: device
83 1
    objectClass: ipHost
84 1
    objectClass: puppetClient
85 1
    objectClass: top
86 1
    cn: testserver
87 1
    environment: testing
88 1
    ipHostNumber: 192.168.0.50
89 1
    description: My test server
90 1
    l: dc1
91 1
    puppetClass: testing
92 1
    puppetClass: solaris
93 1
94 1
In this case, the final result for the node will be the following
95 1
(shown as YAML):
96 1
97 1
    :objectClass:
98 1
    - device
99 1
    - ipHost
100 1
    - puppetClient
101 1
    - top
102 1
    :cn: testserver
103 1
    :environment: testing
104 1
    :description: My test server
105 1
    :l: dc1
106 1
    :classes:
107 1
    - testing
108 1
    - solaris
109 1
    :dn: cn=testserver,ou=Hosts,dc=madstop,dc=com
110 1
111 1
For this node LDAP has assigned the node name, testserver, its
112 1
environment, testing (support for environments in LDAP is in
113 1
release 0.24.3 and later), a description and assigned a list of
114 1
classes. The class list will be testing, solaris, and baseclass;
115 1
note that the node's class list only has the individual classes
116 1
assigned to that node. The class list evaluated by Puppet will
117 1
include parent node classes too.
118 1
119 1
Lastly, any parameters assigned in LDAP, for example here
120 1
ipHostNumber, would be available as variables in your manifests.
121 1
Thus variable $ipHostNumber in the testserver node would have a
122 1
value of 192.168.0.50 assigned to it.
123 1
124 1
# Modifying your LDAP Schema
125 1
126 1
You first have to provide the Puppet schema to your LDAP server.
127 1
You can find the Puppet schema
128 4 James Turnbull
[in Git](http://github.com/puppetlabs/puppet/blob/master/ext/ldap/puppet.schema).
129 1
Place this schema into your schema directory, on Debian for example
130 1
this would be /etc/ldap/schema. I recommend keeping the
131 1
puppet.schema name.
132 1
133 1
With the schema file in place, modify your slapd.conf to load this
134 1
schema by adding it to the list of schema files loaded:
135 1
136 1
    include         /etc/ldap/schema/core.schema
137 1
    include         /etc/ldap/schema/cosine.schema
138 1
    include         /etc/ldap/schema/nis.schema
139 1
    include         /etc/ldap/schema/inetorgperson.schema
140 1
    include         /etc/ldap/schema/puppet.schema
141 1
    ...
142 1
143 1
Restart your server, making sure it comes back up, and you're all
144 1
set.
145 1
146 1
# Loading Nodes Into LDAP
147 1
148 1
In my opinion, the LDAP tool space is still depressingly spare. I
149 1
generally use my own [ldapsh](/projects/ldapsh) tool to manage
150 1
LDAP, but that does not work well for data loading. However you
151 1
decide to load the data, you need to create host entries (usually
152 1
device entries, probably with ipHost as an auxiliary class) and
153 1
then add the Puppet data. This is what my workstation definition
154 1
looks like in LDAP:
155 1
156 1
    dn: cn=culain,ou=Hosts,dc=madstop,dc=com
157 1
    objectClass: device
158 1
    objectClass: ipHost
159 1
    objectClass: puppetClient
160 1
    objectClass: top
161 1
    cn: culain
162 1
    environment: production
163 1
    ipHostNumber: 192.168.0.3
164 1
    puppetclass: webserver
165 1
    puppetclass: puppetserver
166 1
    puppetclass: mailserver
167 1
    parentnode: basenode
168 1
169 1
The DN I'm using for my host follows the model that I recommend for
170 1
all LDAP repositories. This will work well if you decide to start
171 1
using LDAP as an nsswitch source. It doesn't really matter to
172 1
Puppet, though; it just does a query against the search base you
173 1
specify, it doesn't try to guess your DN.
174 1
175 1
# Configuring Puppet to use LDAP
176 1
177 1
Once you have your data in LDAP, you just need to configure Puppet
178 1
to look there. It's pretty much always puppetmasterd that will be
179 1
looking in LDAP so we need to configure the [puppetmasterd] section
180 1
of the puppet.conf configuration file. In Puppet version 0.24 and
181 1
later this means selecting the appropriate node terminus, the
182 1
earlier ldapnodes option is fully deprecated and should not be
183 1
used. For LDAP nodes we use ldap as the terminus in the
184 1
node\_terminus configuration option:
185 1
186 1
    [puppetmasterd]
187 1
    node_terminus = ldap
188 1
    ldapserver = ldapserver.yourdomain.com
189 1
    ldapbase = dc=puppet
190 1
191 1
There is only one required settings: ldapbase for where to search
192 1
for LDAP nodes. You'll probably also want to specify ldapserver,
193 1
since the default is ldap, which likely won't work for most
194 1
people.
195 1
196 1
In other words, enable searching for nodes in LDAP by setting the
197 1
node\_terminus to ldap, and then provide the information necessary
198 1
to make it work. It's a good idea to actually specify the Hosts
199 1
tree as your search base (e.g., ldapbase =
200 1
ou=Hosts,dc=madstop,dc=com), but my database is small enough that
201 1
it doesn't matter.
202 1
203 1
With version 0.23.2 and later you should not need to restart the
204 1
puppetmasterd daemon but it's probably sensible.
205 1
206 1
## Configuring LDAP Nodes in pre-0.24 releases
207 1
208 1
In versions of Puppet prior to 0.24 configuring LDAP in Puppet used
209 1
the ldapnodes configuration option in the puppet.conf configuration
210 1
file.:
211 1
212 1
    [puppetmasterd]
213 1
    ldapnodes = true
214 1
    ldapserver = ldapserver.yourdomain.com
215 1
    ldapbase = dc=puppet
216 1
217 1
In the pre-0.24 versions there are two required settings: ldapnodes
218 1
to indicate you want to look for nodes in LDAP and ldapbase for
219 1
where to search for LDAP nodes. Like later versions you'll probably
220 1
also want to specify ldapserver, since the default is ldap, which
221 1
likely won't work for most people.
222 1
223 1
With earlier versions you'll also need to restart the daemon.
224 1
225 1
# Using Arrays with LDAP
226 1
227 1
By default the puppetVar LDAP attribute does not support arrays. In
228 1
order to support an array with puppetVar you must add these two
229 1
functions to your puppetmaster.
230 1
231 1
/usr/lib/ruby/site\_ruby/1.8/puppet/parser/functions/get\_var.rb:
232 1
233 1
    # Evaluate the value of a variable that might have been defined globally.
234 1
    module Puppet::Parser::Functions
235 1
      newfunction(:get_var, :type => :rvalue) do |args|
236 1
        var = args[0]
237 1
        global_var = lookupvar(var)
238 1
        if global_var != "" and global_var != nil
239 1
          case global_var
240 1
            when "true"
241 1
            return true
242 1
            when "false"
243 1
            return false
244 1
            else
245 1
            return global_var
246 1
          end
247 1
        end
248 1
        if args.length > 1
249 1
          return args[1]
250 1
        end
251 1
        return ""
252 1
      end
253 1
    end
254 1
255 1
/usr/lib/ruby/site\_ruby/1.8/puppet/parser/functions/split.rb:
256 1
257 1
    # Split a string variable into an array using the specified split
258 1
    # character.
259 1
    #
260 1
    # Usage:
261 1
    #
262 1
    #   $string    = 'value1,value2'
263 1
    #   $array_var = split($string, ',')
264 1
    #
265 1
    # $array_var holds the result ['value1', 'value2']
266 1
    #
267 1
    module Puppet::Parser::Functions
268 1
      newfunction(:split, :type => :rvalue) do |args|
269 1
        return args[0].split(args[1])
270 1
      end
271 1
    end
272 1
273 1
In your LDAP definition you can place:
274 1
275 1
    puppetVar: config_exim_trusted_users=lludwig,lak,joe
276 1
277 1
Then you must add this code to the top of a recipe before using
278 1
that variable:
279 1
280 1
    $config_exim_trusted_users = split(get_var('config_exim_trusted_users'), ',')
281 1
282 1
In this example the variable config\_exim\_trusted\_users gets
283 1
reformatted into a Puppet array.
284 1
285 1
# Default Nodes
286 1
287 1
Note that Puppet also supports default node definitions, named
288 1
(imaginatively) default. You can use this to provide a minimal
289 1
configuration for new nodes until you get around to configuring
290 1
each node. Without a default node configuration, unconfigured nodes
291 1
will fail.
292 3 Mohit Chawla
293 5 Darin Perusich
***Note***: If you use LDAP/external nodes then there must be the entry **`node default {}`** in **`site.pp`**. Without this entry your LDAP nodes will not be found.
294 3 Mohit Chawla
295 3 Mohit Chawla
    node default {}
296 3 Mohit Chawla
297 3 Mohit Chawla
And then a default node in LDAP or your external node source like:
298 3 Mohit Chawla
299 3 Mohit Chawla
    dn: cn=default,dc=orgName,dc=com
300 3 Mohit Chawla
    objectClass: device
301 3 Mohit Chawla
    objectClass: puppetClient
302 3 Mohit Chawla
    objectClass: top
303 3 Mohit Chawla
    cn: default