~ubuntu-branches/ubuntu/quantal/puppet/quantal-security

« back to all changes in this revision

Viewing changes to .pc/CVE-2011-3872.patch/lib/puppet/application/kick.rb

  • Committer: Bazaar Package Importer
  • Author(s): Marc Deslauriers
  • Date: 2011-10-24 15:05:12 UTC
  • Revision ID: james.westby@ubuntu.com-20111024150512-yxqwfdp6hcs6of5l
Tags: 2.7.1-1ubuntu3.2
* SECURITY UPDATE: puppet master impersonation via incorrect certificates
  - debian/patches/CVE-2011-3872.patch: refactor certificate handling.
  - Thanks to upstream for providing the patch.
  - CVE-2011-3872

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
require 'puppet/application'
 
2
 
 
3
class Puppet::Application::Kick < Puppet::Application
 
4
 
 
5
  should_not_parse_config
 
6
 
 
7
  attr_accessor :hosts, :tags, :classes
 
8
 
 
9
  option("--all","-a")
 
10
  option("--foreground","-f")
 
11
  option("--debug","-d")
 
12
  option("--ping","-P")
 
13
  option("--test")
 
14
 
 
15
  option("--host HOST") do |arg|
 
16
    @hosts << arg
 
17
  end
 
18
 
 
19
  option("--tag TAG", "-t") do |arg|
 
20
    @tags << arg
 
21
  end
 
22
 
 
23
  option("--class CLASS", "-c") do |arg|
 
24
    @classes << arg
 
25
  end
 
26
 
 
27
  option("--no-fqdn", "-n") do |arg|
 
28
    options[:fqdn] = false
 
29
  end
 
30
 
 
31
  option("--parallel PARALLEL", "-p") do |arg|
 
32
    begin
 
33
      options[:parallel] = Integer(arg)
 
34
    rescue
 
35
      $stderr.puts "Could not convert #{arg.inspect} to an integer"
 
36
      exit(23)
 
37
    end
 
38
  end
 
39
 
 
40
  def help
 
41
    <<-HELP
 
42
 
 
43
puppet-kick(8) -- Remotely control puppet agent
 
44
========
 
45
 
 
46
SYNOPSIS
 
47
--------
 
48
Trigger a puppet agent run on a set of hosts.
 
49
 
 
50
 
 
51
USAGE
 
52
-----
 
53
puppet kick [-a|--all] [-c|--class <class>] [-d|--debug] [-f|--foreground]
 
54
  [-h|--help] [--host <host>] [--no-fqdn] [--ignoreschedules]
 
55
  [-t|--tag <tag>] [--test] [-p|--ping] <host> [<host> [...]]
 
56
 
 
57
 
 
58
DESCRIPTION
 
59
-----------
 
60
This script can be used to connect to a set of machines running 'puppet
 
61
agent' and trigger them to run their configurations. The most common
 
62
usage would be to specify a class of hosts and a set of tags, and
 
63
'puppet kick' would look up in LDAP all of the hosts matching that
 
64
class, then connect to each host and trigger a run of all of the objects
 
65
with the specified tags.
 
66
 
 
67
If you are not storing your host configurations in LDAP, you can specify
 
68
hosts manually.
 
69
 
 
70
You will most likely have to run 'puppet kick' as root to get access to
 
71
the SSL certificates.
 
72
 
 
73
'puppet kick' reads 'puppet master''s configuration file, so that it can
 
74
copy things like LDAP settings.
 
75
 
 
76
 
 
77
USAGE NOTES
 
78
-----------
 
79
Puppet kick is useless unless puppet agent is listening for incoming
 
80
connections and allowing access to the `run` endpoint. This entails
 
81
starting the agent with `listen = true` in its puppet.conf file, and
 
82
allowing access to the `/run` path in its auth.conf file; see
 
83
`http://docs.puppetlabs.com/guides/rest_auth_conf.html` for more
 
84
details.
 
85
 
 
86
Additionally, due to a known bug, you must make sure a
 
87
namespaceauth.conf file exists in puppet agent's $confdir. This file
 
88
will not be consulted, and may be left empty.  
 
89
 
 
90
OPTIONS
 
91
-------
 
92
Note that any configuration parameter that's valid in the configuration
 
93
file is also a valid long argument. For example, 'ssldir' is a valid
 
