fingerpuppet 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,26 +1,53 @@
1
- Usage : fingerpuppet <commandstring>
1
+ Introduction
2
+ ============
2
3
 
3
- Steps for using the API:
4
- 1:) fingerpuppet --init --certname my.cert.name --server my.server.name
5
- Builds the config file
6
- Generates the certificate and CSR
7
- Submits the CSR to the Puppetmaster
4
+ `fingerpuppet` is a simple library and commandline tool to interact with Puppet's REST API
5
+ without needing to have Puppet itself installed. This may be integrated, for example,
6
+ into a provisioning tool to allow your provisioning process to remotely sign certificates
7
+ of newly built systems. Alternatively, you could use it to request known facts about
8
+ a node from your Puppet Master, or even to request a catalog for a node to, for example,
9
+ perform acceptance testing against a new version of Puppet before upgrading your
10
+ production master.
8
11
 
9
- 2:) On puppetmaster: puppet cert sign my.cert.name
12
+ Limitations
13
+ ============
10
14
 
11
- 3:) restapi.rb --install
12
- Downloads the signed certificate and installs it
15
+ This is still in early development. Features may not work completely as advertised, and will
16
+ certainly be less polished than an established product. Pull requests are welcome!
13
17
 
14
- 4:) ...
15
18
 
16
- 5:) Profit!
19
+ Usage
20
+ ============
17
21
 
18
- Your Puppetmaster must be configured to allow requests other than certificate requests.
19
- See http://docs.puppetlabs.com/guides/rest_auth_conf.html for more information.
22
+ `fingerpuppet [commandstring]`
20
23
 
21
- The certname can be specified with --certname or with optional CERTNAME argument to many options.
24
+ Steps for using the API with fingerpuppet:
22
25
 
23
- You may want to use the '-dcn' options to print the cURL equivalent command.
26
+ 1. `fingerpuppet --init --certname my.cert.name --server my.server.name`
27
+ * Builds the config file
28
+ * Generates the certificate and CSR
29
+ * Submits the CSR to the Puppetmaster
30
+ 2. On puppetmaster: `puppet cert sign my.cert.name`
31
+ 3. `fingerpuppet --install`
32
+ * Downloads the signed certificate and installs it
33
+ 4. ...
34
+ 5. Profit!
35
+
36
+ Your Puppetmaster must be configured to allow requests other than certificate requests.
37
+ See [http://docs.puppetlabs.com/guides/rest_auth_conf.html](http://docs.puppetlabs.com/guides/rest_auth_conf.html) for more information.
38
+
39
+ For example, you could add the stanza below as one of the first rules in `auth.conf` to
40
+ allow a single authenticated provisioning system complete access to all endpoints.
41
+
42
+ # Allow the provisioner unfettered access to any endpoint
43
+ path /
44
+ auth yes
45
+ allow provision.mycompany.com
46
+
47
+
48
+ The certname can be specified with `--certname` or with optional `CERTNAME` argument to many options.
49
+
50
+ You may want to use the `-dcn` options to print the cURL equivalent command.
24
51
 
25
52
  -d, --debug runs in debug mode
26
53
  -h, --help Displays this help
@@ -46,7 +73,7 @@ Usage : fingerpuppet <commandstring>
46
73
  --certificate [CERTNAME] Retrieve the certificate for a given certname or 'ca'.
47
74
  --cert_status [CERTNAME] Retrieve the certificate status for a given certname. Set the status by using --state.
48
75
  --cert_revocation_list Retrieve and display the certifiacte revocation list from the master.
49
- --sign [CERTNAME] Instruct the Puppetmaster to sign a certificate. Requires significate privileges in auth.conf.
76
+ --sign [CERTNAME] Instruct the Puppetmaster to sign a certificate. Requires significant privileges in auth.conf.
50
77
 
51
78
  --file_metadata PATH Retrieve the metadata for a file.
52
79
  --getfile PATH Download a file from the Puppetmaster. Save to --file or output on stdout.
@@ -54,3 +81,35 @@ Usage : fingerpuppet <commandstring>
54
81
  --resource RESOURCE Returns a list of resources (e.g. user) or information about a resource (e.g. 'user/elvis')
55
82
  --report [CERTNAME] Sends a YAML report to the Puppetmaster. Requires --file or --state.
56
83
  --status Check to make sure the Puppetmaster is alive and well.
84
+
85
+ Contact
86
+ =======
87
+
88
+ * Author: Ben Ford
89
+ * Email: ben.ford@puppetlabs.com
90
+ * Twitter: @binford2k
91
+ * IRC (Freenode): binford2k
92
+
93
+ License
94
+ =======
95
+
96
+ Copyright (c) 2012 Puppet Labs, info@puppetlabs.com
97
+
98
+ Permission is hereby granted, free of charge, to any person obtaining
99
+ a copy of this software and associated documentation files (the
100
+ "Software"), to deal in the Software without restriction, including
101
+ without limitation the rights to use, copy, modify, merge, publish,
102
+ distribute, sublicense, and/or sell copies of the Software, and to
103
+ permit persons to whom the Software is furnished to do so, subject to
104
+ the following conditions:
105
+
106
+ The above copyright notice and this permission notice shall be
107
+ included in all copies or substantial portions of the Software.
108
+
109
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
110
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
111
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
112
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
113
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
114
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
115
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/bin/fingerpuppet CHANGED
@@ -15,7 +15,7 @@ optparse = OptionParser.new { |opts|
15
15
 
16
16
  2:) On puppetmaster: puppet cert sign my.cert.name
17
17
 
18
- 3:) restapi.rb --install
18
+ 3:) fingerpuppet --install
19
19
  Downloads the signed certificate and installs it
