puppetserver-ca 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/puppetserver/ca/action/clean.rb +102 -0
  3. data/lib/puppetserver/ca/action/create.rb +161 -0
  4. data/lib/puppetserver/ca/action/generate.rb +313 -0
  5. data/lib/puppetserver/ca/action/import.rb +132 -0
  6. data/lib/puppetserver/ca/action/list.rb +132 -0
  7. data/lib/puppetserver/ca/action/revoke.rb +101 -0
  8. data/lib/puppetserver/ca/action/sign.rb +126 -0
  9. data/lib/puppetserver/ca/certificate_authority.rb +224 -0
  10. data/lib/puppetserver/ca/cli.rb +17 -16
  11. data/lib/puppetserver/ca/config/puppet.rb +242 -0
  12. data/lib/puppetserver/ca/config/puppetserver.rb +85 -0
  13. data/lib/puppetserver/ca/utils/cli_parsing.rb +82 -0
  14. data/lib/puppetserver/ca/utils/config.rb +13 -0
  15. data/lib/puppetserver/ca/utils/file_system.rb +90 -0
  16. data/lib/puppetserver/ca/utils/http_client.rb +129 -0
  17. data/lib/puppetserver/ca/utils/signing_digest.rb +27 -0
  18. data/lib/puppetserver/ca/version.rb +1 -1
  19. metadata +17 -17
  20. data/lib/puppetserver/ca/clean_action.rb +0 -157
  21. data/lib/puppetserver/ca/config_utils.rb +0 -11
  22. data/lib/puppetserver/ca/create_action.rb +0 -265
  23. data/lib/puppetserver/ca/generate_action.rb +0 -227
  24. data/lib/puppetserver/ca/import_action.rb +0 -153
  25. data/lib/puppetserver/ca/list_action.rb +0 -153
  26. data/lib/puppetserver/ca/puppet_config.rb +0 -197
  27. data/lib/puppetserver/ca/puppetserver_config.rb +0 -83
  28. data/lib/puppetserver/ca/revoke_action.rb +0 -136
  29. data/lib/puppetserver/ca/sign_action.rb +0 -190
  30. data/lib/puppetserver/ca/utils.rb +0 -80
  31. data/lib/puppetserver/settings/ttl_setting.rb +0 -48
  32. data/lib/puppetserver/utils/file_utilities.rb +0 -78
  33. data/lib/puppetserver/utils/http_client.rb +0 -129
  34. data/lib/puppetserver/utils/signing_digest.rb +0 -25
