summaryrefslogtreecommitdiff
path: root/lib/chef/resource/sudo.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef/resource/sudo.rb')
-rw-r--r--lib/chef/resource/sudo.rb222
1 files changed, 222 insertions, 0 deletions
diff --git a/lib/chef/resource/sudo.rb b/lib/chef/resource/sudo.rb
new file mode 100644
index 0000000000..202e904a35
--- /dev/null
+++ b/lib/chef/resource/sudo.rb
@@ -0,0 +1,222 @@
+#
+# Author:: Bryan W. Berry (<bryan.berry@gmail.com>)
+# Author:: Seth Vargo (<sethvargo@gmail.com>)
+#
+# Copyright:: 2011-2018, Bryan w. Berry
+# Copyright:: 2012-2018, Seth Vargo
+# Copyright:: 2015-2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class Sudo < Chef::Resource
+ resource_name "sudo"
+ provides "sudo"
+
+ description "Use the sudo resource to add or remove individual sudo entries using sudoers.d files."\
+ " Sudo version 1.7.2 or newer is required to use the sudo resource as it relies on the"\
+ " '#includedir' directive introduced in version 1.7.2. The resource does not enforce"\
+ " installing the version. Supported releases of Ubuntu, Debian and RHEL (6+) all support"\
+ " this feature."
+
+ introduced "14.0"
+
+ # acording to the sudo man pages sudo will ignore files in an include dir that have a `.` or `~`
+ # We convert either to `__`
+ property :filename, String,
+ description: "The name of the sudoers.d file",
+ name_property: true,
+ coerce: proc { |x| x.gsub(/[\.~]/, "__") }
+
+ property :users, [String, Array],
+ description: "User(s) to provide sudo privileges to. This accepts either an array or a comma separated.",
+ default: [],
+ coerce: proc { |x| x.is_a?(Array) ? x : x.split(/\s*,\s*/) }
+
+ property :groups, [String, Array],
+ description: "Group(s) to provide sudo privileges to. This accepts either an array or a comma separated list. Leading % on group names is optional.",
+ default: [],
+ coerce: proc { |x| coerce_groups(x) }
+
+ property :commands, Array,
+ description: "An array of commands this sudoer can execute.",
+ default: ["ALL"]
+
+ property :host, String,
+ description: "The host to set in the sudo config.",
+ default: "ALL"
+
+ property :runas, String,
+ description: "User the command(s) can be run as",
+ default: "ALL"
+
+ property :nopasswd, [TrueClass, FalseClass],
+ description: "Allow running sudo without specifying a password sudo",
+ default: false
+
+ property :noexec, [TrueClass, FalseClass],
+ description: "Prevent commands from shelling out.",
+ default: false
+
+ property :template, String,
+ description: "The name of the erb template in your cookbook if you wish to supply your own template."
+
+ property :variables, [Hash, nil],
+ description: "The variables to pass to the custom template. Ignored if not using a custom template.",
+ default: nil
+
+ property :defaults, Array,
+ description: "An array of defaults for the user/group.",
+ default: []
+
+ property :command_aliases, Array,
+ description: "Command aliases that can be used as allowed commands later in the config",
+ default: []
+
+ property :setenv, [TrueClass, FalseClass],
+ description: "Whether to permit the preserving of environment with sudo -E.",
+ default: false
+
+ property :env_keep_add, Array,
+ description: "An array of strings to add to env_keep.",
+ default: []
+
+ property :env_keep_subtract, Array,
+ description: "An array of strings to remove from env_keep.",
+ default: []
+
+ property :visudo_path, String,
+ description: "The path to visudo for config verification.",
+ default: "/usr/sbin/visudo"
+
+ property :config_prefix, String,
+ description: "The directory containing the sudoers config file.",
+ default: lazy { "config_prefix" }
+
+ alias_method :user, :users
+ alias_method :group, :groups
+
+ # make sure each group starts with a %
+ def coerce_groups(x)
+ # split strings on the commas with optional spaces on either side
+ groups = x.is_a?(Array) ? x : x.split(/\s*,\s*/)
+
+ # make sure all the groups start with %
+ groups.map { |g| g[0] == "%" ? g : "%#{g}" }
+ end
+
+ # default config prefix paths based on platform
+ def config_prefix
+ case node["platform_family"]
+ when "smartos"
+ "/opt/local/etc"
+ when "mac_os_x"
+ "/private/etc"
+ else
+ "/etc"
+ end
+ end
+
+ action :create do
+ description "Create a single sudoers config in the sudoers.d directory"
+
+ validate_platform
+ validate_properties
+
+ if docker? # don't even put this into resource collection unless we're in docker
+ declare_resource(:package, "sudo") do
+ action :nothing
+ not_if "which sudo"
+ end.run_action(:install)
+ end
+
+ target = "#{new_resource.config_prefix}/sudoers.d/"
+ declare_resource(:directory, target) unless ::File.exist?(target)
+
+ Chef::Log.warn("#{new_resource.filename} will be rendered, but will not take effect because the #{new_resource.config_prefix}/sudoers config lacks the includedir directive that loads configs from #{new_resource.config_prefix}/sudoers.d/!") if ::File.readlines("#{new_resource.config_prefix}/sudoers").grep(/includedir/).empty?
+
+ if new_resource.template
+ Chef::Log.debug("Template property provided, all other properties ignored.")
+
+ declare_resource(:template, "#{target}#{new_resource.filename}") do
+ source new_resource.template
+ mode "0440"
+ variables new_resource.variables
+ verify "#{new_resource.visudo_path} -cf %{path}" if visudo_present?
+ action :create
+ end
+ else
+ declare_resource(:template, "#{target}#{new_resource.filename}") do
+ source "sudoer.erb"
+ source ::File.expand_path("../support/sudoer.erb", __FILE__)
+ local true
+ mode "0440"
+ variables sudoer: (new_resource.groups + new_resource.users).join(","),
+ host: new_resource.host,
+ runas: new_resource.runas,
+ nopasswd: new_resource.nopasswd,
+ noexec: new_resource.noexec,
+ commands: new_resource.commands,
+ command_aliases: new_resource.command_aliases,
+ defaults: new_resource.defaults,
+ setenv: new_resource.setenv,
+ env_keep_add: new_resource.env_keep_add,
+ env_keep_subtract: new_resource.env_keep_subtract
+ verify "#{new_resource.visudo_path} -cf %{path}" if visudo_present?
+ action :create
+ end
+ end
+ end
+
+ action :install do
+ Chef::Log.warn("The sudo :install action has been renamed :create. Please update your cookbook code for the new action")
+ action_create
+ end
+
+ # Removes a user from the sudoers group
+ action :delete do
+ description "Remove a sudoers config from the sudoers.d directory"
+
+ file "#{new_resource.config_prefix}/sudoers.d/#{new_resource.filename}" do
+ action :delete
+ end
+ end
+
+ action_class do
+ # Make sure we fail on FreeBSD
+ def validate_platform
+ return unless platform_family?("freebsd")
+ raise "The sudo resource cannot run on FreeBSD as FreeBSD does not support using a sudoers.d config directory."
+ end
+
+ # Ensure that the inputs are valid (we cannot just use the resource for this)
+ def validate_properties
+ # if group, user, env_keep_add, env_keep_subtract and template are nil, throw an exception
+ raise "You must specify users, groups, env_keep_add, env_keep_subtract, or template properties!" if new_resource.users.empty? && new_resource.groups.empty? && new_resource.template.nil? && new_resource.env_keep_add.empty? && new_resource.env_keep_subtract.empty?
+
+ # if specifying user or group and template at the same time fail
+ raise "You cannot specify users or groups properties and also specify a template. To use your own template pass in all template variables using the variables property." if (!new_resource.users.empty? || !new_resource.groups.empty?) && !new_resource.template.nil?
+ end
+
+ def visudo_present?
+ return if ::File.exist?(new_resource.visudo_path)
+ Chef::Log.warn("The visudo binary cannot be found at '#{new_resource.visudo_path}'. Skipping sudoer file validation. If visudo is on this system you can specify the path using the 'visudo_path' property.")
+ end
+ end
+ end
+ end
+end