1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
require_relative '../../puppet'
require_relative '../../puppet/parameter/boolean'
Puppet::Type.newtype(:resources) do
@doc = "This is a metatype that can manage other resource types. Any
metaparams specified here will be passed on to any generated resources,
so you can purge unmanaged resources but set `noop` to true so the
purging is only logged and does not actually happen."
newparam(:name) do
desc "The name of the type to be managed."
validate do |name|
raise ArgumentError, _("Could not find resource type '%{name}'") % { name: name } unless Puppet::Type.type(name)
end
munge { |v| v.to_s }
end
newparam(:purge, :boolean => true, :parent => Puppet::Parameter::Boolean) do
desc "Whether to purge unmanaged resources. When set to `true`, this will
delete any resource that is not specified in your configuration and is not
autorequired by any managed resources. **Note:** The `ssh_authorized_key`
resource type can't be purged this way; instead, see the `purge_ssh_keys`
attribute of the `user` type."
defaultto :false
validate do |value|
if munge(value)
unless @resource.resource_type.respond_to?(:instances)
raise ArgumentError, _("Purging resources of type %{res_type} is not supported, since they cannot be queried from the system") % { res_type: @resource[:name] }
end
raise ArgumentError, _("Purging is only supported on types that accept 'ensure'") unless @resource.resource_type.validproperty?(:ensure)
end
end
end
newparam(:unless_system_user) do
desc "This keeps system users from being purged. By default, it
does not purge users whose UIDs are less than the minimum UID for the system (typically 500 or 1000), but you can specify
a different UID as the inclusive limit."
newvalues(:true, :false, /^\d+$/)
munge do |value|
case value
when /^\d+/
Integer(value)
when :true, true
@resource.class.system_users_max_uid
when :false, false
false
when Integer; value
else
raise ArgumentError, _("Invalid value %{value}") % { value: value.inspect }
end
end
defaultto {
if @resource[:name] == "user"
@resource.class.system_users_max_uid
else
nil
end
}
end
newparam(:unless_uid) do
desc 'This keeps specific uids or ranges of uids from being purged when purge is true.
Accepts integers, integer strings, and arrays of integers or integer strings.
To specify a range of uids, consider using the range() function from stdlib.'
munge do |value|
value = [value] unless value.is_a? Array
value.flatten.collect do |v|
case v
when Integer
v
when String
Integer(v)
else
raise ArgumentError, _("Invalid value %{value}.") % { value: v.inspect }
end
end
end
end
WINDOWS_SYSTEM_SID_REGEXES =
# Administrator, Guest, Domain Admins, Schema Admins, Enterprise Admins.
# https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems
[/S-1-5-21.+-500/, /S-1-5-21.+-501/, /S-1-5-21.+-512/, /S-1-5-21.+-518/,
/S-1-5-21.+-519/]
def check(resource)
@checkmethod ||= "#{self[:name]}_check"
@hascheck ||= respond_to?(@checkmethod)
if @hascheck
return send(@checkmethod, resource)
else
return true
end
end
def able_to_ensure_absent?(resource)
resource[:ensure] = :absent
rescue ArgumentError, Puppet::Error
err _("The 'ensure' attribute on %{name} resources does not accept 'absent' as a value") % { name: self[:name] }
false
end
# Generate any new resources we need to manage. This is pretty hackish
# right now, because it only supports purging.
def generate
return [] unless self.purge?
resource_type.instances.
reject { |r| catalog.resource_refs.include? r.ref }.
select { |r| check(r) }.
select { |r| r.class.validproperty?(:ensure) }.
select { |r| able_to_ensure_absent?(r) }.
each { |resource|
resource.copy_metaparams(@parameters)
resource.purging
}
end
def resource_type
unless defined?(@resource_type)
type = Puppet::Type.type(self[:name])
unless type
raise Puppet::DevError, _("Could not find resource type")
end
@resource_type = type
end
@resource_type
end
# Make sure we don't purge users with specific uids
def user_check(resource)
return true unless self[:name] == "user"
return true unless self[:unless_system_user]
resource[:audit] = :uid
current_values = resource.retrieve_resource
current_uid = current_values[resource.property(:uid)]
unless_uids = self[:unless_uid]
return false if system_users.include?(resource[:name])
return false if unless_uids && unless_uids.include?(current_uid)
if current_uid.is_a?(String)
# Windows user; is a system user if any regex matches.
WINDOWS_SYSTEM_SID_REGEXES.none? { |regex| current_uid =~ regex }
else
current_uid > self[:unless_system_user]
end
end
def system_users
%w{root nobody bin noaccess daemon sys}
end
def self.system_users_max_uid
return @system_users_max_uid if @system_users_max_uid
# First try to read the minimum user id from login.defs
if Puppet::FileSystem.exist?('/etc/login.defs')
@system_users_max_uid = Puppet::FileSystem.each_line '/etc/login.defs' do |line|
break $1.to_i - 1 if line =~ /^\s*UID_MIN\s+(\d+)(\s*#.*)?$/
end
end
# Otherwise, use a sensible default based on the OS family
@system_users_max_uid ||= case Puppet.runtime[:facter].value(:osfamily)
when 'OpenBSD', 'FreeBSD'
999
else
499
end
@system_users_max_uid
end
def self.reset_system_users_max_uid!
@system_users_max_uid = nil
end
end
|