@@ -1,157 +0,0 @@
1
- require 'puppetserver/ca/utils'
2
- require 'puppetserver/utils/http_client'
3
- require 'puppetserver/utils/file_utilities'
4
- require 'puppetserver/ca/puppet_config'
5
- require 'puppetserver/ca/revoke_action'
6
-
7
- require 'optparse'
8
- require 'json'
9
-
10
- module Puppetserver
11
- module Ca
12
- class CleanAction
13
-
14
- include Puppetserver::Utils
15
-
16
- CERTNAME_BLACKLIST = %w{--all --config}
17
-
18
- SUMMARY = 'Clean files from the CA for certificate(s)'
19
- BANNER = <<-BANNER
20
- Usage:
21
- puppetserver ca clean [--help|--version]
22
- puppetserver ca clean [--config] --certname CERTNAME[,ADDLCERTNAME]
23
-
24
- Description:
25
- Given one or more valid certnames, instructs the CA to revoke certificates
26
- matching the given certnames if they exist, and then remove files pertaining
27
- to them (keys, cert, and certificate request) over HTTPS using the local
28
- agent's PKI
29
-
30
- Options:
31
- BANNER
32
-
33
- def self.parser(parsed = {})
34
- parsed['certnames'] = []
35
- OptionParser.new do |o|
36
- o.banner = BANNER
37
- o.on('--certname foo,bar', Array,
38
- 'One or more comma separated certnames') do |certs|
39
- parsed['certnames'] += certs
40
- end
41
- o.on('--config PUPPET.CONF', 'Custom path to puppet.conf') do |conf|
42
- parsed['config'] = conf
43
- end
44
- o.on('--help', 'Displays this clean specific help output') do |help|
45
- parsed['help'] = help
46
- end
47
- end
48
- end
49
-
50
- def initialize(logger)
51
- @logger = logger
52
- end
53
-
54
- def parse(args)
55
- results = {}
56
- parser = self.class.parser(results)
57
-
58
- errors = Utils.parse_with_errors(parser, args)
59
-
60
- results['certnames'].each do |certname|
61
- if CERTNAME_BLACKLIST.include?(certname)
62
- errors << " Cannot manage cert named `#{certname}` from " +
63
- "the CLI, if needed use the HTTP API directly"
64
- end
65
- end
66
-
67
- if results['certnames'].empty?
68
- errors << ' At least one certname is required to clean'
69
- end
70
-
71
- errors_were_handled = Utils.handle_errors(@logger, errors, parser.help)
72
-
73
- exit_code = errors_were_handled ? 1 : nil
74
-
75
- return results, exit_code
76
- end
77
-
78
- def run(args)
79
- certnames = args['certnames']
80
- config = args['config']
81
-
82
- if config
83
- errors = FileUtilities.validate_file_paths(config)
84
- return 1 if Utils.handle_errors(@logger, errors)
85
- end
86
-
87
- puppet = PuppetConfig.parse(config)
88
- return 1 if Utils.handle_errors(@logger, puppet.errors)
89
-
90
- passed = clean_certs(certnames, puppet.settings)
91
-
92
- return passed ? 0 : 1
93
- end
94
-
95
- def clean_certs(certnames, settings)
96
- client = HttpClient.new(settings)
97
-
98
- url = client.make_ca_url(settings[:ca_server],
99
- settings[:ca_port],
100
- 'certificate_status')
101
-
102
- results = client.with_connection(url) do |connection|
103
- certnames.map do |certname|
104
- url.resource_name = certname
105
- revoke_result = connection.put(RevokeAction::REQUEST_BODY, url)
106
- revoked = check_revocation(revoke_result, certname)
107
-
108
- cleaned = nil
109
- unless revoked == :error
110
- clean_result = connection.delete(url)
111
- cleaned = check_result(clean_result, certname)
112
- end
113
-
114
- cleaned == :success && [:success, :not_found].include?(revoked)
115
- end
116
- end
117
-
118
- return results.all?
119
- end
120
-
121
- # possibly logs the action, always returns a status symbol 👑
122
- def check_revocation(result, certname)
123
- case result.code
124
- when '200', '204'
125
- @logger.inform "Revoked certificate for #{certname}"
126
- return :success
127
- when '404'
128
- return :not_found
129
- else
130
- @logger.err 'Error:'
131
- @logger.err " Failed revoking certificate for #{certname}"
132
- @logger.err " Received code: #{result.code}, body: #{result.body}"
133
- return :error
134
- end
135
- end
136
-
137
- # logs the action and returns a status symbol 👑
138
- def check_result(result, certname)
139
- case result.code
140
- when '200', '204'
141
- @logger.inform "Cleaned files related to #{certname}"
142
- return :success
143
- when '404'
144
- @logger.err 'Error:'
145
- @logger.err " Could not find files for #{certname}"
146
- return :not_found
147
- else
148
- @logger.err 'Error:'
149
- @logger.err " When cleaning #{certname} received:"
150
- @logger.err " code: #{result.code}"
151
- @logger.err " body: #{result.body.to_s}" if result.body
152
- return :error
153
- end
154
- end
155
- end
156
- end
157
- end
@@ -1,11 +0,0 @@
1
- module Puppetserver
2
- module Ca
3
- module ConfigUtils
4
-
5
- def running_as_root?
6
- !Gem.win_platform? && Process::UID.eid == 0
7
- end
8
-
9
- end
10
- end
11
- end
@@ -1,265 +0,0 @@
1
- require 'puppetserver/ca/utils'
2
- require 'puppetserver/ca/host'
3
- require 'puppetserver/ca/puppet_config'
4
- require 'puppetserver/utils/file_utilities'
5
- require 'puppetserver/utils/http_client'
6
- require 'puppetserver/utils/signing_digest'
7
- require 'json'
8
-
9
- module Puppetserver
10
- module Ca
11
- class CreateAction
12
-
13
- include Puppetserver::Utils
14
-
15
- # Only allow printing ascii characters, excluding /
16
- VALID_CERTNAME = /\A[ -.0-~]+\Z/
17
- CERTNAME_BLACKLIST = %w{--all --config}
18
-
19
- SUMMARY = "Create a new certificate signed by the CA"
20
- BANNER = <<-BANNER
21
- Usage:
22
- puppetserver ca create [--help]
23
- puppetserver ca create [--config] --certname CERTNAME[,ADDLCERTNAME]
24
-
25
- Description:
26
- Creates a new certificate signed by the intermediate CA
27
- and stores generated keys and certs on disk.
28
-
29
- To determine the target location, the default puppet.conf
30
- is consulted for custom values. If using a custom puppet.conf
31
- provide it with the --config flag
32
-
33
- Options:
34
- BANNER
35
- def initialize(logger)
36
- @logger = logger
37
- end
38
-
39
- def self.parser(parsed = {})
40
- parsed['certnames'] = []
41
- OptionParser.new do |opts|
42
- opts.banner = BANNER
43
- opts.on('--certname FOO,BAR', Array,
44
- 'One or more comma separated certnames') do |certs|
45
- parsed['certnames'] += certs
46
- end
47
- opts.on('--help', 'Display this create specific help output') do |help|
48
- parsed['help'] = true
49
- end
50
- opts.on('--config CONF', 'Path to puppet.conf') do |conf|
51
- parsed['config'] = conf
52
- end
53
- end
54
- end
55
-
56
- def parse(args)
57
- results = {}
58
- parser = self.class.parser(results)
59
-
60
- errors = Utils.parse_with_errors(parser, args)
61
-
62
- if results['certnames'].empty?
63
- errors << ' At least one certname is required to create'
64
- else
65
- results['certnames'].each do |certname|
66
- if CERTNAME_BLACKLIST.include?(certname)
67
- errors << " Cannot manage cert named `#{certname}` from " +
68
- "the CLI, if needed use the HTTP API directly"
69
- end
70
-
71
- if certname.match(/\p{Upper}/)
72
- errors << " Certificate names must be lower case"
73
- end
74
-
75
- unless certname =~ VALID_CERTNAME
76
- errors << " Certname #{certname} must not contain unprintable or non-ASCII characters"
77
- end
78
- end
79
- end
80
-
81
- errors_were_handled = Utils.handle_errors(@logger, errors, parser.help)
82
-
83
- exit_code = errors_were_handled ? 1 : nil
84
-
85
- return results, exit_code
86
- end
87
-
88
- def run(input)
89
- certnames = input['certnames']
90
- config_path = input['config']
91
-
92
- # Validate config_path provided
93
- if config_path
94
- errors = FileUtilities.validate_file_paths(config_path)
95
- return 1 if Utils.handle_errors(@logger, errors)
96
- end
97
-
98
- # Load, resolve, and validate puppet config settings
99
- puppet = PuppetConfig.parse(config_path)
100
- return 1 if Utils.handle_errors(@logger, puppet.errors)
101
-
102
- # Load most secure signing digest we can for csr signing.
103
- signer = SigningDigest.new
104
- return 1 if Utils.handle_errors(@logger, signer.errors)
105
-
106
- # Make sure we have all the directories where we will be writing files
107
- FileUtilities.ensure_dir(puppet.settings[:certdir])
108
- FileUtilities.ensure_dir(puppet.settings[:privatekeydir])
109
- FileUtilities.ensure_dir(puppet.settings[:publickeydir])
110
-
111
- # Generate and save certs and associated keys
112
- all_passed = generate_certs(certnames, puppet.settings, signer.digest)
113
- return all_passed ? 0 : 1
114
- end
115
-
116
- # Create csrs and keys, then submit them to CA, request for the CA to sign
117
- # them, download the signed certificates from the CA, and finally save
118
- # the signed certs and associated keys. Returns true if all certs were
119
- # successfully created and saved.
120
- def generate_certs(certnames, settings, digest)
121
- passed = certnames.map do |certname|
122
- key, csr = generate_key_csr(certname, settings, digest)
123
- return false unless submit_certificate_request(certname, csr.to_s, settings)
124
- return false unless sign_cert(certname, settings)
125
- if download_cert(certname, settings)
126
- save_keys(key, certname, settings)
127
- true
128
- else
129
- false
130
- end
131
- end
132
- passed.all?
133
- end
134
-
135
- def generate_key_csr(certname, settings, digest)
136
- host = Puppetserver::Ca::Host.new(digest)
137
- private_key = host.create_private_key(settings[:keylength])
138
- csr = host.create_csr(certname, private_key)
139
-
140
- return private_key, csr
141
- end
142
-
143
- def http_client(settings)
144
- @client ||= HttpClient.new(settings)
145
- end
146
-
147
- # Make an HTTP request to submit certificate requests to CA
148
- # @param certname [String] the name of the certificate to fetch
149
- # @param csr [String] string version of a OpenSSL::X509::Request
150
- # @param settings [Hash] a hash of config settings
151
- # @return [Boolean] success of all csrs being submitted to CA
152
- def submit_certificate_request(certname, csr, settings)
153
- client = http_client(settings)
154
- url = client.make_ca_url(settings[:ca_server],
155
- settings[:ca_port],
156
- 'certificate_request',
157
- certname)
158
-
159
- client.with_connection(url) do |connection|
160
- result = connection.put(csr, url)
161
- check_submit_result(result, certname)
162
- end
163
- end
164
-
165
- def check_submit_result(result, certname)
166
- case result.code
167
- when '200', '204'
168
- @logger.inform "Successfully submitted certificate request for #{certname}"
169
- return true
170
- else
171
- @logger.err 'Error:'
172
- @logger.err " When certificate request submitted for #{certname}:"
173
- @logger.err " code: #{result.code}"
174
- @logger.err " body: #{result.body.to_s}" if result.body
175
- return false
176
- end
177
- end
178
-
179
- # Make an HTTP request to CA to sign the named certificates
180
- # @param certname [String] the name of the certificate to have signed
181
- # @param settings [Hash] a hash of config settings
182
- # @return [Boolean] the success of certificates being signed
183
- def sign_cert(certname, settings)
184
- client = http_client(settings)
185
-
186
- url = client.make_ca_url(settings[:ca_server],
187
- settings[:ca_port],
188
- 'certificate_status',
189
- certname)
190
-
191
- client.with_connection(url) do |connection|
192
- body = JSON.dump({desired_state: 'signed'})
193
- result = connection.put(body, url)
194
- check_sign_result(result, certname)
195
- end
196
- end
197
-
198
- def check_sign_result(result, certname)
199
- case result.code
200
- when '204'
201
- @logger.inform "Successfully signed certificate request for #{certname}"
202
- return true
203
- else
204
- @logger.err 'Error:'
205
- @logger.err " When signing request submitted for #{certname}:"
206
- @logger.err " code: #{result.code}"
207
- @logger.err " body: #{result.body.to_s}" if result.body
208
- return false
209
- end
210
- end
211
-
212
- # Make an HTTP request to fetch the named certificates from CA
213
- # @param certname [String] the name of the certificate to fetch
214
- # @param settings [Hash] a hash of config settings
215
- # @return [Boolean] the success of certificate being downloaded
216
- def download_cert(certname, settings)
217
- client = http_client(settings)
218
- url = client.make_ca_url(settings[:ca_server],
219
- settings[:ca_port],
220
- 'certificate',
221
- certname)
222
- client.with_connection(url) do |connection|
223
- result = connection.get(url)
224
- if downloaded = check_download_result(result, certname)
225
- save_file(result.body, certname, settings[:certdir], "Certificate")
226
- @logger.inform "Successfully downloaded and saved certificate #{certname} to #{settings[:certdir]}/#{certname}.pem"
227
- end
228
- downloaded
229
- end
230
- end
231
-
232
- def check_download_result(result, certname)
233
- case result.code
234
- when '200'
235
- return true
236
- when '404'
237
- @logger.err 'Error:'
238
- @logger.err " Signed certificate #{certname} could not be found on the CA"
239
- return false
240
- else
241
- @logger.err 'Error:'
242
- @logger.err " When download requested for certificate #{certname}:"
243
- @logger.err " code: #{result.code}"
244
- @logger.err " body: #{result.body.to_s}" if result.body
245
- return false
246
- end
247
- end
248
-
249
- def save_keys(key, certname, settings)
250
- public_key = key.public_key
251
- save_file(key, certname, settings[:privatekeydir], "Private key")
252
- save_file(public_key, certname, settings[:publickeydir], "Public key")
253
- @logger.inform "Successfully saved private key for #{certname} to #{settings[:privatekeydir]}/#{certname}.pem"
254
- @logger.inform "Successfully saved public key for #{certname} to #{settings[:publickeydir]}/#{certname}.pem"
255
- end
256
-
257
-
258
- def save_file(content, certname, dir, type)
259
- location = File.join(dir, "#{certname}.pem")
260
- @logger.warn "#{type} #{certname}.pem already exists, overwriting" if File.exist?(location)
261
- FileUtilities.write_file(location, content, 0640)
262
- end
263
- end
264
- end
265
- end
@@ -1,227 +0,0 @@
1
- require 'optparse'
2
- require 'openssl'
3
- require 'puppetserver/utils/file_utilities'
4
- require 'puppetserver/ca/host'
5
- require 'puppetserver/utils/signing_digest'
6
-
7
- module Puppetserver
8
- module Ca
9
- class GenerateAction
10
- include Puppetserver::Utils
11
-
12
- CA_EXTENSIONS = [
13
- ["basicConstraints", "CA:TRUE", true],
14
- ["keyUsage", "keyCertSign, cRLSign", true],
15
- ["subjectKeyIdentifier", "hash", false],
16
- ["authorityKeyIdentifier", "keyid:always", false]
17
- ].freeze
18
-
19
- # Make the certificate valid as of yesterday, because so many people's
20
- # clocks are out of sync. This gives one more day of validity than people
21
- # might expect, but is better than making every person who has a messed up
22
- # clock fail, and better than having every cert we generate expire a day
23
- # before the user expected it to when they asked for "one year".
24
- CERT_VALID_FROM = (Time.now - (60*60*24)).freeze
25
-
26
- SUMMARY = "Generate a root and intermediate signing CA for Puppet Server"
27
- BANNER = <<-BANNER
28
- Usage:
29
- puppetserver ca generate [--help]
30
- puppetserver ca generate [--config PATH]
31
-
32
- Description:
33
- Generate a root and intermediate signing CA for Puppet Server
34
- and store generated CA keys, certs, and crls on disk.
35
-
36
- To determine the target location, the default puppet.conf
37
- is consulted for custom values. If using a custom puppet.conf
38
- provide it with the --config flag
39
-
40
- Options:
41
- BANNER
42
-
43
- def initialize(logger)
44
- @logger = logger
45
- end
46
-
47
- def run(input)
48
- # Validate config_path provided
49
- config_path = input['config']
50
- if config_path
51
- errors = FileUtilities.validate_file_paths(config_path)
52
- return 1 if Utils.handle_errors(@logger, errors)
53
- end
54
-
55
- # Load, resolve, and validate puppet config settings
56
- puppet = PuppetConfig.parse(config_path)
57
- return 1 if Utils.handle_errors(@logger, puppet.errors)
58
-
59
- # Load most secure signing digest we can for cers/crl/csr signing.
60
- signer = SigningDigest.new
61
- return 1 if Utils.handle_errors(@logger, signer.errors)
62
-
63
- # Generate root and intermediate ca and put all the certificates, crls,
64
- # and keys where they should go.
65
- generate_root_and_intermediate_ca(puppet.settings, signer.digest)
66
-
67
- # Puppet's internal CA expects these file to exist.
68
- FileUtilities.ensure_file(puppet.settings[:serial], "001", 0640)
69
- FileUtilities.ensure_file(puppet.settings[:cert_inventory], "", 0640)
70
-
71
- @logger.inform "Generation succeeded. Find your files in #{puppet.settings[:cadir]}"
72
- return 0
73
- end
74
-
75
- def generate_root_and_intermediate_ca(settings, signing_digest)
76
- valid_until = Time.now + settings[:ca_ttl]
77
- host = Puppetserver::Ca::Host.new(signing_digest)
78
-
79
- root_key = host.create_private_key(settings[:keylength])
80
- root_cert = self_signed_ca(root_key, settings[:root_ca_name], valid_until, signing_digest)
81
- root_crl = create_crl_for(root_cert, root_key, valid_until, signing_digest)
82
-
83
- int_key = host.create_private_key(settings[:keylength])
84
- int_csr = host.create_csr(settings[:ca_name], int_key)
85
- int_cert = sign_intermediate(root_key, root_cert, int_csr, valid_until, signing_digest)
86
- int_crl = create_crl_for(int_cert, int_key, valid_until, signing_digest)
87
-
88
- FileUtilities.ensure_dir(settings[:cadir])
89
-
90
- file_properties = [
91
- [settings[:cacert], [int_cert, root_cert]],
92
- [settings[:cakey], int_key],
93
- [settings[:rootkey], root_key],
94
- [settings[:cacrl], [int_crl, root_crl]]
95
- ]
96
-
97
- file_properties.each do |location, content|
98
- @logger.warn "#{location} exists, overwriting" if File.exist?(location)
99
- FileUtilities.write_file(location, content, 0640)
100
- end
101
- end
102
-
103
- def self_signed_ca(key, name, valid_until, signing_digest)
104
- cert = OpenSSL::X509::Certificate.new
105
-
106
- cert.public_key = key.public_key
107
- cert.subject = OpenSSL::X509::Name.new([["CN", name]])
108
- cert.issuer = cert.subject
109
- cert.version = 2
110
- cert.serial = 1
111
-
112
- cert.not_before = CERT_VALID_FROM
113
- cert.not_after = valid_until
114
-
115
- ef = extension_factory_for(cert, cert)
116
- CA_EXTENSIONS.each do |ext|
117
- extension = ef.create_extension(*ext)
118
- cert.add_extension(extension)
119
- end
120
-
121
- cert.sign(key, signing_digest)
122
-
123
- cert
124
- end
125
-
126
- def extension_factory_for(ca, cert = nil)
127
- ef = OpenSSL::X509::ExtensionFactory.new
128
- ef.issuer_certificate = ca
129
- ef.subject_certificate = cert if cert
130
-
131
- ef
132
- end
133
-
134
- def create_crl_for(ca_cert, ca_key, valid_until, signing_digest)
135
- crl = OpenSSL::X509::CRL.new
136
- crl.version = 1
137
- crl.issuer = ca_cert.subject
138
-
139
- ef = extension_factory_for(ca_cert)
140
- crl.add_extension(
141
- ef.create_extension(["authorityKeyIdentifier", "keyid:always", false]))
142
- crl.add_extension(
143
- OpenSSL::X509::Extension.new("crlNumber", OpenSSL::ASN1::Integer(0)))
144
-
145
- crl.last_update = CERT_VALID_FROM
146
- crl.next_update = valid_until
147
- crl.sign(ca_key, signing_digest)
148
-
149
- crl
150
- end
151
-
152
- def sign_intermediate(ca_key, ca_cert, csr, valid_until, signing_digest)
153
- cert = OpenSSL::X509::Certificate.new
154
-
155
- cert.public_key = csr.public_key
156
- cert.subject = csr.subject
157
- cert.issuer = ca_cert.subject
158
- cert.version = 2
159
- cert.serial = 2
160
-
161
- cert.not_before = CERT_VALID_FROM
162
- cert.not_after = valid_until
163
-
164
- ef = extension_factory_for(ca_cert, cert)
165
- CA_EXTENSIONS.each do |ext|
166
- extension = ef.create_extension(*ext)
167
- cert.add_extension(extension)
168
- end
169
- cert.sign(ca_key, signing_digest)
170
-
171
- cert
172
- end
173
-
174
- def parse(cli_args)
175
- parser, inputs, unparsed = parse_inputs(cli_args)
176
-
177
- if !unparsed.empty?
178
- @logger.err 'Error:'
179
- @logger.err 'Unknown arguments or flags:'
180
- unparsed.each do |arg|
181
- @logger.err " #{arg}"
182
- end
183
-
184
- @logger.err ''
185
- @logger.err parser.help
186
-
187
- exit_code = 1
188
- else
189
- exit_code = nil
190
- end
191
-
192
- return inputs, exit_code
193
- end
194
-
195
- def parse_inputs(inputs)
196
- parsed = {}
197
- unparsed = []
198
-
199
- parser = self.class.parser(parsed)
200
-
201
- begin
202
- parser.order!(inputs) do |nonopt|
203
- unparsed << nonopt
204
- end
205
- rescue OptionParser::ParseError => e
206
- unparsed += e.args
207
- unparsed << inputs.shift unless inputs.first =~ /^-{1,2}/
208
- retry
209
- end
210
-
211
- return parser, parsed, unparsed
212
- end
213
-
214
- def self.parser(parsed = {})
215
- OptionParser.new do |opts|
216
- opts.banner = BANNER
217
- opts.on('--help', 'Display this generate specific help output') do |help|
218
- parsed['help'] = true
219
- end
220
- opts.on('--config CONF', 'Path to puppet.conf') do |conf|
221
- parsed['config'] = conf
222
- end
223
- end
224
- end
225
- end
226
- end
227
- end