]> git.madduck.net Git - puppet/sudo.git/blob - manifests/init.pp

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

5e807d618677f26a87e6d42947e7a9ce2cb4f3f3
[puppet/sudo.git] / manifests / init.pp
1 class sudo (
2     Optional[String[1]] $sudogroup = undef,
3 ) {
4     $include_directory = '/etc/sudoers.d'
5     $file_defaults = {
6         owner        => "root",
7         group        => "root",
8         mode         => "0440",
9         validate_cmd => "sh -c 'if ! visudo --check --file=%; then cat %; exit 1; fi'",
10     }
11     $default_target = '00-defaults'
12
13     include sudo::install
14     include sudo::files
15     include sudo::defaults
16
17     define option (
18         Optional[String[1]] $parameter = undef,
19         Variant[String[1],Array[String[1]],Integer,Boolean] $value = true,
20         Enum['generic','host','user','cmnd','runas'] $context = 'generic',
21         Optional[Variant[Array[String[1]],String[1]]] $list = undef,
22         String[1] $target = $sudo::default_target,
23         Integer $order = 10,
24         Optional[String[1]] $comment = undef,
25         Boolean $newline_before = true,
26     ) {
27         $param = $parameter ? { undef => $name, default => $parameter }
28         $_list = type($list) ? { list => $list, default => [$list] }
29         sudo::internals::add_sudoers_fragment { "${name}":
30             target  => $target,
31             content => template("sudo/option_line.erb"),
32             order   => $order,
33             comment => $comment,
34         }
35     }
36
37     define alias (
38         Enum['host','user','cmnd','runas'] $type,
39         Optional[Variant[Array[String[1]],String[1]]] $list = undef,
40         String[1] $target = $sudo::default_target,
41         Integer $order = 10,
42         Optional[String[1]] $comment = undef,
43         Boolean $newline_before = true,
44     ) {
45         if $name !~ /^[[:upper:]][[:upper:]_[:digit:]]*$/ {
46             fail("sudoers alias definition '$name' can only contain uppercase letter, numbers, and the underscore")
47         }
48         $_list = type($list) ? { list => $list, default => [$list] }
49         sudo::internals::add_sudoers_fragment { "${name}":
50             target  => $target,
51             content => template("sudo/alias_line.erb"),
52             order   => $order,
53             comment => $comment,
54         }
55     }
56
57     define rule (
58         Variant[Array[String[1]],String[1]] $who,
59         Variant[Array[String[1]],String[1]] $where = 'ALL',
60         Optional[Variant[Array[String[1]],String[1]]] $as_whom = 'ALL',
61         Optional[Variant[Array[String[1]],String[1]]] $as_group = 'ALL',
62         Variant[Array[String[1]],String[1]] $what,
63         String[1] $target = $sudo::default_target,
64         Integer $order = 10,
65         Optional[String[1]] $comment = undef,
66         Boolean $newline_before = true,
67     )
68     {
69         $_comment = $comment ? { undef => $name, default => $comment }
70         sudo::internals::add_sudoers_fragment { "${name}":
71             target  => $target,
72             content => template("sudo/rule_line.erb"),
73             order   => $order,
74             comment => $comment,
75         }
76     }
77 }
78
79 class sudo::install {
80
81     package { "sudo":
82         ensure => latest,
83     }
84 }
85
86 class sudo::files (
87 ) {
88     $include_directory = $sudo::include_directory
89     file { default:
90              *              => $sudo::file_defaults
91            ;
92            "/etc/sudoers":
93                content      => template("sudo/sudoers.erb"),
94                validate_cmd => "visudo --check --strict --file=%",
95            ;
96            "$include_directory":
97                mode         => "0770",
98                recurse      => true,
99                purge        => true,
100                ignore       => ["*.local","*-local-*","local-*"],
101            ;
102            "$include_directory/README":
103                content      => @(EOT)
104                                # This directory is managed by Puppet
105                                #
106                                # Local files can be named any of:
107                                #   - local-*
108                                #   - *-local-*
109                                #   - *.local
110                                | EOT
111            ;
112     }
113 }
114
115 class sudo::defaults (
116     Optional[String[1]] $sudogroup = undef,
117     Boolean $root_may_sudo = true,
118     Optional[Hash] $generic = undef,
119     Optional[Hash] $user = undef,
120     Optional[Hash] $host = undef,
121     Optional[Hash] $runas = undef,
122     Optional[Hash] $cmnd = undef,
123 ) {
124     $netfacts = $facts[networking] ? { undef => $facts, default => $facts[networking] }
125     sudo::alias { "LOCALHOST":
126         type => host,
127         list => [ "localhost"
128                 , $netfacts[hostname]
129                 , $netfacts[fqdn]
130                 ],
131     }
132
133     if $sudogroup {
134         $sudogroup_target = "00-sudogroup"
135
136         group { "$sudogroup":
137             ensure => present,
138             system => true
139         }->
140         sudo::rule { "sudogroup":
141             who     => "%$sudogroup",
142             where   => "LOCALHOST",
143             require => Sudo::Alias["LOCALHOST"],
144             what    => "PASSWD: ALL",
145             target  => "$sudogroup_target",
146             comment => "Members of the ${sudogroup} group can use sudo (with password)",
147         }
148     }
149
150     if $root_may_sudo {
151         $rootsudo_target = "00-root_may_sudo"
152
153         sudo::option { "syslog":
154             value   => false,
155             context => user,
156             list    => "root",
157             target  => "$rootsudo_target",
158             comment => "No need to log root usage of sudo",
159         }->
160         sudo::rule { "root_may_sudo":
161             who     => "root",
162             where   => "LOCALHOST",
163             require => Sudo::Alias["LOCALHOST"],
164             what    => "NOPASSWD: ALL",
165             target  => "$rootsudo_target",
166             comment => "root may inadvertedly run sudo, so let them:",
167         }
168     }
169
170     if $generic {
171         concat::fragment { "sudo::defaults::generic comment":
172             target  => "sudoers_file_$sudo::default_target",
173             order   => 14,
174             content => "\n# Generated from the sudo::defaults::generic class parameter:\n",
175         }
176         $generic.each | $param, $value | {
177             sudo::option { "$param":
178                 value    => $value,
179                 order    => 15,
180                 newline_before => false,
181                 require  => Concat::Fragment["sudo::defaults::generic comment"],
182             }
183         }
184         concat::fragment { "sudo::defaults::generic end":
185             target  => "sudoers_file_$sudo::default_target",
186             order   => 16,
187             content => "# End sudo::defaults::generic class parameters\n",
188         }
189     }
190
191     $context_hash = {"user"=>$user,"host"=>$host,"runas"=>$runas,"cmnd"=>$cmnd}
192     $context_hash.keys.each | $index, $context | {
193         $defaults = $context_hash[$context]
194         if $defaults {
195             concat::fragment { "sudo::defaults::${context} comment":
196                 target  => "sudoers_$default_target",
197                 order   => 17 + $index * 3,
198                 content => "\n# Generated from the sudo::defaults::${context} class parameter:\n",
199             }
200             $defaults.each | $list, $items  | {
201                 $items.each | $param, $value | {
202                     sudo::option { "${context}_${list}_${param}":
203                         parameter => $param,
204                         context   => $context,
205                         list      => $list,
206                         value     => $value,
207                         order     => 18 + $index * 3,
208                         newline_before => false,
209                     }
210                 }
211             }
212             concat::fragment { "sudo::defaults::${context} end":
213                 target  => "sudoers_$default_target",
214                 order   => 19 + $index * 3,
215                 content => "# End sudo::defaults::${context} class parameters\n",
216             }
217         }
218     }
219 }
220
221 class sudo::internals {
222
223     define add_sudoers_fragment (
224         String[1] $target,
225         String[1] $content,
226         Integer $order,
227         Optional[String[1]] $comment = undef,
228     ) {
229         sudo::internals::ensure_sudoers_file { "${name}":
230             target  => $target
231         }
232         $ts = strftime("%s.%N")
233         # include the timestamp to preserve order in the output if execution
234         # is ordered
235         concat::fragment { "${ts}_sudoers_fragment_${target}_${name}":
236             target  => "sudoers_file_${target}",
237             content => $content,
238             order   => $order,
239         }
240     }
241     define ensure_sudoers_file(
242         String[1] $target,
243     ) {
244         ensure_resource('concat', "sudoers_file_${target}", {
245                 tag     => "${target}",
246                 path    => "${sudo::include_directory}/$target",
247                 warn    => "# THIS FILE IS MANAGED BY PUPPET; CHANGES WILL BE OVERWRITTEN\n",
248                 require => File[$sudo::include_directory],
249             } + $sudo::file_defaults,
250         )
251     }
252 }