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,83 +0,0 @@
1
- require 'hocon'
2
- require 'puppetserver/ca/config_utils'
3
-
4
- module Puppetserver
5
- module Ca
6
- # Provides an interface for querying Puppetserver settings w/o loading
7
- # Puppetserver or any TK config service. Uses the ruby-hocon gem for parsing.
8
- class PuppetserverConfig
9
-
10
- include Puppetserver::Ca::ConfigUtils
11
-
12
- def self.parse(config_path = nil)
13
- instance = new(config_path)
14
- instance.load
15
-
16
- return instance
17
- end
18
-
19
- attr_reader :errors, :settings
20
-
21
- def initialize(supplied_config_path = nil)
22
- @using_default_location = !supplied_config_path
23
- @config_path = supplied_config_path || "/etc/puppetlabs/puppetserver/conf.d/ca.conf"
24
-
25
- @settings = nil
26
- @errors = []
27
- end
28
-
29
- # Populate this config object with the CA-related settings
30
- def load
31
- if explicitly_given_config_file_or_default_config_exists?
32
- begin
33
- results = Hocon.load(@config_path)
34
- rescue Hocon::ConfigError => e
35
- errors << e.message
36
- end
37
- end
38
-
39
- overrides = results || {}
40
- @settings = supply_defaults(overrides).freeze
41
- end
42
-
43
- private
44
-
45
- # Return the correct confdir. We check for being root on *nix,
46
- # else the user path. We do not include a check for running
47
- # as Adminstrator since non-development scenarios for Puppet Server
48
- # on Windows are unsupported.
49
- # Note that Puppet Server runs as the [pe-]puppet user but to
50
- # start/stop it you must be root.
51
- def user_specific_ca_dir
52
- if running_as_root?
53
- '/etc/puppetlabs/puppetserver/ca'
54
- else
55
- "#{ENV['HOME']}/.puppetlabs/etc/puppetserver/ca"
56
- end
57
- end
58
-
59
- # Supply defaults for any CA settings not present in the config file
60
- # @param [Hash] overrides setting names and values loaded from the config file,
61
- # for overriding the defaults
62
- # @return [Hash] CA-related settings
63
- def supply_defaults(overrides = {})
64
- ca_settings = overrides['certificate-authority'] || {}
65
- settings = {}
66
-
67
- cadir = settings[:cadir] = ca_settings.fetch('cadir', user_specific_ca_dir)
68
-
69
- settings[:cacert] = ca_settings.fetch('cacert', "#{cadir}/ca_crt.pem")
70
- settings[:cakey] = ca_settings.fetch('cakey', "#{cadir}/ca_key.pem")
71
- settings[:cacrl] = ca_settings.fetch('cacrl', "#{cadir}/ca_crl.pem")
72
- settings[:serial] = ca_settings.fetch('serial', "#{cadir}/serial")
73
- settings[:cert_inventory] = ca_settings.fetch('cert-inventory', "#{cadir}/inventory.txt")
74
-
75
- return settings
76
- end
77
-
78
- def explicitly_given_config_file_or_default_config_exists?
79
- !@using_default_location || File.exist?(@config_path)
80
- end
81
- end
82
- end
83
- end
@@ -1,136 +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
-
6
- require 'optparse'
7
- require 'json'
8
-
9
- module Puppetserver
10
- module Ca
11
- class RevokeAction
12
-
13
- include Puppetserver::Utils
14
-
15
- REQUEST_BODY = JSON.dump({ desired_state: 'revoked' })
16
- CERTNAME_BLACKLIST = %w{--all --config}
17
-
18
- SUMMARY = 'Revoke a given certificate'
19
- BANNER = <<-BANNER
20
- Usage:
21
- puppetserver ca revoke [--help|--version]
22
- puppetserver ca revoke [--config] --certname CERTNAME[,ADDLCERTNAME]
23
-
24
- Description:
25
- Given one or more valid certnames, instructs the CA to revoke them over
26
- HTTPS using the local agent's PKI
27
-
28
- Options:
29
- BANNER
30
-
31
- def self.parser(parsed = {})
32
- parsed['certnames'] = []
33
- OptionParser.new do |o|
34
- o.banner = BANNER
35
- o.on('--certname foo,bar', Array,
36
- 'One or more comma separated certnames') do |certs|
37
- parsed['certnames'] += certs
38
- end
39
- o.on('--config PUPPET.CONF', 'Custom path to puppet.conf') do |conf|
40
- parsed['config'] = conf
41
- end
42
- o.on('--help', 'Displays this revoke specific help output') do |help|
43
- parsed['help'] = help
44
- end
45
- end
46
- end
47
-
48
- def initialize(logger)
49
- @logger = logger
50
- end
51
-
52
- def parse(args)
53
- results = {}
54
- parser = self.class.parser(results)
55
-
56
- errors = Utils.parse_with_errors(parser, args)
57
-
58
- results['certnames'].each do |certname|
59
- if CERTNAME_BLACKLIST.include?(certname)
60
- errors << " Cannot manage cert named `#{certname}` from " +
61
- "the CLI, if needed use the HTTP API directly"
62
- end
63
- end
64
-
65
- if results['certnames'].empty?
66
- errors << ' At least one certname is required to revoke'
67
- end
68
-
69
- errors_were_handled = Utils.handle_errors(@logger, errors, parser.help)
70
-
71
- # if there is an exit_code then Cli will return it early, so we only
72
- # return an exit_code if there's an error
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 = revoke_certs(certnames, puppet.settings)
91
-
92
- return passed ? 0 : 1
93
- end
94
-
95
- def revoke_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 will be a list of trues & falses based on the success
103
- # of revocations
104
- results = client.with_connection(url) do |connection|
105
- certnames.map do |certname|
106
- url.resource_name = certname
107
- result = connection.put(REQUEST_BODY, url)
108
-
109
- check_result(result, certname)
110
- end
111
- end
112
-
113
- return results.all?
114
- end
115
-
116
- # logs the action and returns a boolean for success/failure
117
- def check_result(result, certname)
118
- case result.code
119
- when '200', '204'
120
- @logger.inform "Revoked certificate for #{certname}"
121
- return true
122
- when '404'
123
- @logger.err 'Error:'
124
- @logger.err " Could not find certificate for #{certname}"
125
- return false
126
- else
127
- @logger.err 'Error:'
128
- @logger.err " When revoking #{certname} received:"
129
- @logger.err " code: #{result.code}"
130
- @logger.err " body: #{result.body.to_s}" if result.body
131
- return false
132
- end
133
- end
134
- end
135
- end
136
- end
@@ -1,190 +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 'optparse'
6
- require 'openssl'
7
- require 'net/https'
8
- require 'json'
9
-
10
- module Puppetserver
11
- module Ca
12
- class SignAction
13
-
14
- include Puppetserver::Utils
15
-
16
- SUMMARY = 'Sign a given certificate'
17
- BANNER = <<-BANNER
18
- Usage:
19
- puppetserver ca sign [--help|--version]
20
- puppetserver ca sign [--config] --certname CERTNAME[,CERTNAME]
21
- puppetserver ca sign --all
22
-
23
- Description:
24
- Given a comma-separated list of valid certnames, instructs the CA to sign each cert.
25
-
26
- Options:
27
- BANNER
28
- BODY = JSON.dump({desired_state: 'signed'})
29
-
30
- def self.parser(parsed = {})
31
- OptionParser.new do |opts|
32
- opts.banner = BANNER
33
- opts.on('--certname x,y,z', Array, 'the name(s) of the cert(s) to be signed') do |cert|
34
- parsed['certname'] = cert
35
- end
36
- opts.on('--config PUPPET.CONF', 'Custom path to Puppet\'s config file') do |conf|
37
- parsed['config'] = conf
38
- end
39
- opts.on('--help', 'Display this command specific help output') do |help|
40
- parsed['help'] = true
41
- end
42
- opts.on('--version', 'Output the version') do |v|
43
- parsed['version'] = true
44
- end
45
- opts.on('--all', 'Operate on all certnames') do |a|
46
- parsed['all'] = true
47
- end
48
- end
49
- end
50
-
51
- def initialize(logger)
52
- @logger = logger
53
- end
54
-
55
- def run(input)
56
- config = input['config']
57
-
58
- if config
59
- errors = FileUtilities.validate_file_paths(config)
60
- return 1 if Utils.handle_errors(@logger, errors)
61
- end
62
-
63
- puppet = PuppetConfig.parse(config)
64
- return 1 if Utils.handle_errors(@logger, puppet.errors)
65
-
66
- if input['all']
67
- requested_certnames = get_all_pending_certs(puppet.settings)
68
- if requested_certnames.nil?
69
- return 1
70
- end
71
- else
72
- requested_certnames = input['certname']
73
- end
74
-
75
- success = sign_requested_certs(requested_certnames, puppet.settings)
76
- return success ? 0 : 1
77
- end
78
-
79
- def http_client(settings)
80
- @client ||= HttpClient.new(settings)
81
- end
82
-
83
- def get_certificate_statuses(settings)
84
- client = http_client(settings)
85
- url = client.make_ca_url(settings[:ca_server],
86
- settings[:ca_port],
87
- 'certificate_statuses',
88
- 'any_key')
89
- client.with_connection(url) do |connection|
90
- connection.get(url)
91
- end
92
- end
93
-
94
- def sign_certs(certnames,settings)
95
- results = {}
96
- client = http_client(settings)
97
- url = client.make_ca_url(settings[:ca_server],
98
- settings[:ca_port],
99
- 'certificate_status')
100
- client.with_connection(url) do |connection|
101
- certnames.each do |certname|
102
- url.resource_name = certname
103
- results[certname] = connection.put(BODY, url)
104
- end
105
- end
106
- return results
107
- end
108
-
109
- def get_all_certs(settings)
110
- result = get_certificate_statuses(settings)
111
-
112
- unless result.code == 200
113
- @logger.err 'Error:'
114
- @logger.err " #{result.inspect}"
115
- return nil
116
- end
117
- return result
118
- end
119
-
120
- def select_pending_certs(get_result)
121
- requested_certnames = JSON.parse(get_result).select{|e| e["state"] == "requested"}.map{|e| e["name"]}
122
-
123
- if requested_certnames.empty?
124
- @logger.err 'Error:'
125
- @logger.err " No waiting certificate requests to sign"
126
- return nil
127
- end
128
-
129
- return requested_certnames
130
- end
131
-
132
- def get_all_pending_certs(settings)
133
- result = get_all_certs(settings)
134
- if result
135
- select_pending_certs(result.body)
136
- end
137
- end
138
-
139
- def sign_requested_certs(certnames,settings)
140
- success = true
141
- results = sign_certs(certnames, settings)
142
- results.each do |certname, result|
143
- case result.code
144
- when '204'
145
- @logger.inform "Signed certificate for #{certname}"
146
- when '404'
147
- @logger.err 'Error:'
148
- @logger.err " Could not find certificate for #{certname}"
149
- success = false
150
- else
151
- @logger.err 'Error:'
152
- @logger.err " When download requested for #{result.inspect}"
153
- @logger.err " code: #{result.code}"
154
- @logger.err " body: #{result.body.to_s}" if result.body
155
- success = false
156
- end
157
- end
158
- return success
159
- end
160
-
161
- def check_flag_usage(results)
162
- if results['certname'] && results['all']
163
- '--all and --certname cannot be used together'
164
- elsif !results['certname'] && !results['all']
165
- 'No arguments given'
166
- elsif results['certname'] && results['certname'].include?('--all')
167
- 'Cannot use --all with --certname. If you actually have a certificate request ' +
168
- 'for a certifcate named --all, you need to use the HTTP API.'
169
- end
170
- end
171
-
172
- def parse(args)
173
- results = {}
174
- parser = self.class.parser(results)
175
-
176
- errors = Utils.parse_with_errors(parser, args)
177
-
178
- if check_flag_usage(results)
179
- errors << check_flag_usage(results)
180
- end
181
-
182
- errors_were_handled = Utils.handle_errors(@logger, errors, parser.help)
183
-
184
- exit_code = errors_were_handled ? 1 : nil
185
-
186
- return results, exit_code
187
- end
188
- end
189
- end
190
- end
@@ -1,80 +0,0 @@
1
- module Puppetserver
2
- module Ca
3
- module Utils
4
- def self.parse_without_raising(parser, args)
5
- all, not_flags, malformed_flags, unknown_flags = [], [], [], []
6
-
7
- begin
8
- # OptionParser calls this block when it finds a value that doesn't
9
- # start with one or two dashes and doesn't follow a flag that
10
- # consumes a value.
11
- parser.order!(args) do |not_flag|
12
- not_flags << not_flag
13
- all << not_flag
14
- end
15
- rescue OptionParser::MissingArgument => e
16
- malformed_flags += e.args
17
- all += e.args
18
-
19
- retry
20
- rescue OptionParser::ParseError => e
21
- flag = e.args.first
22
- unknown_flags << flag
23
- all << flag
24
-
25
- if does_not_contain_argument(flag) &&
26
- args.first &&
27
- next_arg_is_not_another_flag(args.first)
28
-
29
- value = args.shift
30
- unknown_flags << value
31
- all << value
32
- end
33
-
34
- retry
35
- end
36
-
37
- return all, not_flags, malformed_flags, unknown_flags
38
- end
39
-
40
- def self.parse_with_errors(parser, args)
41
- errors = []
42
-
43
- _, non_flags, malformed_flags, unknown_flags = parse_without_raising(parser, args)
44
-
45
- malformed_flags.each {|f| errors << " Missing argument to flag `#{f}`" }
46
- unknown_flags.each {|f| errors << " Unknown flag or argument `#{f}`" }
47
- non_flags.each {|f| errors << " Unknown input `#{f}`" }
48
-
49
- errors
50
- end
51
-
52
- def self.handle_errors(log, errors, usage = nil)
53
- unless errors.empty?
54
- log.err 'Error:'
55
- errors.each {|e| log.err e }
56
-
57
- if usage
58
- log.err ''
59
- log.err usage
60
- end
61
-
62
- return true
63
- else
64
- return false
65
- end
66
- end
67
-
68
- private
69
-
70
- # eg. --flag=argument-to-flag
71
- def self.does_not_contain_argument(flag)
72
- !flag.include?('=')
73
- end
74
-
75
- def self.next_arg_is_not_another_flag(maybe_an_arg)
76
- !maybe_an_arg.start_with?('-')
77
- end
78
- end
79
- end
80
- end