94
configuration parameter, so you can specify '--ssldir <directory>' as an
 
95
argument.
 
96
 
 
97
See the configuration file documentation at
 
98
http://docs.puppetlabs.com/references/latest/configuration.html for
 
99
the full list of acceptable parameters. A commented list of all
 
100
configuration options can also be generated by running puppet master
 
101
with '--genconfig'.
 
102
 
 
103
* --all:
 
104
  Connect to all available hosts. Requires LDAP support at this point.
 
105
 
 
106
* --class:
 
107
  Specify a class of machines to which to connect. This only works if
 
108
  you have LDAP configured, at the moment.
 
109
 
 
110
* --debug:
 
111
  Enable full debugging.
 
112
 
 
113
* --foreground:
 
114
  Run each configuration in the foreground; that is, when connecting to
 
115
  a host, do not return until the host has finished its run. The default
 
116
  is false.
 
117
 
 
118
* --help:
 
119
  Print this help message
 
120
 
 
121
* --host:
 
122
  A specific host to which to connect. This flag can be specified more
 
123
  than once.
 
124
 
 
125
* --ignoreschedules:
 
126
  Whether the client should ignore schedules when running its
 
127
  configuration. This can be used to force the client to perform work it
 
128
  would not normally perform so soon. The default is false.
 
129
 
 
130
* --parallel:
 
131
  How parallel to make the connections. Parallelization is provided by
 
132
  forking for each client to which to connect. The default is 1, meaning
 
133
  serial execution.
 
134
 
 
135
* --tag:
 
136
  Specify a tag for selecting the objects to apply. Does not work with
 
137
  the --test option.
 
138
 
 
139
* --test:
 
140
  Print the hosts you would connect to but do not actually connect. This
 
141
  option requires LDAP support at this point.
 
142
 
 
143
* --ping:
 
144
  Do a ICMP echo against the target host. Skip hosts that don't respond
 
145
  to ping.
 
146
 
 
147
 
 
148
EXAMPLE
 
149
-------
 
150
    $ sudo puppet kick -p 10 -t remotefile -t webserver host1 host2
 
151
 
 
152
 
 
153
AUTHOR
 
154
------
 
155
Luke Kanies
 
156
 
 
157
 
 
158
COPYRIGHT
 
159
---------
 
160
Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
 
161
 
 
162
    HELP
 
163
  end
 
164
 
 
165
  def run_command
 
166
    @hosts += command_line.args
 
167
    options[:test] ? test : main
 
168
  end
 
169
 
 
170
  def test
 
171
    puts "Skipping execution in test mode"
 
172
    exit(0)
 
173
  end
 
174
 
 
175
  def main
 
176
    require 'puppet/network/client'
 
177
 
 
178
    Puppet.warning "Failed to load ruby LDAP library. LDAP functionality will not be available" unless Puppet.features.ldap?
 
179
    require 'puppet/util/ldap/connection'
 
180
 
 
181
    todo = @hosts.dup
 
182
 
 
183
    failures = []
 
184
 
 
185
    # Now do the actual work
 
186
    go = true
 
187
    while go
 
188
      # If we don't have enough children in process and we still have hosts left to
 
189
      # do, then do the next host.
 
190
      if @children.length < options[:parallel] and ! todo.empty?
 
191
        host = todo.shift
 
192
        pid = fork do
 
193
          run_for_host(host)
 
194
        end
 
195
        @children[pid] = host
 
196
      else
 
197
        # Else, see if we can reap a process.
 
198
        begin
 
199
          pid = Process.wait
 
200
 
 
201
          if host = @children[pid]
 
202
            # Remove our host from the list of children, so the parallelization
 
203
            # continues working.
 
204
            @children.delete(pid)
 
205
            failures << host if $CHILD_STATUS.exitstatus != 0
 
206
            print "#{host} finished with exit code #{$CHILD_STATUS.exitstatus}\n"
 
207
          else
 
208
            $stderr.puts "Could not find host for PID #{pid} with status #{$CHILD_STATUS.exitstatus}"
 
209
          end
 
210
        rescue Errno::ECHILD
 
211
          # There are no children left, so just exit unless there are still
 
212
          # children left to do.
 
213
          next unless todo.empty?
 
214
 
 
215
          if failures.empty?
 
