File: resources.rb

package info (click to toggle)
puppet-agent 7.23.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 19,092 kB
  • sloc: ruby: 245,074; sh: 456; makefile: 38; xml: 33
file content (186 lines) | stat: -rw-r--r-- 6,019 bytes parent folder | download
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