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 |