20
20
 
21
21
  4:) ...
@@ -30,7 +30,7 @@ optparse = OptionParser.new { |opts|
30
30
  You may want to use the '-dcn' options to print the cURL equivalent command.
31
31
 
32
32
  "
33
-
33
+
34
34
  options[:show]=false
35
35
  opts.on("-d", "--debug", "runs in debug mode") do
36
36
  options[:debug] = true
@@ -193,7 +193,7 @@ begin
193
193
  when 'delete'
194
194
  restAPI.delete(options[:certname])
195
195
  when 'file_metadata'
196
- restAPI.file_metadata(options[:argument])
196
+ restAPI.file_metadata(options[:argument])
197
197
  when 'getfile'
198
198
  restAPI.getfile(options[:argument])
199
199
  when 'certificate'
@@ -210,7 +210,8 @@ begin
210
210
  restAPI.report(options[:certname], options[:filename], options[:state])
211
211
  else
212
212
  puts 'Use -h/--help for usage documentation.'
213
- end
213
+ end
214
214
  rescue Exception => e
215
215
  puts e
216
216
  end
217
+
@@ -4,14 +4,14 @@ require "net/https"
4
4
  module Fingerpuppet
5
5
  class RestAPI
6
6
  attr_accessor :server, :certname
7
-
7
+
8
8
  def initialize( options={} )
9
9
  @debug = options[:debug]
10
10
  @curl = options[:curl]
11
11
  @nop = options[:nop]
12
12
  @output = options[:output]
13
13
  @configdir = File.expand_path('~/.fingerpuppet')
14
-
14
+
15
15
  begin
16
16
  config = YAML.load_file("#{@configdir}/config.yaml")
17
17
  @server = config['server']
@@ -20,19 +20,19 @@ module Fingerpuppet
20
20
  puts 'Initializing API...'
21
21
  end
22
22
  end
23
-
23
+
24
24
  # a helper that allows one to use either commandline curl or Net::HTTP
25
25
  # this is useful mostly with -dcn to just print out the curl commandline
26
26
  # you would use to accomplish what you're trying to do
27
27
  def command( opts={} )
28
28
  # this allows a global @output var, but to also override that per call
29
29
  opts[:output] ||= @output
30
-
30
+
31
31
  if @curl
32
32
  curl(opts)
33
33
  else
34
34
  data = rest(opts)
35
-
35
+
36
36
  if opts[:output]
37
37
  save(opts[:output], data)
38
38
  else
@@ -42,36 +42,36 @@ module Fingerpuppet
42
42
  end
43
43
  end
44
44
  end
45
-
45
+
46
46
  def save(path, data)
47
47
  file = File.new(path, 'w')
48
48
  file.syswrite(data)
49
49
  file.close
50
50
  end
51
-
51
+
52
52
  def rest( opts={} )
53
53
  opts[:type] ||= 'yaml'
54
54
  uri = "/production/#{opts[:action]}/#{opts[:argument]}"
55
-
55
+
56
56
  http = Net::HTTP.new(@server, 8140)
57
57
  http.use_ssl = true
58
-
58
+
59
59
  unless opts[:noauth]
60
60
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
61
-
61
+
62
62
  store = OpenSSL::X509::Store.new
63
63
  store.add_cert(OpenSSL::X509::Certificate.new(File.read("#{@configdir}/ca_crt.pem")))
64
64
  http.cert_store = store
65
-
65
+
66
66
  http.key = OpenSSL::PKey::RSA.new(File.read("#{@configdir}/#{@certname}.key"))
67
67
  http.cert = OpenSSL::X509::Certificate.new(File.read("#{@configdir}/#{@certname}.pem"))
68
68
  end
69
-
69
+
70
70
  case opts[:method]
71
71
  when 'PUT'
72
72
  request = Net::HTTP::Put.new(uri)
73
73
  request["Content-Type"] = "text/#{opts[:type]}"
74
-
74
+
75
75
  if opts[:file]
76
76
  # set the body to the binary contents of :file
77
77
  file = File.open(opts[:file], 'rb')
@@ -80,7 +80,7 @@ module Fingerpuppet
80
80
  # set the body to the string value of :data
81
81
  request.body = opts[:data]
82
82
  end
83
-
83
+
84
84
  when 'DELETE'
85
85
  request = Net::HTTP::Delete.new(uri)
86
86
  when 'HEAD'
@@ -89,44 +89,44 @@ module Fingerpuppet
89
89
  # default to a GET request
90
90
  request = Net::HTTP::Get.new(uri)
91
91
  end
92
-
92
+
93
93
  request["Accept"] = opts[:type]
94
-
94
+
95
95
  if @debug
96
96
  puts '------ HTTP Request ------'
97
97
  puts request.to_yaml
98
98
  puts '--------------------------'
99
99
  end
100
-
100
+
101
101
  return @nop ? '' : http.request(request).body
102
102
  end
103
-
103
+
104
104
  #def command(action, argument='', method='GET', type='yaml', output=false, file=false, data=false)
105
105
  def curl( opts={} )
106
106
  opts[:type] ||= 'yaml'
107
-
107
+
108
108
  if opts[:noauth]
109
109
  auth = '-k '
110
110
  else
111
111
  auth = "--cert #{@configdir}/#{@certname}.pem --key #{@configdir}/#{@certname}.key --cacert #{@configdir}/ca_crt.pem"
112
112
  end
113
-
113
+
114
114
  output = opts[:output] ? "-o #{opts[:output]}" : ''
115
115
  header = "-H 'Accept: #{opts[:type]}'"
116
-
116
+
117
117
  case opts[:method]
118
118
  when 'PUT'
119
119
  methodstr = '-X PUT'
120
120
  header = "-H 'Content-Type: text/#{opts[:type]}'"
121
-
121
+
122
122
  if opts[:file]
123
123
  filestr = "--data-binary @#{opts[:file]}"
124
124
  end
125
-
125
+
126
126
  if opts[:data]
127
127
  datastr = "--data '#{opts[:data]}'"
128
128
  end
129
-
129
+
130
130
  when 'DELETE'
131
131
  methodstr = '-X DELETE'
132
132
  when 'HEAD'
@@ -135,7 +135,7 @@ module Fingerpuppet
135
135
  # default to a GET request
136
136
  methodstr = ''
137
137
  end
138
-
138
+
139
139
  uri = "https://#{@server}:8140/production/#{opts[:action]}/#{opts[:argument]}"
140
140
  cmd = "curl #{auth} #{methodstr} #{output} #{filestr} #{datastr} #{header} \"#{uri}\"" #quoted uri for fact ampersands
141
141
  if @debug
@@ -147,50 +147,50 @@ module Fingerpuppet
147
147
  puts # newline after curl output
148
148
  end
149
149
  end
150
-
150
+
151
151
  def init(certname, server)
152
152
  if certname == nil || server == nil
153
153
  puts "Must set server and certname to initialize API"
154
154
  exit
155
155
  end
156
-
156
+
157
157
  @certname = certname
158
158
  @server = server
159
-
159
+
160
160
  if File::directory?( @configdir )
161
161
  require 'fileutils'
162
162
  FileUtils.rm_rf( @configdir )
163
163
  end
164
-
164
+
165
165
  Dir.mkdir( @configdir )
166
166
  configfile = File.new("#{@configdir}/config.yaml", 'w')
167
167
  configfile.syswrite("version: 0.1\n")
168
168
  configfile.syswrite("certname: #{@certname}\n")
169
169
  configfile.syswrite("server: #{@server}\n")
170
170
  configfile.close
171
-
171
+
172
172
  begin
173
173
  if not system("openssl genrsa -out #{@configdir}/#{@certname}.key 1024")
174
174
  raise StandardError, 'Certificate generation failed.'
175
175
  end
176
-
176
+
177
177
  if not system("openssl req -new -key #{@configdir}/#{@certname}.key -subj '/CN=#{@certname}' -out #{@configdir}/#{@certname}.csr")
178
178
  raise StandardError, 'CSR generation failed.'
179
179
  end
180
-
180
+
181
181
  self.command( { :action => 'certificate_request',
182
182
  :argument => @certname,
183
183
  :file => "#{@configdir}/#{@certname}.csr",
184
184
  :type => 'plain',
185
185
  :method => 'PUT',
186
186
  :noauth => true } )
187
-
187
+
188
188
  puts "CSR submitted. Now go sign it on the server and rerun this with --install."
189
189
  rescue Exception => e
190
190
  puts "Failure: #{e.message}"
191
191
  end
192
192
  end
193
-
193
+
194
194
  def install
195
195
  begin
196
196
  self.command( { :action => 'certificate',
@@ -198,134 +198,134 @@ module Fingerpuppet
198
198
  :output => "#{@configdir}/#{@certname}.pem",
199
199
  :type => 's',
200
200
  :noauth => true } )
201
-
201
+
202
202
  self.command( { :action => 'certificate',
203
203
  :argument => 'ca',
204
204
  :output => "#{@configdir}/ca_crt.pem",
205
205
  :type => 's',
206
206
  :noauth => true } )
207
-
207
+
208
208
  puts "Certificate installed. API ready for use."
209
209
  rescue Exception => e
210
210
  puts "Failure: #{e.message}"
211
211
  end
212
212
  end
213
-
213
+
214
214
  def catalog
215
- self.command( { :action => 'catalog',
215
+ self.command( { :action => 'catalog',
216
216
  :argument => @certname } )
217
217
  end
218
-
218
+
219
219
  def status
220
- self.command( { :action => 'status',
220
+ self.command( { :action => 'status',
221
221
  :argument => 'no_key' } )
222
222
  end
223
-
223
+
224
224
  def facts(node)
225
- self.command( { :action => 'facts',
225
+ self.command( { :action => 'facts',
226
226
  :argument => node } )
227
227
  end
228
-
228
+
229
229
  def search(query)
230
230
  query.map!{ |item| "facts.#{item}"}
231
- self.command( { :action => 'facts_search',
231
+ self.command( { :action => 'facts_search',
232
232
  :argument => "search?#{query.join('&')}" } )
233
233
  end
234
-
234
+
235
235
  def node(node)
236
- self.command( { :action => 'node',
236
+ self.command( { :action => 'node',
237
237
  :argument => node } )
238
238
  end
239
-
239
+
240
240
  def insert(node, path)
241
- self.command( { :action => 'facts',
242
- :argument => node,
243
- :method => 'PUT',
241
+ self.command( { :action => 'facts',
242
+ :argument => node,
243
+ :method => 'PUT',
244
244
  :file => path } )
245
245
  end
246
-
246
+
247
247
  def file_metadata(path)
248
248
  self.command( { :action => 'file_content',
249
249
  :argument => path,
250
250
  :type => 'yaml' } )
251
251
  end
252
-
252
+
253
253
  def getfile(path)
254
254
  self.command( { :action => 'file_content',
255
255
  :argument => path,
256
256
  :type => 'raw' } )
257
257
  end
258
-
258
+
259
259
  def delete(node)
260
260
  self.certificate_status(node, 'revoked')
261
- self.command( { :action => 'certificate_status',
262
- :argument => node,
263
- :method => 'DELETE',
261
+ self.command( { :action => 'certificate_status',
262
+ :argument => node,
263
+ :method => 'DELETE',
264
264
  :type => 'pson' } )
265
265
  end
266
-
266
+
267
267
  def certificate(node)
268
- self.command( { :action => 'certificate',
269
- :argument => node,
268
+ self.command( { :action => 'certificate',
269
+ :argument => node,
270
270
  :type => 's' } )
271
271
  end
272
-
272
+
273
273
  def sign(node)
274
- self.command( { :action => 'certificate_status',
275
- :argument => node,
276
- :method => 'PUT',
274
+ self.command( { :action => 'certificate_status',
275
+ :argument => node,
276
+ :method => 'PUT',
277
277
  :type => 'pson',
278
- :noauth => true,
279
278
  :data => "{\"desired_state\":\"signed\"}" } )
280
279
  end
281
-
280
+
282
281
  def certificate_revocation_list
283
- self.command( { :action => 'certificate_revocation_list',
284
- :argument => 'ca',
282
+ self.command( { :action => 'certificate_revocation_list',
283
+ :argument => 'ca',
285
284
  :type => 's' } )
286
285
  end
287
-
286
+
288
287
  def certificate_status(node, state=nil)
289
288
  if node == nil
290
- self.command( { :action => 'certificate_statuses',
291
- :action => 'no_key',
289
+ self.command( { :action => 'certificate_statuses',
290
+ :action => 'no_key',
292
291
  :type => 'pson' } )
293
292
  elsif state == nil
294
- self.command( { :action => 'certificate_status',
295
- :argument => node,
293
+ self.command( { :action => 'certificate_status',
294
+ :argument => node,
296
295
  :type => 'pson' } )
297
296
  else
298
- self.command( { :action => 'certificate_status',
299
- :argument => node,
300
- :method => 'PUT',
301
- :type => 'pson',
297
+ self.command( { :action => 'certificate_status',
298
+ :argument => node,
299
+ :method => 'PUT',
300
+ :type => 'pson',
302
301
  :data => "{\"desired_state\":\"#{state}\"}" } )
303
302
  end
304
303
  end
305
-
304
+
306
305
  def resource(resource)
307
- self.command( { :action => 'resource',
306
+ self.command( { :action => 'resource',
308
307
  :argument => resource } )
309
308
  end
310
-
309
+
311
310
  def report(node, file, data=nil)
312
311
  if data
313
- self.command( { :action => 'report',
314
- :argument => node,
315
- :method => 'PUT',
312
+ self.command( { :action => 'report',
313
+ :argument => node,
314
+ :method => 'PUT',
316
315
  :data => data } )
317
316
  else
318
- self.command( { :action => 'report',
319
- :argument => node,
320
- :method => 'PUT',
317
+ self.command( { :action => 'report',
318
+ :argument => node,
319
+ :method => 'PUT',
321
320
  :file => file } )
322
321
  end
323
322
  end
324
-
323
+
325
324
  def debug
326
325
  puts "@configdir: #{@configdir}"
327
326
  puts " @server: #{@server}"
328
327
  puts " @certname: #{@certname}"
329
328
  end
330
329
  end
331
- end
330
+ end
331
+
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 1
9
- version: 0.0.1
8
+ - 2
9
+ version: 0.0.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ben Ford
@@ -14,11 +14,19 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2013-03-28 00:00:00 -07:00
17
+ date: 2013-03-28 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
21
- description: " A simple library and tool to interact with Puppet's REST API without needing Puppet itself installed.\n"
21
+ description: |
22
+ `fingerpuppet` is a simple library and commandline tool to interact with Puppet's REST API
23
+ without needing to have Puppet itself installed. This may be integrated, for example,
24
+ into a provisioning tool to allow your provisioning process to remotely sign certificates
25
+ of newly built systems. Alternatively, you could use it to request known facts about
26
+ a node from your Puppet Master, or even to request a catalog for a node to, for example,
27
+ perform acceptance testing against a new version of Puppet before upgrading your
28
+ production master.
29
+
22
30
  email: binford2k@gmail.com
23
31
  executables:
24
32
  - fingerpuppet