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 |