openvoxserver-ca 3.0.0.pre.rc1
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.
- checksums.yaml +7 -0
- data/.github/dependabot.yml +17 -0
- data/.github/release.yml +41 -0
- data/.github/workflows/gem_release.yaml +106 -0
- data/.github/workflows/prepare_release.yml +28 -0
- data/.github/workflows/release.yml +28 -0
- data/.github/workflows/unit_tests.yaml +45 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +16 -0
- data/CHANGELOG.md +15 -0
- data/CODEOWNERS +4 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +15 -0
- data/Gemfile +20 -0
- data/LICENSE +202 -0
- data/README.md +118 -0
- data/Rakefile +30 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/puppetserver-ca +10 -0
- data/lib/puppetserver/ca/action/clean.rb +109 -0
- data/lib/puppetserver/ca/action/delete.rb +286 -0
- data/lib/puppetserver/ca/action/enable.rb +140 -0
- data/lib/puppetserver/ca/action/generate.rb +330 -0
- data/lib/puppetserver/ca/action/import.rb +196 -0
- data/lib/puppetserver/ca/action/list.rb +253 -0
- data/lib/puppetserver/ca/action/migrate.rb +97 -0
- data/lib/puppetserver/ca/action/prune.rb +289 -0
- data/lib/puppetserver/ca/action/revoke.rb +108 -0
- data/lib/puppetserver/ca/action/setup.rb +188 -0
- data/lib/puppetserver/ca/action/sign.rb +146 -0
- data/lib/puppetserver/ca/certificate_authority.rb +418 -0
- data/lib/puppetserver/ca/cli.rb +145 -0
- data/lib/puppetserver/ca/config/puppet.rb +309 -0
- data/lib/puppetserver/ca/config/puppetserver.rb +84 -0
- data/lib/puppetserver/ca/errors.rb +40 -0
- data/lib/puppetserver/ca/host.rb +176 -0
- data/lib/puppetserver/ca/local_certificate_authority.rb +304 -0
- data/lib/puppetserver/ca/logger.rb +49 -0
- data/lib/puppetserver/ca/stub.rb +17 -0
- data/lib/puppetserver/ca/utils/cli_parsing.rb +67 -0
- data/lib/puppetserver/ca/utils/config.rb +61 -0
- data/lib/puppetserver/ca/utils/file_system.rb +109 -0
- data/lib/puppetserver/ca/utils/http_client.rb +232 -0
- data/lib/puppetserver/ca/utils/inventory.rb +84 -0
- data/lib/puppetserver/ca/utils/signing_digest.rb +27 -0
- data/lib/puppetserver/ca/version.rb +5 -0
- data/lib/puppetserver/ca/x509_loader.rb +170 -0
- data/lib/puppetserver/ca.rb +7 -0
- data/openvoxserver-ca.gemspec +31 -0
- data/tasks/spec.rake +15 -0
- data/tasks/vox.rake +19 -0
- metadata +154 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
require 'puppetserver/ca/certificate_authority'
|
4
|
+
require 'puppetserver/ca/config/puppet'
|
5
|
+
require 'puppetserver/ca/utils/cli_parsing'
|
6
|
+
require 'puppetserver/ca/utils/file_system'
|
7
|
+
|
8
|
+
module Puppetserver
|
9
|
+
module Ca
|
10
|
+
module Action
|
11
|
+
class Revoke
|
12
|
+
|
13
|
+
include Puppetserver::Ca::Utils
|
14
|
+
|
15
|
+
CERTNAME_BLOCKLIST = %w{--all --config}
|
16
|
+
|
17
|
+
SUMMARY = 'Revoke certificate(s)'
|
18
|
+
BANNER = <<-BANNER
|
19
|
+
Usage:
|
20
|
+
puppetserver ca revoke [--help]
|
21
|
+
puppetserver ca revoke [--config] --certname NAME[,NAME]
|
22
|
+
|
23
|
+
Description:
|
24
|
+
Given one or more valid certnames, instructs the CA to revoke them over
|
25
|
+
HTTPS using the local agent's PKI
|
26
|
+
|
27
|
+
Options:
|
28
|
+
BANNER
|
29
|
+
|
30
|
+
def self.parser(parsed = {})
|
31
|
+
parsed['certnames'] = []
|
32
|
+
OptionParser.new do |o|
|
33
|
+
o.banner = BANNER
|
34
|
+
o.on('--certname NAME[,NAME]', Array,
|
35
|
+
'One or more comma separated certnames') do |certs|
|
36
|
+
parsed['certnames'] += certs
|
37
|
+
end
|
38
|
+
o.on('--config CONF', 'Custom path to puppet.conf') do |conf|
|
39
|
+
parsed['config'] = conf
|
40
|
+
end
|
41
|
+
o.on('--help', 'Displays this revoke specific help output') do |help|
|
42
|
+
parsed['help'] = true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(logger)
|
48
|
+
@logger = logger
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse(args)
|
52
|
+
results = {}
|
53
|
+
parser = self.class.parser(results)
|
54
|
+
|
55
|
+
errors = CliParsing.parse_with_errors(parser, args)
|
56
|
+
|
57
|
+
results['certnames'].each do |certname|
|
58
|
+
if CERTNAME_BLOCKLIST.include?(certname)
|
59
|
+
errors << " Cannot manage cert named `#{certname}` from " +
|
60
|
+
"the CLI, if needed use the HTTP API directly"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
if results['certnames'].empty?
|
65
|
+
errors << ' At least one certname is required to revoke'
|
66
|
+
end
|
67
|
+
|
68
|
+
errors_were_handled = Errors.handle_with_usage(@logger, errors, parser.help)
|
69
|
+
|
70
|
+
# if there is an exit_code then Cli will return it early, so we only
|
71
|
+
# return an exit_code if there's an error
|
72
|
+
exit_code = errors_were_handled ? 1 : nil
|
73
|
+
|
74
|
+
return results, exit_code
|
75
|
+
end
|
76
|
+
|
77
|
+
def run(args)
|
78
|
+
certnames = args['certnames']
|
79
|
+
config = args['config']
|
80
|
+
|
81
|
+
if config
|
82
|
+
errors = FileSystem.validate_file_paths(config)
|
83
|
+
return 1 if Errors.handle_with_usage(@logger, errors)
|
84
|
+
end
|
85
|
+
|
86
|
+
puppet = Config::Puppet.parse(config, @logger)
|
87
|
+
return 1 if Errors.handle_with_usage(@logger, puppet.errors)
|
88
|
+
|
89
|
+
result = revoke_certs(certnames, puppet.settings)
|
90
|
+
|
91
|
+
case result
|
92
|
+
when :success
|
93
|
+
return 0
|
94
|
+
when :invalid
|
95
|
+
return 24
|
96
|
+
when :not_found, :error
|
97
|
+
return 1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def revoke_certs(certnames, settings)
|
102
|
+
ca = Puppetserver::Ca::CertificateAuthority.new(@logger, settings)
|
103
|
+
ca.revoke_certs(certnames)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
require 'puppetserver/ca/config/puppet'
|
4
|
+
require 'puppetserver/ca/errors'
|
5
|
+
require 'puppetserver/ca/local_certificate_authority'
|
6
|
+
require 'puppetserver/ca/utils/config'
|
7
|
+
require 'puppetserver/ca/utils/cli_parsing'
|
8
|
+
require 'puppetserver/ca/utils/file_system'
|
9
|
+
require 'puppetserver/ca/utils/signing_digest'
|
10
|
+
|
11
|
+
module Puppetserver
|
12
|
+
module Ca
|
13
|
+
module Action
|
14
|
+
class Setup
|
15
|
+
include Puppetserver::Ca::Utils
|
16
|
+
|
17
|
+
SUMMARY = "Setup a self-signed CA chain for Puppet Server"
|
18
|
+
BANNER = <<-BANNER
|
19
|
+
Usage:
|
20
|
+
puppetserver ca setup [--help]
|
21
|
+
puppetserver ca setup [--config PATH] [--subject-alt-names NAME[,NAME]]
|
22
|
+
[--certname NAME] [--ca-name NAME] [--root-ca-name NAME]
|
23
|
+
|
24
|
+
Description:
|
25
|
+
Setup a root and intermediate signing CA for Puppet Server
|
26
|
+
and store generated CA keys, certs, crls, and associated
|
27
|
+
server related files on disk.
|
28
|
+
|
29
|
+
The `--subject-alt-names` flag can be used to add SANs to the
|
30
|
+
certificate generated for the Puppet server. Multiple names can be
|
31
|
+
listed as a comma separated string. These can be either DNS names or
|
32
|
+
IP addresses, differentiated by prefixes: `DNS:foo.bar.com,IP:123.456.789`.
|
33
|
+
Names with no prefix will be treated as DNS names.
|
34
|
+
|
35
|
+
Options:
|
36
|
+
BANNER
|
37
|
+
|
38
|
+
def initialize(logger)
|
39
|
+
@logger = logger
|
40
|
+
end
|
41
|
+
|
42
|
+
def run(input)
|
43
|
+
# Validate config_path provided
|
44
|
+
config_path = input['config']
|
45
|
+
if config_path
|
46
|
+
errors = FileSystem.validate_file_paths(config_path)
|
47
|
+
return 1 if Errors.handle_with_usage(@logger, errors)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Load, resolve, and validate puppet config settings
|
51
|
+
settings_overrides = {}
|
52
|
+
settings_overrides[:certname] = input['certname'] unless input['certname'].empty?
|
53
|
+
settings_overrides[:ca_name] = input['ca-name'] unless input['ca-name'].empty?
|
54
|
+
settings_overrides[:root_ca_name] = input['root-ca-name'] unless input['root-ca-name'].empty?
|
55
|
+
# Since puppet expects the key to be called 'dns_alt_names', we need to use that here
|
56
|
+
# to ensure that the overriding works correctly.
|
57
|
+
settings_overrides[:dns_alt_names] = input['subject-alt-names'] unless input['subject-alt-names'].empty?
|
58
|
+
|
59
|
+
puppet = Config::Puppet.new(config_path)
|
60
|
+
puppet.load(cli_overrides: settings_overrides, logger: @logger)
|
61
|
+
return 1 if Errors.handle_with_usage(@logger, puppet.errors)
|
62
|
+
|
63
|
+
# Load most secure signing digest we can for cers/crl/csr signing.
|
64
|
+
signer = SigningDigest.new
|
65
|
+
return 1 if Errors.handle_with_usage(@logger, signer.errors)
|
66
|
+
|
67
|
+
# Generate root and intermediate ca and put all the certificates, crls,
|
68
|
+
# and keys where they should go.
|
69
|
+
errors = generate_pki(puppet.settings, signer.digest)
|
70
|
+
return 1 if Errors.handle_with_usage(@logger, errors)
|
71
|
+
|
72
|
+
@logger.inform "Generation succeeded. Find your files in #{puppet.settings[:cadir]}"
|
73
|
+
return 0
|
74
|
+
end
|
75
|
+
|
76
|
+
def generate_pki(settings, signing_digest)
|
77
|
+
ca = Puppetserver::Ca::LocalCertificateAuthority.new(signing_digest, settings)
|
78
|
+
|
79
|
+
root_key, root_cert, root_crl = ca.create_root_cert
|
80
|
+
ca.create_intermediate_cert(root_key, root_cert)
|
81
|
+
server_key, server_cert = ca.create_server_cert
|
82
|
+
return ca.errors if ca.errors.any?
|
83
|
+
|
84
|
+
FileSystem.ensure_dirs([settings[:ssldir],
|
85
|
+
settings[:cadir],
|
86
|
+
settings[:certdir],
|
87
|
+
settings[:privatekeydir],
|
88
|
+
settings[:publickeydir],
|
89
|
+
settings[:signeddir]])
|
90
|
+
|
91
|
+
public_files = [
|
92
|
+
[settings[:cacert], [ca.cert, root_cert]],
|
93
|
+
[settings[:cacrl], [ca.crl, root_crl]],
|
94
|
+
[settings[:cadir] + '/infra_crl.pem', [ca.crl, root_crl]],
|
95
|
+
[settings[:hostcert], server_cert],
|
96
|
+
[settings[:localcacert], [ca.cert, root_cert]],
|
97
|
+
[settings[:hostcrl], [ca.crl, root_crl]],
|
98
|
+
[settings[:hostpubkey], server_key.public_key],
|
99
|
+
[settings[:capub], ca.key.public_key],
|
100
|
+
[settings[:cert_inventory], ca.inventory_entry(server_cert)],
|
101
|
+
[settings[:cadir] + '/infra_inventory.txt', ''],
|
102
|
+
[settings[:cadir] + '/infra_serials', ''],
|
103
|
+
[settings[:serial], "002"],
|
104
|
+
[File.join(settings[:signeddir], "#{settings[:certname]}.pem"), server_cert],
|
105
|
+
]
|
106
|
+
|
107
|
+
private_files = [
|
108
|
+
[settings[:hostprivkey], server_key],
|
109
|
+
[settings[:rootkey], root_key],
|
110
|
+
[settings[:cakey], ca.key],
|
111
|
+
]
|
112
|
+
|
113
|
+
files_to_check = public_files + private_files
|
114
|
+
# We don't want to error if server's keys exist. Certain workflows
|
115
|
+
# allow the agent to have already be installed with keys and then
|
116
|
+
# upgraded to be a server. The host class will honor keys, if both
|
117
|
+
# public and private exist, and error if only one exists - as is
|
118
|
+
# previous behavior.
|
119
|
+
files_to_check = files_to_check.map(&:first) - [settings[:hostpubkey], settings[:hostprivkey]]
|
120
|
+
errors = FileSystem.check_for_existing_files(files_to_check)
|
121
|
+
|
122
|
+
if !errors.empty?
|
123
|
+
instructions = <<-ERR
|
124
|
+
If you would really like to replace your CA, please delete the existing files first.
|
125
|
+
Note that any certificates that were issued by this CA will become invalid if you
|
126
|
+
replace it!
|
127
|
+
ERR
|
128
|
+
errors << instructions
|
129
|
+
return errors
|
130
|
+
end
|
131
|
+
|
132
|
+
public_files.each do |location, content|
|
133
|
+
FileSystem.write_file(location, content, 0644)
|
134
|
+
end
|
135
|
+
|
136
|
+
private_files.each do |location, content|
|
137
|
+
FileSystem.write_file(location, content, 0640)
|
138
|
+
end
|
139
|
+
|
140
|
+
Puppetserver::Ca::Utils::Config.symlink_to_old_cadir(settings[:cadir], settings[:confdir])
|
141
|
+
|
142
|
+
return []
|
143
|
+
end
|
144
|
+
|
145
|
+
def parse(cli_args)
|
146
|
+
results = {}
|
147
|
+
parser = self.class.parser(results)
|
148
|
+
errors = CliParsing.parse_with_errors(parser, cli_args)
|
149
|
+
errors_were_handled = Errors.handle_with_usage(@logger, errors, parser.help)
|
150
|
+
exit_code = errors_were_handled ? 1 : nil
|
151
|
+
return results, exit_code
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.parser(parsed = {})
|
155
|
+
parsed['subject-alt-names'] = ''
|
156
|
+
parsed['ca-name'] = ''
|
157
|
+
parsed['root-ca-name'] = ''
|
158
|
+
parsed['certname'] = ''
|
159
|
+
OptionParser.new do |opts|
|
160
|
+
opts.banner = BANNER
|
161
|
+
opts.on('--help', 'Display this command-specific help output') do |help|
|
162
|
+
parsed['help'] = true
|
163
|
+
end
|
164
|
+
opts.on('--config CONF', 'Path to puppet.conf') do |conf|
|
165
|
+
parsed['config'] = conf
|
166
|
+
end
|
167
|
+
opts.on('--subject-alt-names NAME[,NAME]',
|
168
|
+
'Subject alternative names for the server cert') do |sans|
|
169
|
+
parsed['subject-alt-names'] = sans
|
170
|
+
end
|
171
|
+
opts.on('--ca-name NAME',
|
172
|
+
'Common name to use for the CA signing cert') do |name|
|
173
|
+
parsed['ca-name'] = name
|
174
|
+
end
|
175
|
+
opts.on('--root-ca-name NAME',
|
176
|
+
'Common name to use for the self-signed Root CA cert') do |name|
|
177
|
+
parsed['root-ca-name'] = name
|
178
|
+
end
|
179
|
+
opts.on('--certname NAME',
|
180
|
+
'Common name to use for the server cert') do |name|
|
181
|
+
parsed['certname'] = name
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'openssl'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
require 'puppetserver/ca/certificate_authority'
|
6
|
+
require 'puppetserver/ca/config/puppet'
|
7
|
+
require 'puppetserver/ca/errors'
|
8
|
+
require 'puppetserver/ca/utils/cli_parsing'
|
9
|
+
require 'puppetserver/ca/utils/file_system'
|
10
|
+
|
11
|
+
module Puppetserver
|
12
|
+
module Ca
|
13
|
+
module Action
|
14
|
+
class Sign
|
15
|
+
|
16
|
+
include Puppetserver::Ca::Utils
|
17
|
+
|
18
|
+
SUMMARY = 'Sign certificate request(s)'
|
19
|
+
BANNER = <<-BANNER
|
20
|
+
Usage:
|
21
|
+
puppetserver ca sign [--help]
|
22
|
+
puppetserver ca sign [--config] --certname NAME[,NAME]
|
23
|
+
puppetserver ca sign --all
|
24
|
+
|
25
|
+
Description:
|
26
|
+
Given a comma-separated list of valid certnames, instructs the CA to sign
|
27
|
+
each cert.
|
28
|
+
|
29
|
+
Options:
|
30
|
+
BANNER
|
31
|
+
|
32
|
+
def self.parser(parsed = {})
|
33
|
+
OptionParser.new do |opts|
|
34
|
+
opts.banner = BANNER
|
35
|
+
opts.on('--ttl TTL', 'The time-to-live for each cert signed') do |ttl|
|
36
|
+
parsed['ttl'] = ttl
|
37
|
+
end
|
38
|
+
opts.on('--certname NAME[,NAME]', Array, 'the name(s) of the cert(s) to be signed') do |cert|
|
39
|
+
parsed['certname'] = cert
|
40
|
+
end
|
41
|
+
opts.on('--config CONF', 'Custom path to Puppet\'s config file') do |conf|
|
42
|
+
parsed['config'] = conf
|
43
|
+
end
|
44
|
+
opts.on('--help', 'Display this command-specific help output') do |help|
|
45
|
+
parsed['help'] = true
|
46
|
+
end
|
47
|
+
opts.on('--all', 'Operate on all certnames') do |a|
|
48
|
+
parsed['all'] = true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(logger)
|
54
|
+
@logger = logger
|
55
|
+
end
|
56
|
+
|
57
|
+
def run(input)
|
58
|
+
config = input['config']
|
59
|
+
|
60
|
+
if config
|
61
|
+
errors = FileSystem.validate_file_paths(config)
|
62
|
+
return 1 if Errors.handle_with_usage(@logger, errors)
|
63
|
+
end
|
64
|
+
|
65
|
+
puppet = Config::Puppet.parse(config, @logger)
|
66
|
+
return 1 if Errors.handle_with_usage(@logger, puppet.errors)
|
67
|
+
|
68
|
+
ca = Puppetserver::Ca::CertificateAuthority.new(@logger, puppet.settings)
|
69
|
+
bulk_sign = ca.server_has_bulk_signing_endpoints
|
70
|
+
|
71
|
+
# Bulk sign endpoints don't allow setting TTL, so
|
72
|
+
# use single signing endpoint if TTL is specified.
|
73
|
+
success = false
|
74
|
+
if input['ttl'] || !bulk_sign
|
75
|
+
if input['all']
|
76
|
+
requested_certnames = get_all_pending_certs(ca)
|
77
|
+
return 1 if requested_certnames.nil?
|
78
|
+
return 24 if requested_certnames.empty?
|
79
|
+
else
|
80
|
+
requested_certnames = input['certname']
|
81
|
+
end
|
82
|
+
|
83
|
+
success = ca.sign_certs(requested_certnames, input['ttl'])
|
84
|
+
return success ? 0 : 1
|
85
|
+
else
|
86
|
+
result = input['all'] ? ca.sign_all : ca.sign_bulk(input['certname'])
|
87
|
+
case result
|
88
|
+
when :success
|
89
|
+
return 0
|
90
|
+
when :no_requests
|
91
|
+
return 24
|
92
|
+
else
|
93
|
+
return 1
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def get_all_pending_certs(ca)
|
99
|
+
if result = ca.get_certificate_statuses
|
100
|
+
select_pending_certs(result.body)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def select_pending_certs(get_result)
|
105
|
+
requested_certnames = JSON.parse(get_result).select{|e| e["state"] == "requested"}.map{|e| e["name"]}
|
106
|
+
|
107
|
+
if requested_certnames.empty?
|
108
|
+
@logger.err 'Error:'
|
109
|
+
@logger.err " No waiting certificate requests to sign"
|
110
|
+
return requested_certnames
|
111
|
+
end
|
112
|
+
|
113
|
+
return requested_certnames
|
114
|
+
end
|
115
|
+
|
116
|
+
def check_flag_usage(results)
|
117
|
+
if results['certname'] && results['all']
|
118
|
+
'--all and --certname cannot be used together'
|
119
|
+
elsif !results['certname'] && !results['all']
|
120
|
+
'No arguments given'
|
121
|
+
elsif results['certname'] && results['certname'].include?('--all')
|
122
|
+
'Cannot use --all with --certname. If you actually have a certificate request ' +
|
123
|
+
'for a certifcate named --all, you need to use the HTTP API.'
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def parse(args)
|
128
|
+
results = {}
|
129
|
+
parser = self.class.parser(results)
|
130
|
+
|
131
|
+
errors = CliParsing.parse_with_errors(parser, args)
|
132
|
+
|
133
|
+
if err = check_flag_usage(results)
|
134
|
+
errors << err
|
135
|
+
end
|
136
|
+
|
137
|
+
errors_were_handled = Errors.handle_with_usage(@logger, errors, parser.help)
|
138
|
+
|
139
|
+
exit_code = errors_were_handled ? 1 : nil
|
140
|
+
|
141
|
+
return results, exit_code
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|