216
            puts "Finished"
 
217
            exit(0)
 
218
          else
 
219
            puts "Failed: #{failures.join(", ")}"
 
220
            exit(3)
 
221
          end
 
222
        end
 
223
      end
 
224
    end
 
225
  end
 
226
 
 
227
  def run_for_host(host)
 
228
    if options[:ping]
 
229
      out = %x{ping -c 1 #{host}}
 
230
      unless $CHILD_STATUS == 0
 
231
        $stderr.print "Could not contact #{host}\n"
 
232
        exit($CHILD_STATUS)
 
233
      end
 
234
    end
 
235
 
 
236
    require 'puppet/run'
 
237
    Puppet::Run.indirection.terminus_class = :rest
 
238
    port = Puppet[:puppetport]
 
239
    url = ["https://#{host}:#{port}", "production", "run", host].join('/')
 
240
 
 
241
    print "Triggering #{host}\n"
 
242
    begin
 
243
      run_options = {
 
244
        :tags => @tags,
 
245
        :background => ! options[:foreground],
 
246
        :ignoreschedules => options[:ignoreschedules]
 
247
      }
 
248
      run = Puppet::Run.indirection.save(Puppet::Run.new( run_options ), url)
 
249
      puts "Getting status"
 
250
      result = run.status
 
251
      puts "status is #{result}"
 
252
    rescue => detail
 
253
      puts detail.backtrace if Puppet[:trace]
 
254
      $stderr.puts "Host #{host} failed: #{detail}\n"
 
255
      exit(2)
 
256
    end
 
257
 
 
258
    case result
 
259
    when "success";
 
260
      exit(0)
 
261
    when "running"
 
262
      $stderr.puts "Host #{host} is already running"
 
263
      exit(3)
 
264
    else
 
265
      $stderr.puts "Host #{host} returned unknown answer '#{result}'"
 
266
      exit(12)
 
267
    end
 
268
  end
 
269
 
 
270
  def initialize(*args)
 
271
    super
 
272
    @hosts = []
 
273
    @classes = []
 
274
    @tags = []
 
275
  end
 
276
 
 
277
  def preinit
 
278
    [:INT, :TERM].each do |signal|
 
279
      Signal.trap(signal) do
 
280
        $stderr.puts "Cancelling"
 
281
        exit(1)
 
282
      end
 
283
    end
 
284
    options[:parallel] = 1
 
285
    options[:verbose] = true
 
286
    options[:fqdn] = true
 
287
    options[:ignoreschedules] = false
 
288
    options[:foreground] = false
 
289
  end
 
290
 
 
291
  def setup
 
292
    if options[:debug]
 
293
      Puppet::Util::Log.level = :debug
 
294
    else
 
295
      Puppet::Util::Log.level = :info
 
296
    end
 
297
 
 
298
    # Now parse the config
 
299
    Puppet.parse_config
 
300
 
 
301
    if Puppet[:node_terminus] == "ldap" and (options[:all] or @classes)
 
302
      if options[:all]
 
303
        @hosts = Puppet::Node.indirection.search("whatever", :fqdn => options[:fqdn]).collect { |node| node.name }
 
304
        puts "all: #{@hosts.join(", ")}"
 
305
      else
 
306
        @hosts = []
 
307
        @classes.each do |klass|
 
308
          list = Puppet::Node.indirection.search("whatever", :fqdn => options[:fqdn], :class => klass).collect { |node| node.name }
 
309
          puts "#{klass}: #{list.join(", ")}"
 
310
 
 
311
          @hosts += list
 
312
        end
 
313
      end
 
314
    elsif ! @classes.empty?
 
315
      $stderr.puts "You must be using LDAP to specify host classes"
 
316
      exit(24)
 
317
    end
 
318
 
 
319
    @children = {}
 
320
 
 
321
    # If we get a signal, then kill all of our children and get out.
 
322
    [:INT, :TERM].each do |signal|
 
323
      Signal.trap(signal) do
 
324
        Puppet.notice "Caught #{signal}; shutting down"
 
325
        @children.each do |pid, host|
 
326
          Process.kill("INT", pid)
 
327
        end
 
328
 
 
329
        waitall
 
330
 
 
331
        exit(1)
 
332
      end
 
333
    end
 
334
 
 
335
  end
 
336
 
 
337
end