Puppet Augeas

Version 19 (Chris Phillips, 07/06/2011 07:44 am)

1 10 Andrei Pozolotin
{{toc}}
2 10 Andrei Pozolotin
3 1
# Using Puppet with Augeas
4 1
5 1
[Augeas](http://augeas.net/index.html) is a lovely tool that treats
6 1
config files (well, anything really, but it's mostly about config
7 1
files) as trees of values. You then modify the tree as you like,
8 1
and write the file back.
9 1
10 15 Rob McBroom
For example, the "PermitRootLogin" setting in `sshd_config` can be referenced like this:
11 15 Rob McBroom
12 15 Rob McBroom
    $ augtool print /files/etc/ssh/sshd_config/PermitRootLogin
13 15 Rob McBroom
    /files/etc/ssh/sshd_config/PermitRootLogin = "yes"
14 15 Rob McBroom
15 15 Rob McBroom
And it can be modified:
16 15 Rob McBroom
17 15 Rob McBroom
    $ augtool
18 15 Rob McBroom
    augtool> set /files/etc/ssh/sshd_config/PermitRootLogin no
19 15 Rob McBroom
    augtool> save
20 15 Rob McBroom
    Saved 1 file(s)
21 15 Rob McBroom
    $ grep PermitRootLogin /etc/ssh/sshd_config
22 15 Rob McBroom
    PermitRootLogin no
23 15 Rob McBroom
24 1
This is basically the solution to the problem of dealing with
25 1
upstream configuration changes combined with local modifications:
26 1
you can allow the upstream changes through and then apply changes
27 1
with Augeas to the new version.
28 1
29 1
Assuming you want to work with Augeas, this is a description of how
30 1
to perform Augeas changes using Puppet. You'll need Puppet >=
31 17 Nick Fagerlund
0.24.7 for this. The basic usage is in the [Type Reference](http://docs.puppetlabs.com/references/latest/type.html#augeas).
32 15 Rob McBroom
The command-line utility `augtool` will be used to demonstrate things here, but it is not required to use Augeas within Puppet. On most Linux distributions, `augtool` can be found in a separate package.
33 1
34 1
The somewhat more important, and unfortunately complicated, part is
35 1
figuring out what the tree for a file looks like so you can
36 1
manipulate it properly. The definition that Augeas uses to turn a
37 1
file into a tree is called a lens, and understanding the trees is
38 1
more difficult than it should be, because many lenses are not
39 1
documented sufficiently, or at all. The documentation for those
40 1
that are
41 1
[has its own surprisingly hard to find page](http://augeas.net/docs/lenses.html)
42 1
on the Augeas site. You can see what lenses are available by
43 15 Rob McBroom
looking in `/usr/share/augeas/lenses/` (or
44 15 Rob McBroom
`/usr/local/share/augeas/lenses/`, or possibly somewhere else,
45 1
depending on your setup).
46 1
47 15 Rob McBroom
You can see which files Augeas has successfully parsed by
48 15 Rob McBroom
running `augtool ls /files/` and drilling down from there. If a
49 15 Rob McBroom
file hasn't been properly parsed by Augeas, it simply won't
50 15 Rob McBroom
show up. This could mean that the file has a syntax error, the
51 15 Rob McBroom
file doesn't exist, you don't have permission to read the file,
52 15 Rob McBroom
or it could imply a failure in the lens itself.
53 1
54 15 Rob McBroom
The easiest way to understand how Augeas handles a particular file is to examine it using `augtool ls` and/or `augtool print`. In fact, you might want to make your changes by hand in a text editor, then use `augtool print` to give you an idea what the final result should look like.
55 1
56 15 Rob McBroom
Here's an example of how to determine the tree structure of a file, in this case `/etc/exports`. This is based on examples from from the bottom of `man 5 exports`:
57 1
58 1
    $ augtool
59 1
    augtool> ls /files/etc/exports/
60 1
    comment[1] = /etc/exports: the access control list for filesystems which may be exported
61 1
    comment[2] = to NFS clients.  See exports(5).
62 1
    comment[3] = sample /etc/exports file
63 1
    dir[1]/ = /
64 1
    dir[2]/ = /projects
65 1
    dir[3]/ = /usr
66 1
    dir[4]/ = /home/joe
67 1
68 1
From here you can investigate the structure, like so:
69 1
70 1
    augtool> ls /files/etc/exports/dir[1]
71 1
    client[1]/ = master
72 1
    client[2]/ = trusty
73 1
74 1
The corresponding line in the file is:
75 1
76 15 Rob McBroom
    /   master(rw) trusty(rw,no_root_squash)
77 1
78 1
Digging further:
79 1
80 1
    augtool> ls /files/etc/exports/dir[1]/client[1]
81 1
    option = rw
82 1
83 15 Rob McBroom
So, to add a new entry, you'd do something like this:
84 1
85 1
    augtool> set /files/etc/exports/dir[last()+1] /foo
86 1
    augtool> set /files/etc/exports/dir[last()]/client weeble
87 1
    augtool> set /files/etc/exports/dir[last()]/client/option[1] ro
88 1
    augtool> set /files/etc/exports/dir[last()]/client/option[2] all_squash
89 1
    augtool> save
90 1
    Saved 1 file(s)
91 1
92 1
Which creates the line:
93 1
94 1
    /foo weeble(ro,all_squash)
95 1
96 15 Rob McBroom
Now that we've seen some examples in `augtool`, let's make the same changes using Puppet.
97 1
98 15 Rob McBroom
Here's the `sshd_config` example:
99 15 Rob McBroom
100 15 Rob McBroom
    augeas { "sshd_config":
101 15 Rob McBroom
      changes => [
102 15 Rob McBroom
        "set /files/etc/ssh/sshd_config/PermitRootLogin no",
103 15 Rob McBroom
      ],
104 15 Rob McBroom
    }
105 15 Rob McBroom
106 15 Rob McBroom
The Augeas resource in Puppet has a useful attribute called "context" which allows you to specify a root for all of your changes. This is especially nice when making many changes to the same file. The above could also be written like this:
107 15 Rob McBroom
108 15 Rob McBroom
    augeas { "sshd_config":
109 15 Rob McBroom
      context => "/files/etc/ssh/sshd_config",
110 15 Rob McBroom
      changes => [
111 15 Rob McBroom
        "set PermitRootLogin no",
112 15 Rob McBroom
      ],
113 15 Rob McBroom
    }
114 15 Rob McBroom
115 15 Rob McBroom
Note that values containing whitespace need to be quoted. In this example, we use single-quotes since the `set` statement itself is already enclosed in double-quotes. You could also do the opposite.
116 15 Rob McBroom
117 15 Rob McBroom
    "set kernel.sem '500 512000 64 1024'",
118 15 Rob McBroom
119 15 Rob McBroom
Here's the `/etc/exports` example:
120 15 Rob McBroom
121 1
    augeas{ "export foo" :
122 1
        context => "/files/etc/exports",
123 1
        changes => [
124 1
            "set dir[last()+1] /foo",
125 1
            "set dir[last()]/client weeble",
126 1
            "set dir[last()]/client/option[1] ro",
127 1
            "set dir[last()]/client/option[2] all_squash",
128 1
        ],
129 1
    }
130 1
131 15 Rob McBroom
This adds the line as described above. In fact, it works too well. **It will add the line every time Puppet runs.** This is one of the biggest gotchas to using Augeas in Puppet. It's very easy to create resources that repeat changes on every run. (Technically, it will stop when you run out of space on that filesystem.) In almost every case, if you use a path containing `last()`, you will also need to use the Augeas resource's "onlyif" attribute to keep it under control.
132 1
133 15 Rob McBroom
In the above example, you could add something like this:
134 1
135 15 Rob McBroom
    onlyif => "match dir[. = '/foo'] size == 0",
136 1
137 15 Rob McBroom
Which essentially says "only add it if it's not already there". The problem with this approach is that it only considers one thing (the name of the export in this case). If you were to change something else like "client" later, it would never get applied because an entry named "/foo" is found. You can combine multiple tests into the "onlyif" attribute to look for changes in all the various components, but then you are practically defining the entire resource twice. There's a better way.
138 1
139 16 Rob McBroom
## A Better Way ##
140 16 Rob McBroom
141 15 Rob McBroom
In many cases, if you set a value for a non-existent path, Augeas will create the path for you. By using a more dynamic path to refer to things, you can make sure changes to any part of the tree will get applied, and you can do it without a messy "onlyif".
142 15 Rob McBroom
143 15 Rob McBroom
Since the share name is unique in `/etc/exports` you can use that to refer to a particular entry. In the example above, it would be `/files/etc/exports/dir[. = '/foo']`.
144 15 Rob McBroom
145 15 Rob McBroom
Here's an improved version of the example above:
146 15 Rob McBroom
147 15 Rob McBroom
    augeas{ "export foo" : 
148 15 Rob McBroom
        context => "/files/etc/exports",
149 15 Rob McBroom
        changes => [ 
150 15 Rob McBroom
            "set dir[. = '/foo'] /foo",
151 15 Rob McBroom
            "set dir[. = '/foo']/client weeble",
152 15 Rob McBroom
            "set dir[. = '/foo']/client/option[1] ro",
153 15 Rob McBroom
            "set dir[. = '/foo']/client/option[2] all_squash",
154 15 Rob McBroom
        ],
155 15 Rob McBroom
    } 
156 15 Rob McBroom
157 15 Rob McBroom
This has several advantages.
158 15 Rob McBroom
159 15 Rob McBroom
  * If the entry doesn't exist at all, it will get created.
160 15 Rob McBroom
  * If the entry already exists and you change any value, it will get updated.
161 1
  * The line won't be added to `/etc/exports` on every single Puppet run.
162 16 Rob McBroom
  * You don’t need to figure out an “onlyif” attribute to control Augeas because there is no “onlyif” attribute.
163 15 Rob McBroom
164 15 Rob McBroom
The next section examines this technique in detail.
165 15 Rob McBroom
166 15 Rob McBroom
## Paths for Numbered Items ##
167 15 Rob McBroom
168 15 Rob McBroom
Note: This is based on experience and trial & error, not some deep understanding of Augeas or studying of documentation, so terminology might not be 100% correct and there could be a better/simpler way, so always experiment on your own. You may want to look [this][pe] over, then come back here for some practical examples.
169 15 Rob McBroom
170 15 Rob McBroom
Changing things like `sshd_config` or Postfix's `main.cf` (where you have a series of simple key=value pairs) is pretty straightforward. Unfortunately, not every configuration item in a file has a unique "left hand side" to use in the path. In situations like this, Augeas will assign numbers to each item.
171 15 Rob McBroom
172 15 Rob McBroom
There are three ways things might get numbered. We'll show a working example for each (and some limitations).
173 15 Rob McBroom
174 15 Rob McBroom
1. The items themselves are numbers
175 15 Rob McBroom
176 15 Rob McBroom
        # augtool ls /files/etc/hosts
177 15 Rob McBroom
        1/ = (none)
178 15 Rob McBroom
        2/ = (none)
179 15 Rob McBroom
180 15 Rob McBroom
2. A simple array of items with the same name
181 15 Rob McBroom
182 15 Rob McBroom
        # augtool ls /files/etc/sudoers
183 15 Rob McBroom
        spec[1]/ = (none)
184 15 Rob McBroom
        spec[2]/ = (none)
185 15 Rob McBroom
186 15 Rob McBroom
3. An array of items with a value assigned
187 15 Rob McBroom
188 15 Rob McBroom
        # augtool ls /files/etc/exports
189 15 Rob McBroom
        dir[1]/ = /foo
190 15 Rob McBroom
        dir[2]/ = /bar
191 15 Rob McBroom
192 15 Rob McBroom
Starting with `/etc/hosts`, say you want to make sure the "localhost" entry also contains the system's current hostname and FQDN.
193 15 Rob McBroom
194 15 Rob McBroom
    augtool> ls /files/etc/hosts
195 15 Rob McBroom
    #comment[1] = Do not remove the following line, or various programs
196 15 Rob McBroom
    #comment[2] = that require network functionality will fail.
197 15 Rob McBroom
    1/ = (none)
198 15 Rob McBroom
    2/ = (none)
199 15 Rob McBroom
    3/ = (none)
200 15 Rob McBroom
    4/ = (none)
201 15 Rob McBroom
202 15 Rob McBroom
Which one of these is the line we want? It's most likely `/files/etc/hosts/1`, but you can't be sure. Fortunately, Augeas lets us identify a unique path using components of the item at that path. Here's the complete entry we want to change, referenced by number:
203 15 Rob McBroom
204 15 Rob McBroom
    augtool> print /files/etc/hosts/1
205 15 Rob McBroom
    /files/etc/hosts/1
206 15 Rob McBroom
    /files/etc/hosts/1/ipaddr = "127.0.0.1"
207 15 Rob McBroom
    /files/etc/hosts/1/canonical = "localhost"
208 15 Rob McBroom
    /files/etc/hosts/1/alias[1] = "webserver1"
209 15 Rob McBroom
    /files/etc/hosts/1/alias[2] = "webserver1.domain.com"
210 15 Rob McBroom
211 15 Rob McBroom
Here's the same entry, picked out by matching the "ipaddr" component:
212 15 Rob McBroom
213 15 Rob McBroom
    augtool> print /files/etc/hosts/*[ipaddr = '127.0.0.1']
214 15 Rob McBroom
    /files/etc/hosts/1
215 15 Rob McBroom
    /files/etc/hosts/1/ipaddr = "127.0.0.1"
216 15 Rob McBroom
    /files/etc/hosts/1/canonical = "localhost"
217 15 Rob McBroom
    /files/etc/hosts/1/alias[1] = "webserver1"
218 15 Rob McBroom
    /files/etc/hosts/1/alias[2] = "webserver1.domain.com"
219 15 Rob McBroom
220 15 Rob McBroom
This not only works for listing and printing, but for changing values as well.
221 15 Rob McBroom
222 15 Rob McBroom
    augtool> set /files/etc/hosts/*[ipaddr = '127.0.0.1']/alias[1] webserver2
223 15 Rob McBroom
224 15 Rob McBroom
So our Puppet manifest could maintain our desired "localhost" entry without knowing the order of the lines in the file:
225 15 Rob McBroom
226 15 Rob McBroom
    augeas { "localhost":
227 15 Rob McBroom
      context => "/files/etc/hosts",
228 15 Rob McBroom
      changes => [
229 15 Rob McBroom
        "set *[ipaddr = '127.0.0.1']/canonical localhost",
230 15 Rob McBroom
        "set *[ipaddr = '127.0.0.1']/alias[1] $hostname",
231 15 Rob McBroom
        "set *[ipaddr = '127.0.0.1']/alias[2] $hostname.domain.com",
232 15 Rob McBroom
      ],
233 15 Rob McBroom
    }
234 15 Rob McBroom
235 15 Rob McBroom
Note that the above example assumes a line for "127.0.0.1" is already defined.
236 15 Rob McBroom
237 15 Rob McBroom
With `sudoers`, you could add a simple entry by matching on the value for "user".
238 15 Rob McBroom
239 15 Rob McBroom
    augeas { "sudojoe":
240 15 Rob McBroom
      context => "/files/etc/sudoers",
241 15 Rob McBroom
      changes => [
242 15 Rob McBroom
        "set spec[user = 'joe']/user joe",
243 15 Rob McBroom
        "set spec[user = 'joe']/host_group/host ALL",
244 15 Rob McBroom
        "set spec[user = 'joe']/host_group/command ALL",
245 15 Rob McBroom
        "set spec[user = 'joe']/host_group/command/runas_user ALL",
246 15 Rob McBroom
      ],
247 15 Rob McBroom
    }
248 15 Rob McBroom
249 15 Rob McBroom
It might seem strange to set the value for "user" by matching "user", but it works. You just have to be sure "user" is defined first in this example so the rest of the values can be assigned. (This is a simplified example. Since the user alone doesn't necessarily uniquely identify an entry in `sudoers`, you should be careful matching on that alone.)
250 15 Rob McBroom
251 15 Rob McBroom
We've already seen how to handle an array of items with values, like `/etc/exports`, but just to be clear, when the item itself has a value, you can match it using ".".
252 15 Rob McBroom
253 15 Rob McBroom
    augtool> print /files/etc/exports/dir[. = '/foo']
254 15 Rob McBroom
255 15 Rob McBroom
Wherever possible, try to find a unique path that can be used to define entries and avoid "onlyif". If you get stuck, refer to the [Path Expressions][pe] section of the Augeas Wiki.
256 15 Rob McBroom
257 15 Rob McBroom
### Limitations ###
258 15 Rob McBroom
259 15 Rob McBroom
In some cases, (`/etc/sudoers`, `/etc/services`) there may not be a single item that makes an entry unique, but you can combine them to ensure you're targeting the right one. For most things in `/etc/services`, there are two lines (for TCP and UDP) with the same name and port.
260 15 Rob McBroom
261 15 Rob McBroom
    augtool> print /files/etc/services/service-name[. = 'ssh']
262 15 Rob McBroom
    /files/etc/services/service-name[23] = "ssh"
263 15 Rob McBroom
    /files/etc/services/service-name[23]/port = "22"
264 15 Rob McBroom
    /files/etc/services/service-name[23]/protocol = "tcp"
265 15 Rob McBroom
    /files/etc/services/service-name[23]/#comment = "SSH Remote Login Protocol"
266 15 Rob McBroom
    /files/etc/services/service-name[24] = "ssh"
267 15 Rob McBroom
    /files/etc/services/service-name[24]/port = "22"
268 15 Rob McBroom
    /files/etc/services/service-name[24]/protocol = "udp"
269 15 Rob McBroom
    /files/etc/services/service-name[24]/#comment = "SSH Remote Login Protocol"
270 15 Rob McBroom
    
271 15 Rob McBroom
    augtool> print /files/etc/services/service-name[port = '22']
272 15 Rob McBroom
    (same output as above)
273 15 Rob McBroom
274 15 Rob McBroom
But we can target just one line by using multiple components:
275 15 Rob McBroom
276 15 Rob McBroom
    augtool> print /files/etc/services/service-name[port = '22'][protocol = 'tcp']
277 15 Rob McBroom
    /files/etc/services/service-name[23] = "ssh"
278 15 Rob McBroom
    /files/etc/services/service-name[23]/port = "22"
279 15 Rob McBroom
    /files/etc/services/service-name[23]/protocol = "tcp"
280 15 Rob McBroom
    /files/etc/services/service-name[23]/#comment = "SSH Remote Login Protocol"
281 15 Rob McBroom
282 15 Rob McBroom
You can use complicated paths like this to modify existing values, but unfortunately, they can't be used to create new entries. Remember, with `sudoers` we had to set "user" first in order to match on it in subsequent lines. To match on two values they both need to be set, and you can't set them at the same time, which is why this only works for things that already exist.
283 15 Rob McBroom
284 18 Patrick Mohr
In cases like this, you will need to revert back to using a combination of `last()` and "onlyif", until http://projects.puppetlabs.com/issues/6494 is released.  At that point you can create the whole tree and them move it into place.
285 15 Rob McBroom
286 15 Rob McBroom
[pe]: http://augeas.net/page/Path_expressions
287 15 Rob McBroom
288 7 Andrei Pozolotin
# Working Examples
289 7 Andrei Pozolotin
290 15 Rob McBroom
You can test a manifest containing and Augeas resource like this:
291 7 Andrei Pozolotin
292 7 Andrei Pozolotin
    sudo puppet apply --verbose --debug --trace --summarize test.pp
293 7 Andrei Pozolotin
294 7 Andrei Pozolotin
## /etc/sysctl.conf
295 7 Andrei Pozolotin
296 7 Andrei Pozolotin
works on puppet 2.6.1; fedora 12; ubuntu 10.04
297 7 Andrei Pozolotin
    
298 7 Andrei Pozolotin
    class sysctl {
299 7 Andrei Pozolotin
    
300 7 Andrei Pozolotin
      # nested class/define
301 7 Andrei Pozolotin
      define conf ( $value ) {
302 7 Andrei Pozolotin
        
303 7 Andrei Pozolotin
        # $name is provided by define invocation
304 7 Andrei Pozolotin
        
305 7 Andrei Pozolotin
        # guid of this entry
306 7 Andrei Pozolotin
        $key = $name
307 7 Andrei Pozolotin
        
308 7 Andrei Pozolotin
        $context = "/files/etc/sysctl.conf"
309 7 Andrei Pozolotin
        
310 7 Andrei Pozolotin
         augeas { "sysctl_conf/$key":
311 7 Andrei Pozolotin
           context => "$context",
312 19 Chris Phillips
           onlyif  => "get $key != '$value'",
313 19 Chris Phillips
           changes => "set $key '$value'",
314 8 Andrei Pozolotin
           notify  => Exec["sysctl"],
315 8 Andrei Pozolotin
         }
316 8 Andrei Pozolotin
      
317 8 Andrei Pozolotin
      } 
318 8 Andrei Pozolotin
    
319 8 Andrei Pozolotin
       file { "sysctl_conf":
320 8 Andrei Pozolotin
          name => $operatingsystem ? {
321 8 Andrei Pozolotin
            default => "/etc/sysctl.conf",
322 8 Andrei Pozolotin
          },
323 8 Andrei Pozolotin
       }
324 8 Andrei Pozolotin
    
325 11 Rob McBroom
       exec { "sysctl -p":
326 11 Rob McBroom
          alias => "sysctl",
327 11 Rob McBroom
          refreshonly => true,
328 11 Rob McBroom
          subscribe => File["sysctl_conf"],
329 11 Rob McBroom
       }
330 11 Rob McBroom
    
331 11 Rob McBroom
    }
332 11 Rob McBroom
333 11 Rob McBroom
use case:
334 11 Rob McBroom
335 11 Rob McBroom
    include sysctl
336 11 Rob McBroom
337 11 Rob McBroom
    sysctl::conf { 
338 11 Rob McBroom
      
339 11 Rob McBroom
      # prevent java heap swap
340 11 Rob McBroom
      "vm.swappiness": value =>  0;
341 11 Rob McBroom
      
342 11 Rob McBroom
      # increase max read/write buffer size that can be applied via setsockopt()
343 11 Rob McBroom
      "net.core.rmem_max": value =>  16777216;
344 11 Rob McBroom
      "net.core.wmem_max": value =>  16777216;
345 11 Rob McBroom
      
346 11 Rob McBroom
    }
347 11 Rob McBroom
348 11 Rob McBroom
349 11 Rob McBroom
## /etc/security/limits.conf
350 11 Rob McBroom
351 11 Rob McBroom
works on puppet 2.6.1; fedora 12; ubuntu 10.04
352 11 Rob McBroom
    
353 11 Rob McBroom
    class limits {
354 11 Rob McBroom
    
355 11 Rob McBroom
      # nested class/define
356 11 Rob McBroom
      define conf (
357 11 Rob McBroom
        $domain = "root",
358 11 Rob McBroom
        $type = "soft",
359 11 Rob McBroom
        $item = "nofile",
360 11 Rob McBroom
        $value = "10000"
361 11 Rob McBroom
        ) {
362 11 Rob McBroom
        
363 11 Rob McBroom
          # guid of this entry
364 11 Rob McBroom
          $key = "$domain/$type/$item"
365 11 Rob McBroom
          
366 11 Rob McBroom
          # augtool> match /files/etc/security/limits.conf/domain[.="root"][./type="hard" and ./item="nofile" and ./value="10000"]
367 11 Rob McBroom
    
368 11 Rob McBroom
          $context = "/files/etc/security/limits.conf"
369 11 Rob McBroom
     
370 11 Rob McBroom
          $path_list  = "domain[.=\"$domain\"][./type=\"$type\" and ./item=\"$item\"]"
371 11 Rob McBroom
          $path_exact = "domain[.=\"$domain\"][./type=\"$type\" and ./item=\"$item\" and ./value=\"$value\"]"
372 11 Rob McBroom
        
373 11 Rob McBroom
          # TODO add duplicate entry cleanup
374 11 Rob McBroom
          
375 11 Rob McBroom
          augeas { "limits_conf/$key":
376 11 Rob McBroom
             context => "$context",
377 11 Rob McBroom
             onlyif  => "match $path_exact size==0",
378 11 Rob McBroom
             changes => [
379 11 Rob McBroom
               # remove all matching to the $domain, $type, $item, for any $value
380 11 Rob McBroom
               "rm $path_list", 
381 11 Rob McBroom
               # insert new node at the end of tree
382 11 Rob McBroom
               "set domain[last()+1] $domain",
383 11 Rob McBroom
               # assign values to the new node
384 11 Rob McBroom
               "set domain[last()]/type $type",
385 1
               "set domain[last()]/item $item",
386 11 Rob McBroom
               "set domain[last()]/value $value",
387 11 Rob McBroom
             ],
388 11 Rob McBroom
           }
389 11 Rob McBroom
      
390 11 Rob McBroom
      } 
391 11 Rob McBroom
    
392 11 Rob McBroom
    }
393 11 Rob McBroom
394 11 Rob McBroom
use case:
395 11 Rob McBroom
396 11 Rob McBroom
    include limits
397 11 Rob McBroom
398 11 Rob McBroom
    limits::conf { 
399 11 Rob McBroom
      
400 11 Rob McBroom
      # maximum number of open files/sockets for root
401 11 Rob McBroom
      "root-soft": domain => root, type => soft, item => nofile, value =>  9999;
402 11 Rob McBroom
      "root-hard": domain => root, type => hard, item => nofile, value =>  9999;
403 11 Rob McBroom
      
404 11 Rob McBroom
    }
405 11 Rob McBroom
406 15 Rob McBroom
## sshd_config ##
407 11 Rob McBroom
408 11 Rob McBroom
Configure `sshd`:
409 11 Rob McBroom
410 1
    augeas { "sshd_config":
411 1
      context => "/files/etc/ssh/sshd_config",
412 11 Rob McBroom
      changes => [
413 11 Rob McBroom
        # track which key was used to logged in
414 11 Rob McBroom
        "set LogLevel VERBOSE",
415 11 Rob McBroom
        # permit root logins only using publickey
416 11 Rob McBroom
        "set PermitRootLogin without-password",
417 11 Rob McBroom
      ],
418 11 Rob McBroom
      notify => Service["sshd"],
419 11 Rob McBroom
    }
420 11 Rob McBroom
    
421 1
    service { "sshd":
422 1
      name => $operatingsystem ? {
423 11 Rob McBroom
        Debian => "ssh",
424 11 Rob McBroom
        default => "sshd",
425 11 Rob McBroom
      },
426 11 Rob McBroom
      require => Augeas["sshd_config"],
427 11 Rob McBroom
      enable => true,
428 11 Rob McBroom
      ensure => running,
429 11 Rob McBroom
    }
430 11 Rob McBroom
431 15 Rob McBroom
## Default Runlevel ##
432 15 Rob McBroom
433 11 Rob McBroom
Set the default runlevel to 3:
434 11 Rob McBroom
435 1
    augeas { "runlevel":
436 11 Rob McBroom
      context => "/files/etc/inittab",
437 11 Rob McBroom
      changes => [
438 11 Rob McBroom
        "set id/runlevels 3",
439 11 Rob McBroom
      ],
440 11 Rob McBroom
    }
441 11 Rob McBroom
442 15 Rob McBroom
## Kernel Boot Options ##
443 15 Rob McBroom
444 11 Rob McBroom
Suppose a vendor (like VMWare) has some recommended kernel parameters that you want to apply. Assuming you have a class that only applied to VMWare guests, you could do the following.
445 11 Rob McBroom
446 11 Rob McBroom
    # improve time keeping for VMs
447 11 Rob McBroom
    case $hardwareisa {
448 11 Rob McBroom
      "i386": {
449 11 Rob McBroom
        # not going to worry about these
450 11 Rob McBroom
      }
451 11 Rob McBroom
      default: {
452 12 Don Harden
        augeas { "vmtime":
453 11 Rob McBroom
          context => "/files/etc/grub.conf",
454 11 Rob McBroom
          changes => [
455 11 Rob McBroom
            "set title[1]/kernel/divider 10",
456 15 Rob McBroom
            # clear sets the left hand side but assigns no value
457 12 Don Harden
            "clear title[1]/kernel/notsc",
458 12 Don Harden
          ],
459 12 Don Harden
        }
460 12 Don Harden
      }
461 12 Don Harden
    }
462 12 Don Harden
463 12 Don Harden
## Adding a service that uses both TCP and UDP to /etc/services ##
464 12 Don Harden
465 12 Don Harden
Augeas needs a unique identifier when using set. So the trick to adding a service that uses both TCP and UDP is to use child nodes of the service.  For example, to add zabbix-agent you can use this (note the [2] for the second zabbix-agent):
466 12 Don Harden
467 12 Don Harden
    augeas { "zabbix-agent":
468 12 Don Harden
       context =>  "/files/etc/services",
469 12 Don Harden
       changes => [
470 12 Don Harden
          "ins service-name after service-name[last()]",
471 12 Don Harden
          "set service-name[last()] zabbix-agent",
472 12 Don Harden
          "set service-name[. = 'zabbix-agent']/port 10050",
473 12 Don Harden
          "set service-name[. = 'zabbix-agent']/protocol tcp",
474 12 Don Harden
          "ins service-name after /files/etc/services/service-name[last()]",
475 12 Don Harden
          "set service-name[last()] zabbix-agent",
476 12 Don Harden
          "set service-name[. = 'zabbix-agent'][2]/port 10050",
477 12 Don Harden
          "set service-name[. = 'zabbix-agent'][2]/protocol udp",
478 1
       ],  
479 1
       onlyif => "match service-name[port = '10050'] size == 0",
480 1
    }
481 1
482 15 Rob McBroom
Works on CentOS 5.5 with Puppet 2.6 and Augeas 7.2-2