+class sudo (
+ Optional[String[1]] $sudogroup = undef,
+) {
+ $include_directory = '/etc/sudoers.d'
+ $file_defaults = {
+ owner => "root",
+ group => "root",
+ mode => "0440",
+ validate_cmd => "sh -c 'if ! visudo --check --file=%; then cat %; exit 1; fi'",
+ }
+ $default_target = '00-defaults'
+
+ include sudo::install
+ include sudo::files
+ include sudo::defaults
+
+ define option (
+ Optional[String[1]] $parameter = undef,
+ Variant[String[1],Array[String[1]],Integer,Boolean] $value = true,
+ Enum['generic','host','user','cmnd','runas'] $context = 'generic',
+ Optional[Variant[Array[String[1]],String[1]]] $list = undef,
+ String[1] $target = $sudo::default_target,
+ Integer $order = 10,
+ Optional[String[1]] $comment = undef,
+ Boolean $newline_before = true,
+ ) {
+ $param = $parameter ? { undef => $name, default => $parameter }
+ $_list = type($list) ? { list => $list, default => [$list] }
+ sudo::internals::add_sudoers_fragment { "${name}":
+ target => $target,
+ content => template("sudo/option_line.erb"),
+ order => $order,
+ comment => $comment,
+ }
+ }
+
+ define alias (
+ Enum['host','user','cmnd','runas'] $type,
+ Optional[Variant[Array[String[1]],String[1]]] $list = undef,
+ String[1] $target = $sudo::default_target,
+ Integer $order = 10,
+ Optional[String[1]] $comment = undef,
+ Boolean $newline_before = true,
+ ) {
+ if $name !~ /^[[:upper:]][[:upper:]_[:digit:]]*$/ {
+ fail("sudoers alias definition '$name' can only contain uppercase letter, numbers, and the underscore")
+ }
+ $_list = type($list) ? { list => $list, default => [$list] }
+ sudo::internals::add_sudoers_fragment { "${name}":
+ target => $target,
+ content => template("sudo/alias_line.erb"),
+ order => $order,
+ comment => $comment,
+ }
+ }
+
+ define rule (
+ Variant[Array[String[1]],String[1]] $who,
+ Variant[Array[String[1]],String[1]] $where = 'ALL',
+ Optional[Variant[Array[String[1]],String[1]]] $as_whom = 'ALL',
+ Optional[Variant[Array[String[1]],String[1]]] $as_group = 'ALL',
+ Variant[Array[String[1]],String[1]] $what,
+ String[1] $target = $sudo::default_target,
+ Integer $order = 10,
+ Optional[String[1]] $comment = undef,
+ Boolean $newline_before = true,
+ )
+ {
+ $_comment = $comment ? { undef => $name, default => $comment }
+ sudo::internals::add_sudoers_fragment { "${name}":
+ target => $target,
+ content => template("sudo/rule_line.erb"),
+ order => $order,
+ comment => $comment,
+ }
+ }
+}
+
+class sudo::install {
+
+ package { "sudo":
+ ensure => latest,
+ }
+}
+
+class sudo::files (
+) {
+ $include_directory = $sudo::include_directory
+ file { default:
+ * => $sudo::file_defaults
+ ;
+ "/etc/sudoers":
+ content => template("sudo/sudoers.erb"),
+ validate_cmd => "visudo --check --strict --file=%",
+ ;
+ "$include_directory":
+ mode => "0770",
+ recurse => true,
+ purge => true,
+ ignore => ["*.local","*-local-*","local-*"],
+ ;
+ "$include_directory/README":
+ content => @(EOT)
+ # This directory is managed by Puppet
+ #
+ # Local files can be named any of:
+ # - local-*
+ # - *-local-*
+ # - *.local
+ | EOT
+ ;
+ }
+}
+
+class sudo::defaults (
+ Optional[String[1]] $sudogroup = undef,
+ Boolean $root_may_sudo = true,
+ Optional[Hash] $generic = undef,
+ Optional[Hash] $user = undef,
+ Optional[Hash] $host = undef,
+ Optional[Hash] $runas = undef,
+ Optional[Hash] $cmnd = undef,
+) {
+ $netfacts = $facts[networking] ? { undef => $facts, default => $facts[networking] }
+ sudo::alias { "LOCALHOST":
+ type => host,
+ list => [ "localhost"
+ , $netfacts[hostname]
+ , $netfacts[fqdn]
+ ],
+ }
+
+ if $sudogroup {
+ $sudogroup_target = "00-sudogroup"
+
+ group { "$sudogroup":
+ ensure => present,
+ system => true
+ }->
+ sudo::rule { "sudogroup":
+ who => "%$sudogroup",
+ where => "LOCALHOST",
+ require => Sudo::Alias["LOCALHOST"],
+ what => "PASSWD: ALL",
+ target => "$sudogroup_target",
+ comment => "Members of the ${sudogroup} group can use sudo (with password)",
+ }
+ }
+
+ if $root_may_sudo {
+ $rootsudo_target = "00-root_may_sudo"
+
+ sudo::option { "syslog":
+ value => false,
+ context => user,
+ list => "root",
+ target => "$rootsudo_target",
+ comment => "No need to log root usage of sudo",
+ }->
+ sudo::rule { "root_may_sudo":
+ who => "root",
+ where => "LOCALHOST",
+ require => Sudo::Alias["LOCALHOST"],
+ what => "NOPASSWD: ALL",
+ target => "$rootsudo_target",
+ comment => "root may inadvertedly run sudo, so let them:",
+ }
+ }
+
+ if $generic {
+ concat::fragment { "sudo::defaults::generic comment":
+ target => "sudoers_file_$sudo::default_target",
+ order => 14,
+ content => "\n# Generated from the sudo::defaults::generic class parameter:\n",
+ }
+ $generic.each | $param, $value | {
+ sudo::option { "$param":
+ value => $value,
+ order => 15,
+ newline_before => false,
+ require => Concat::Fragment["sudo::defaults::generic comment"],
+ }
+ }
+ concat::fragment { "sudo::defaults::generic end":
+ target => "sudoers_file_$sudo::default_target",
+ order => 16,
+ content => "# End sudo::defaults::generic class parameters\n",
+ }
+ }
+
+ $context_hash = {"user"=>$user,"host"=>$host,"runas"=>$runas,"cmnd"=>$cmnd}
+ $context_hash.keys.each | $index, $context | {
+ $defaults = $context_hash[$context]
+ if $defaults {
+ concat::fragment { "sudo::defaults::${context} comment":
+ target => "sudoers_$default_target",
+ order => 17 + $index * 3,
+ content => "\n# Generated from the sudo::defaults::${context} class parameter:\n",
+ }
+ $defaults.each | $list, $items | {
+ $items.each | $param, $value | {
+ sudo::option { "${context}_${list}_${param}":
+ parameter => $param,
+ context => $context,
+ list => $list,
+ value => $value,
+ order => 18 + $index * 3,
+ newline_before => false,
+ }
+ }
+ }
+ concat::fragment { "sudo::defaults::${context} end":
+ target => "sudoers_$default_target",
+ order => 19 + $index * 3,
+ content => "# End sudo::defaults::${context} class parameters\n",
+ }
+ }
+ }
+}
+
+class sudo::internals {
+
+ define add_sudoers_fragment (
+ String[1] $target,
+ String[1] $content,
+ Integer $order,
+ Optional[String[1]] $comment = undef,
+ ) {
+ sudo::internals::ensure_sudoers_file { "${name}":
+ target => $target
+ }
+ $ts = strftime("%s.%N")
+ # include the timestamp to preserve order in the output if execution
+ # is ordered
+ concat::fragment { "${ts}_sudoers_fragment_${target}_${name}":
+ target => "sudoers_file_${target}",
+ content => $content,
+ order => $order,
+ }
+ }
+ define ensure_sudoers_file(
+ String[1] $target,
+ ) {
+ ensure_resource('concat', "sudoers_file_${target}", {
+ tag => "${target}",
+ path => "${sudo::include_directory}/$target",
+ warn => "# THIS FILE IS MANAGED BY PUPPET; CHANGES WILL BE OVERWRITTEN\n",
+ require => File[$sudo::include_directory],
+ } + $sudo::file_defaults,
+ )
+ }
+}