diff --git a/Gemfile b/Gemfile index 77480e1..1b21931 100644 --- a/Gemfile +++ b/Gemfile @@ -1,54 +1,55 @@ source ENV['GEM_SOURCE'] || 'https://rubygems.org' def location_for(place, fake_version = nil) if place =~ /^(git[:@][^#]*)#(.*)/ [fake_version, { git: $1, branch: $2, require: false }].compact elsif place =~ /^file:\/\/(.*)/ ['>= 0', { path: File.expand_path($1), require: false }] else [place, { require: false }] end end group :test do gem 'puppetlabs_spec_helper', require: false gem 'rspec-puppet', require: false, git: 'https://github.com/rodjek/rspec-puppet.git' gem 'rspec-puppet-facts', require: false gem 'rspec-puppet-utils', require: false gem 'puppet-lint-absolute_classname-check', require: false gem 'puppet-lint-leading_zero-check', require: false gem 'puppet-lint-trailing_comma-check', require: false gem 'puppet-lint-version_comparison-check', require: false gem 'puppet-lint-classes_and_types_beginning_with_digits-check', require: false gem 'puppet-lint-unquoted_string-check', require: false gem 'puppet-lint-variable_contains_upcase', require: false gem 'metadata-json-lint', require: false gem 'puppet-blacksmith', require: false gem 'voxpupuli-release', require: false, git: 'https://github.com/voxpupuli/voxpupuli-release-gem.git' gem 'rubocop-rspec', '~> 1.5', require: false if RUBY_VERSION >= '2.2.0' gem 'json_pure', '<= 2.0.1', require: false if RUBY_VERSION < '2.0.0' gem 'rspec-its', require: false end group :development do gem 'travis', require: false gem 'travis-lint', require: false gem 'guard-rake', require: false end group :system_tests do - gem 'serverspec', require: false - gem 'beaker-rspec', require: false + gem 'beaker', '~>2.51', :require => false + gem 'beaker-rspec', :require => false + gem 'serverspec', :require => false gem 'beaker-puppet_install_helper', require: false end if (facterversion = ENV['FACTER_GEM_VERSION']) gem 'facter', facterversion.to_s, require: false, groups: [:test] else gem 'facter', require: false, groups: [:test] end ENV['PUPPET_VERSION'].nil? ? puppetversion = '~> 4.0' : puppetversion = ENV['PUPPET_VERSION'].to_s gem 'puppet', puppetversion, require: false, groups: [:test] # vim: syntax=ruby diff --git a/README.md b/README.md index 31ae4fd..c864dec 100644 --- a/README.md +++ b/README.md @@ -1,198 +1,220 @@ # puppet-sudo [![Build Status](https://secure.travis-ci.org/saz/puppet-sudo.png)](http://travis-ci.org/saz/puppet-sudo) https://github.com/saz/puppet-sudo Manage sudo configuration via Puppet +### Supported OS +Some family and some specific os are supported by this module +* debian osfamily (debian, ubuntu, kali, ...) +* redhat osfamily (redhat, centos, fedora, ...) +* suse osfamily (suse, opensuse, ...) +* solaris osfamily (Solaris, OmniOS, SmartOS, ...) +* freebsd osfamily +* openbsd osfamily +* aix osfamily +* darwin osfamily +* gentoo operating system +* archlinux operating system +* amazon operating system + ### Gittip [![Support via Gittip](https://rawgithub.com/twolfson/gittip-badge/0.2.0/dist/gittip.png)](https://www.gittip.com/saz/) ## Usage ### WARNING **This module will purge your current sudo config** If this is not what you're expecting, set `purge` and/or `config_file_replace` to **false** ### Install sudo with default sudoers #### Purge current sudo config ```puppet class { 'sudo': } ``` #### Purge sudoers.d directory, but leave sudoers file as it is ```puppet class { 'sudo': config_file_replace => false, } ``` #### Leave current sudo config as it is ```puppet class { 'sudo': purge => false, config_file_replace => false, } ``` #### Use LDAP along with sudo Sudo do not always include by default the support for LDAP. On Debian and Ubuntu a special package sudo-ldap will be used. On Gentoo there is also the needing to include [puppet portage module by Gentoo](https://forge.puppetlabs.com/gentoo/portage). If not present, only a notification will be shown. ```puppet class { 'sudo': ldap_enable => true, } ``` ### Adding sudoers configuration #### Using Code ```puppet class { 'sudo': } sudo::conf { 'web': source => 'puppet:///files/etc/sudoers.d/web', } sudo::conf { 'admins': priority => 10, content => "%admins ALL=(ALL) NOPASSWD: ALL", } sudo::conf { 'joe': priority => 60, source => 'puppet:///files/etc/sudoers.d/users/joe', } ``` #### Using Hiera A hiera hash may be used to assemble the sudoers configuration. Hash merging is also enabled, which supports layering the configuration settings. Examples using: - YAML backend - an environment called __production__ - a __/etc/puppet/hiera.yaml__ hierarchy configuration: ```yaml :hierarchy: - "%{environment}" - "defaults" ``` ##### Load module ###### Using Puppet version 3+ Load the module via Puppet Code or your ENC. ```puppet include sudo ``` ###### Using Puppet version 2.7+ After [Installing Hiera](http://docs.puppetlabs.com/hiera/1/installing.html): - Load the `sudo` and `sudo::configs` modules via Puppet Code or your ENC. ```puppet include sudo include sudo::configs ``` ##### Configure Hiera YAML __(defaults.yaml)__ These defaults will apply to all systems. ```yaml sudo::configs: 'web': 'source' : 'puppet:///files/etc/sudoers.d/web' 'admins': 'content' : "%admins ALL=(ALL) NOPASSWD: ALL" 'priority' : 10 'joe': 'priority' : 60 'source' : 'puppet:///files/etc/sudoers.d/users/joe' ``` ##### Configure Hiera YAML __(production.yaml)__ This will only apply to the production environment. In this example we are: - inheriting/preserving the __web__ configuration - overriding the __admins__ configuration - removing the __joe__ configuration +- adding the __bill__ template ```yaml sudo::configs: 'admins': 'content' : "%prodadmins ALL=(ALL) NOPASSWD: ALL" 'priority' : 10 'joe': 'ensure' : 'absent' 'source' : 'puppet:///files/etc/sudoers.d/users/joe' + 'bill': + 'template' : "mymodule/bill.erb" ``` If you have Hiera version >= 1.2.0 and enable [Hiera Deeper Merging](http://docs.puppetlabs.com/hiera/1/lookup_types.html#deep-merging-in-hiera--120) you may conditionally override any setting. In this example we are: - inheriting/preserving the __web__ configuration - overriding the __admins:content__ setting - inheriting/preserving the __admins:priority__ setting - inheriting/preserving the __joe:source__ and __joe:priority__ settings - removing the __joe__ configuration +- adding the __bill__ template ```yaml sudo::configs: 'admins': 'content' : "%prodadmins ALL=(ALL) NOPASSWD: ALL" 'joe': 'ensure' : 'absent' + 'bill': + 'template' : "mymodule/bill.erb" ``` ##### Set a custom name for the sudoers file In some edge cases, the automatically generated sudoers file name is insufficient. For example, when an application generates a sudoers file with a fixed file name, using this class with the purge option enabled will always delete the custom file and adding it manually will generate a file with the right content, but the wrong name. To solve this, you can use the ```sudo_file_name``` option to manually set the desired file name. ```puppet sudo::conf { "foreman-proxy": ensure => "present", source => "puppet:///modules/sudo/foreman-proxy", sudo_file_name => "foreman-proxy", } ``` ### sudo::conf / sudo::configs notes -* You can pass template() through content parameter. * One of content or source must be set. +* Content may be an array, string will be added with return carriage after each element. +* In order to properly pass a template() use template instead of content, as hiera would run template function otherwise. ## sudo class parameters | Parameter | Type | Default | Description | | :-------------- | :------ |:----------- | :---------- | | enable | boolean | true | Set this to remove or purge all sudoers configs | | package | string | OS specific | Set package name _(for unsupported platforms)_ | | package_ensure | string | present | latest, absent, or a specific package version | | package_source | string | OS specific | Set package source _(for unsupported platforms)_ | | purge | boolean | true | Purge unmanaged files from config_dir | | purge_ignore | string | undef | Files excluded from purging in config_dir | | config_file | string | OS specific | Set config_file _(for unsupported platforms)_ | | config_file_replace | boolean | true | Replace config file with module config file | | config_dir | string | OS specific | Set config_dir _(for unsupported platforms)_ | | source | string | OS specific | Set source _(for unsupported platforms)_ | | ldap_enable | boolean | false | Add support to LDAP | ## sudo::conf class / sudo::configs hash parameters | Parameter | Type | Default | Description | | :-------------- | :----- |:----------- | :---------- | | ensure | string | present | present or absent | | priority | number | 10 | file name prefix | | content | string | undef | content of configuration snippet | | source | string | undef | source of configuration snippet | +| template | string | undef | template of configuration snippet | | sudo_config_dir | string | OS Specific | configuration snippet directory _(for unsupported platforms)_ | | sudo_file_name | string | undef | custom file name for sudo file in sudoers directory | diff --git a/manifests/conf.pp b/manifests/conf.pp index f532af3..e22cf0e 100644 --- a/manifests/conf.pp +++ b/manifests/conf.pp @@ -1,120 +1,123 @@ # Define: sudo::conf # # This module manages sudo configurations # # Parameters: # [*ensure*] # Ensure if present or absent. # Default: present # # [*priority*] # Prefix file name with $priority # Default: 10 # # [*content*] # Content of configuration snippet. # Default: undef # # [*source*] # Source of configuration snippet. # Default: undef # # [*sudo_config_dir*] # Where to place configuration snippets. # Only set this, if your platform is not supported or # you know, what you're doing. # Default: auto-set, platform specific # # Actions: # Installs sudo configuration snippets # # Requires: # Class sudo # # Sample Usage: # sudo::conf { 'admins': # source => 'puppet:///files/etc/sudoers.d/admins', # } # # [Remember: No empty lines between comments and class definition] define sudo::conf( $ensure = present, $priority = 10, $content = undef, $source = undef, + $template = undef, $sudo_config_dir = undef, $sudo_file_name = undef ) { include ::sudo # Hack to allow the user to set the config_dir from the # sudo::config parameter, but default to $sudo::params::config_dir # if it is not provided. $sudo::params isn't included before # the parameters are loaded in. $sudo_config_dir_real = $sudo_config_dir ? { undef => $sudo::config_dir, $sudo_config_dir => $sudo_config_dir } # sudo skip file name that contain a "." $dname = regsubst($name, '\.', '-', 'G') if size("x${priority}") == 2 { $priority_real = "0${priority}" } else { $priority_real = $priority } # build current file name with path if $sudo_file_name != undef { $cur_file = "${sudo_config_dir_real}${sudo_file_name}" } else { $cur_file = "${sudo_config_dir_real}${priority_real}_${dname}" } # replace whitespace in file name $cur_file_real = regsubst($cur_file, '\s+', '_', 'G') Class['sudo'] -> Sudo::Conf[$name] if $::osfamily == 'RedHat' { if (versioncmp($::sudoversion, '1.7.2p1') < 0) { warning("Found sudo with version ${::sudoversion}, but at least version 1.7.2p1 is required!") } } if $content != undef { if is_array($content) { $lines = join($content, "\n") $content_real = "${lines}\n" } else { $content_real = "# This file is managed by Puppet; changes may be overwritten\n${content}\n" } + } elsif $template != undef { + $content_real = template($template) } else { $content_real = undef } if $ensure == 'present' { $notify_real = Exec["sudo-syntax-check for file ${cur_file}"] } else { $notify_real = undef } file { "${priority_real}_${dname}": ensure => $ensure, path => $cur_file_real, owner => 'root', group => $sudo::params::config_file_group, mode => '0440', source => $source, content => $content_real, notify => $notify_real, } exec {"sudo-syntax-check for file ${cur_file}": command => "visudo -c -f '${cur_file_real}' || ( rm -f '${cur_file_real}' && exit 1)", refreshonly => true, path => ['/bin', '/sbin', '/usr/bin', '/usr/sbin'], } }