puppetserver-ca 0.4.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/puppetserver/ca/action/create.rb +7 -2
- data/lib/puppetserver/ca/action/generate.rb +1 -0
- data/lib/puppetserver/ca/action/import.rb +1 -0
- data/lib/puppetserver/ca/action/list.rb +1 -1
- data/lib/puppetserver/ca/config/puppet.rb +1 -1
- data/lib/puppetserver/ca/host.rb +134 -9
- data/lib/puppetserver/ca/local_certificate_authority.rb +39 -4
- data/lib/puppetserver/ca/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f958c8e39c9f5a84c417138710b43c09d088189
|
4
|
+
data.tar.gz: 33f332146f6f3c3318dbfe7cc786205d74a3a021
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77847b302859de89a4566dff4d4230e049c488f09baeca6b1b9ec86b68c68803942dfb6c187b37b5f9eb80a2ee531f62fe5fd0f4877ffd820cba10ecd672c0a5
|
7
|
+
data.tar.gz: 3234826e83a9a397216cf1133c80572e5ad3f4d8f06f4c0fc5189f1eac048d08e82814be8e54e4bc8008962fbd28c5780406952a64d03ba349aeb7fd90141c63
|
@@ -134,6 +134,7 @@ BANNER
|
|
134
134
|
|
135
135
|
passed = certnames.map do |certname|
|
136
136
|
key, csr = generate_key_csr(certname, settings, digest)
|
137
|
+
return false unless csr
|
137
138
|
return false unless ca.submit_certificate_request(certname, csr)
|
138
139
|
return false unless ca.sign_certs([certname])
|
139
140
|
if result = ca.get_certificate(certname)
|
@@ -152,9 +153,13 @@ BANNER
|
|
152
153
|
private_key = host.create_private_key(settings[:keylength])
|
153
154
|
extensions = []
|
154
155
|
if !settings[:subject_alt_names].empty?
|
155
|
-
extensions <<
|
156
|
+
extensions << OpenSSL::X509::Extension.new("subjectAltName", settings[:subject_alt_names], false)
|
156
157
|
end
|
157
|
-
csr = host.create_csr(certname,
|
158
|
+
csr = host.create_csr(name: certname,
|
159
|
+
key: private_key,
|
160
|
+
cli_extensions: extensions,
|
161
|
+
csr_attributes_path: settings[:csr_attributes])
|
162
|
+
return if CliParsing.handle_errors(@logger, host.errors)
|
158
163
|
|
159
164
|
return private_key, csr
|
160
165
|
end
|
@@ -78,6 +78,7 @@ BANNER
|
|
78
78
|
root_key, root_cert, root_crl = ca.create_root_cert
|
79
79
|
int_key, int_cert, int_crl = ca.create_intermediate_cert(root_key, root_cert)
|
80
80
|
master_key, master_cert = ca.create_master_cert(int_key, int_cert)
|
81
|
+
return ca.errors if ca.errors.any?
|
81
82
|
|
82
83
|
FileSystem.ensure_dirs([settings[:ssldir],
|
83
84
|
settings[:cadir],
|
@@ -74,6 +74,7 @@ BANNER
|
|
74
74
|
def import(loader, settings, signing_digest)
|
75
75
|
ca = Puppetserver::Ca::LocalCertificateAuthority.new(signing_digest, settings)
|
76
76
|
master_key, master_cert = ca.create_master_cert(loader.key, loader.certs.first)
|
77
|
+
return ca.errors if ca.errors.any?
|
77
78
|
|
78
79
|
FileSystem.ensure_dirs([settings[:ssldir],
|
79
80
|
settings[:cadir],
|
@@ -119,6 +119,7 @@ module Puppetserver
|
|
119
119
|
:cacert => '$cadir/ca_crt.pem',
|
120
120
|
:cakey => '$cadir/ca_key.pem',
|
121
121
|
:capub => '$cadir/ca_pub.pem',
|
122
|
+
:csr_attributes => '$confdir/csr_attributes.yaml',
|
122
123
|
:rootkey => '$cadir/root_key.pem',
|
123
124
|
:cacrl => '$cadir/ca_crl.pem',
|
124
125
|
:serial => '$cadir/serial',
|
@@ -130,7 +131,6 @@ module Puppetserver
|
|
130
131
|
:hostcert => '$certdir/$certname.pem',
|
131
132
|
:hostprivkey => '$privatekeydir/$certname.pem',
|
132
133
|
:hostpubkey => '$publickeydir/$certname.pem',
|
133
|
-
:publickeydir => '$ssldir/public_keys',
|
134
134
|
:ca_ttl => '15y',
|
135
135
|
:certificate_revocation => 'true',
|
136
136
|
:signeddir => '$cadir/signed',
|
data/lib/puppetserver/ca/host.rb
CHANGED
@@ -1,36 +1,161 @@
|
|
1
1
|
require 'openssl'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'yaml'
|
2
4
|
|
3
5
|
module Puppetserver
|
4
6
|
module Ca
|
5
7
|
class Host
|
8
|
+
# Exclude OIDs that may conflict with how Puppet creates CSRs.
|
9
|
+
#
|
10
|
+
# We only have nominal support for Microsoft extension requests, but since we
|
11
|
+
# ultimately respect that field when looking for extension requests in a CSR
|
12
|
+
# we need to prevent that field from being written to directly.
|
13
|
+
PRIVATE_CSR_ATTRIBUTES = [
|
14
|
+
'extReq', '1.2.840.113549.1.9.14',
|
15
|
+
'msExtReq', '1.3.6.1.4.1.311.2.1.14',
|
16
|
+
]
|
17
|
+
|
18
|
+
PRIVATE_EXTENSIONS = [
|
19
|
+
'subjectAltName', '2.5.29.17',
|
20
|
+
]
|
21
|
+
|
22
|
+
# A mapping of Puppet extension short names to their OIDs. These appear in
|
23
|
+
# csr_attributes.yaml.
|
24
|
+
PUPPET_SHORT_NAMES =
|
25
|
+
{'pp_uuid' => "1.3.6.1.4.1.34380.1.1.1",
|
26
|
+
'pp_instance_id' => "1.3.6.1.4.1.34380.1.1.2",
|
27
|
+
'pp_image_name' => "1.3.6.1.4.1.34380.1.1.3",
|
28
|
+
'pp_preshared_key'=> "1.3.6.1.4.1.34380.1.1.4",
|
29
|
+
'pp_cost_center' => "1.3.6.1.4.1.34380.1.1.5",
|
30
|
+
'pp_product' => "1.3.6.1.4.1.34380.1.1.6",
|
31
|
+
'pp_project' => "1.3.6.1.4.1.34380.1.1.7",
|
32
|
+
'pp_application' => "1.3.6.1.4.1.34380.1.1.8",
|
33
|
+
'pp_service'=> "1.3.6.1.4.1.34380.1.1.9",
|
34
|
+
'pp_employee' => "1.3.6.1.4.1.34380.1.1.10",
|
35
|
+
'pp_created_by' => "1.3.6.1.4.1.34380.1.1.11",
|
36
|
+
'pp_environment' => "1.3.6.1.4.1.34380.1.1.12",
|
37
|
+
'pp_role' => "1.3.6.1.4.1.34380.1.1.13",
|
38
|
+
'pp_software_version' => "1.3.6.1.4.1.34380.1.1.14",
|
39
|
+
'pp_department' => "1.3.6.1.4.1.34380.1.1.15",
|
40
|
+
'pp_cluster' => "1.3.6.1.4.1.34380.1.1.16",
|
41
|
+
'pp_provisioner' => "1.3.6.1.4.1.34380.1.1.17",
|
42
|
+
'pp_region' => "1.3.6.1.4.1.34380.1.1.18",
|
43
|
+
'pp_datacenter' => "1.3.6.1.4.1.34380.1.1.19",
|
44
|
+
'pp_zone' => "1.3.6.1.4.1.34380.1.1.20",
|
45
|
+
'pp_network' => "1.3.6.1.4.1.34380.1.1.21",
|
46
|
+
'pp_securitypolicy' => "1.3.6.1.4.1.34380.1.1.22",
|
47
|
+
'pp_cloudplatform' => "1.3.6.1.4.1.34380.1.1.23",
|
48
|
+
'pp_apptier' => "1.3.6.1.4.1.34380.1.1.24",
|
49
|
+
'pp_hostname' => "1.3.6.1.4.1.34380.1.1.25",
|
50
|
+
'pp_authorization' => "1.3.6.1.4.1.34380.1.3.1",
|
51
|
+
'pp_auth_role' => "1.3.6.1.4.1.34380.1.3.13"}
|
52
|
+
|
53
|
+
attr_reader :errors
|
6
54
|
|
7
55
|
def initialize(digest)
|
8
56
|
@digest = digest
|
57
|
+
@errors = []
|
9
58
|
end
|
10
59
|
|
11
60
|
def create_private_key(keylength)
|
12
61
|
OpenSSL::PKey::RSA.new(keylength)
|
13
62
|
end
|
14
63
|
|
15
|
-
def create_csr(name
|
64
|
+
def create_csr(name:, key:, cli_extensions: [], csr_attributes_path: '')
|
16
65
|
csr = OpenSSL::X509::Request.new
|
17
66
|
csr.public_key = key.public_key
|
18
67
|
csr.subject = OpenSSL::X509::Name.new([["CN", name]])
|
19
68
|
csr.version = 2
|
20
|
-
|
21
|
-
|
69
|
+
|
70
|
+
custom_attributes = get_custom_attributes(csr_attributes_path)
|
71
|
+
extension_requests = get_extension_requests(csr_attributes_path)
|
72
|
+
|
73
|
+
add_csr_attributes(csr, custom_attributes)
|
74
|
+
add_csr_extensions(csr, extension_requests, cli_extensions)
|
75
|
+
|
76
|
+
csr.sign(key, @digest) if @errors.empty?
|
22
77
|
|
23
78
|
csr
|
24
79
|
end
|
25
80
|
|
26
|
-
def
|
27
|
-
OpenSSL::
|
81
|
+
def extension_attribute(extensions)
|
82
|
+
seq = OpenSSL::ASN1::Sequence(extensions)
|
83
|
+
ext_req = OpenSSL::ASN1::Set([seq])
|
84
|
+
OpenSSL::X509::Attribute.new("extReq", ext_req)
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_custom_attributes(attributes_path)
|
88
|
+
if csr_attributes = load_csr_attributes(attributes_path)
|
89
|
+
csr_attributes['custom_attributes']
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def get_extension_requests(attributes_path)
|
94
|
+
if csr_attributes = load_csr_attributes(attributes_path)
|
95
|
+
csr_attributes['extension_requests']
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# This loads all the custom_attributes and extension requests
|
100
|
+
# from the csr_attributes.yaml
|
101
|
+
def load_csr_attributes(attributes_path)
|
102
|
+
@custom_csr_attributes ||=
|
103
|
+
if File.exist?(attributes_path)
|
104
|
+
yaml = YAML.load_file(attributes_path)
|
105
|
+
if !yaml.is_a?(Hash)
|
106
|
+
@errors << "Invalid CSR attributes, expected instance of Hash, received instance of #{yaml.class}"
|
107
|
+
return
|
108
|
+
end
|
109
|
+
yaml
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def add_csr_attributes(csr, csr_attributes)
|
114
|
+
if csr_attributes
|
115
|
+
csr_attributes.each do |oid, value|
|
116
|
+
begin
|
117
|
+
if PRIVATE_CSR_ATTRIBUTES.include? oid
|
118
|
+
@errors << "Cannot specify CSR attribute #{oid}: conflicts with internally used CSR attribute"
|
119
|
+
end
|
120
|
+
oid = PUPPET_SHORT_NAMES[oid] || oid
|
121
|
+
encoded = OpenSSL::ASN1::PrintableString.new(value.to_s)
|
122
|
+
attr_set = OpenSSL::ASN1::Set.new([encoded])
|
123
|
+
|
124
|
+
csr.add_attribute(OpenSSL::X509::Attribute.new(oid, attr_set))
|
125
|
+
rescue OpenSSL::X509::AttributeError => e
|
126
|
+
@errors << "Cannot create CSR with attribute #{oid}: #{e.message}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def add_csr_extensions(csr, extension_requests, cli_extensions)
|
133
|
+
if extension_requests || cli_extensions.any?
|
134
|
+
extensions =
|
135
|
+
if extension_requests
|
136
|
+
validated_extensions(extension_requests) + cli_extensions
|
137
|
+
else
|
138
|
+
cli_extensions
|
139
|
+
end
|
140
|
+
csr.add_attribute(extension_attribute(extensions))
|
141
|
+
end
|
28
142
|
end
|
29
143
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
144
|
+
def validated_extensions(extension_requests)
|
145
|
+
extensions = []
|
146
|
+
extension_requests.each do |oid, value|
|
147
|
+
begin
|
148
|
+
if PRIVATE_EXTENSIONS.include? oid
|
149
|
+
@errors << "Cannot specify CSR extension request #{oid}: conflicts with internally used extension request"
|
150
|
+
end
|
151
|
+
oid = PUPPET_SHORT_NAMES[oid] || oid
|
152
|
+
ext = OpenSSL::X509::Extension.new(oid, OpenSSL::ASN1::UTF8String.new(value.to_s).to_der, false)
|
153
|
+
extensions << ext
|
154
|
+
rescue OpenSSL::X509::ExtensionError => e
|
155
|
+
@errors << "Cannot create CSR with extension request #{oid}: #{e.message}"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
extensions
|
34
159
|
end
|
35
160
|
end
|
36
161
|
end
|
@@ -16,6 +16,8 @@ module Puppetserver
|
|
16
16
|
SSL_SERVER_CERT = "1.3.6.1.5.5.7.3.1"
|
17
17
|
SSL_CLIENT_CERT = "1.3.6.1.5.5.7.3.2"
|
18
18
|
|
19
|
+
CLI_AUTH_EXT_OID = "1.3.6.1.4.1.34380.1.3.39"
|
20
|
+
|
19
21
|
MASTER_EXTENSIONS = [
|
20
22
|
["basicConstraints", "CA:FALSE", true],
|
21
23
|
["nsComment", "Puppet Server Internal Certificate", false],
|
@@ -39,6 +41,10 @@ module Puppetserver
|
|
39
41
|
@settings = settings
|
40
42
|
end
|
41
43
|
|
44
|
+
def errors
|
45
|
+
@host.errors
|
46
|
+
end
|
47
|
+
|
42
48
|
def valid_until
|
43
49
|
Time.now + @settings[:ca_ttl]
|
44
50
|
end
|
@@ -62,7 +68,7 @@ module Puppetserver
|
|
62
68
|
|
63
69
|
def create_master_cert(ca_key, ca_cert)
|
64
70
|
master_key = @host.create_private_key(@settings[:keylength])
|
65
|
-
master_csr = @host.create_csr(@settings[:certname], master_key)
|
71
|
+
master_csr = @host.create_csr(name: @settings[:certname], key: master_key)
|
66
72
|
master_cert = sign_master_cert(ca_key, ca_cert, master_csr)
|
67
73
|
return master_key, master_cert
|
68
74
|
end
|
@@ -77,11 +83,28 @@ module Puppetserver
|
|
77
83
|
cert.not_before = CERT_VALID_FROM
|
78
84
|
cert.not_after = valid_until
|
79
85
|
|
86
|
+
return unless add_custom_extensions(cert)
|
87
|
+
|
80
88
|
ef = extension_factory_for(int_cert, cert)
|
89
|
+
add_master_extensions(cert, ef)
|
90
|
+
add_subject_alt_names_extension(cert, ef)
|
91
|
+
cert.sign(int_key, @digest)
|
92
|
+
|
93
|
+
cert
|
94
|
+
end
|
95
|
+
|
96
|
+
def add_master_extensions(cert, ef)
|
81
97
|
MASTER_EXTENSIONS.each do |ext|
|
82
98
|
extension = ef.create_extension(*ext)
|
83
99
|
cert.add_extension(extension)
|
84
100
|
end
|
101
|
+
|
102
|
+
# Status API access for the CA CLI
|
103
|
+
cli_auth_ext = OpenSSL::X509::Extension.new(CLI_AUTH_EXT_OID, OpenSSL::ASN1::UTF8String.new("true").to_der, false)
|
104
|
+
cert.add_extension(cli_auth_ext)
|
105
|
+
end
|
106
|
+
|
107
|
+
def add_subject_alt_names_extension(cert, ef)
|
85
108
|
sans =
|
86
109
|
if @settings[:subject_alt_names].empty?
|
87
110
|
"DNS:puppet, DNS:#{@settings[:certname]}"
|
@@ -90,9 +113,21 @@ module Puppetserver
|
|
90
113
|
end
|
91
114
|
alt_names_ext = ef.create_extension("subjectAltName", sans, false)
|
92
115
|
cert.add_extension(alt_names_ext)
|
116
|
+
end
|
93
117
|
|
94
|
-
|
95
|
-
|
118
|
+
# This takes all the extension requests from csr_attributes.yaml and
|
119
|
+
# adds those to the cert
|
120
|
+
def add_custom_extensions(cert)
|
121
|
+
extension_requests = @host.get_extension_requests(@settings[:csr_attributes])
|
122
|
+
|
123
|
+
if extension_requests
|
124
|
+
extensions = @host.validated_extensions(extension_requests)
|
125
|
+
extensions.each do |ext|
|
126
|
+
cert.add_extension(ext)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
@host.errors.empty?
|
96
131
|
end
|
97
132
|
|
98
133
|
def create_root_cert
|
@@ -146,7 +181,7 @@ module Puppetserver
|
|
146
181
|
|
147
182
|
def create_intermediate_cert(root_key, root_cert)
|
148
183
|
int_key = @host.create_private_key(@settings[:keylength])
|
149
|
-
int_csr = @host.create_csr(@settings[:ca_name], int_key)
|
184
|
+
int_csr = @host.create_csr(name: @settings[:ca_name], key: int_key)
|
150
185
|
int_cert = sign_intermediate(root_key, root_cert, int_csr)
|
151
186
|
int_crl = create_crl_for(int_cert, int_key)
|
152
187
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puppetserver-ca
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-08-
|
11
|
+
date: 2018-08-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: facter
|