puppetserver-ca 0.4.3 → 0.5.0
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 +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
|