fingerpuppet 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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