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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9c19462a3167093517421b973f330b005a6776b4
4
- data.tar.gz: 5c4dc2716c5063512594005c625ad391ca6ec3f0
3
+ metadata.gz: 2f958c8e39c9f5a84c417138710b43c09d088189
4
+ data.tar.gz: 33f332146f6f3c3318dbfe7cc786205d74a3a021
5
5
  SHA512:
6
- metadata.gz: cac3a5c1d00ddb4e4fc9948fc09c91bc6436e8c245cd17610b971e7dbc74c033ba41c7602d12fdbe11808eb51a0612c5fee71353af4b9956b4ac449fb889ec84
7
- data.tar.gz: 8e8ac33850a91d128282572c0ef2f4dd421dd447911e09cc6aa561eae1f6b381c3b06f0e9661581616c2c516da92490ad59d180744737bf6873c049a9d9d673f
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 << host.create_extension("subjectAltName", settings[:subject_alt_names])
156
+ extensions << OpenSSL::X509::Extension.new("subjectAltName", settings[:subject_alt_names], false)
156
157
  end
157
- csr = host.create_csr(certname, private_key, extensions)
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],
@@ -111,7 +111,7 @@ Options:
111
111
 
112
112
  def get_all_certs(settings)
113
113
  result = Puppetserver::Ca::CertificateAuthority.new(@logger, settings).get_certificate_statuses
114
- JSON.parse(result.body)
114
+ JSON.parse(result.body) if result
115
115
  end
116
116
 
117
117
  def parse(args)
@@ -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',
@@ -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, key, extensions = [])
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
- add_csr_extension(csr, extensions) unless extensions.empty?
21
- csr.sign(key, @digest)
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 create_extension(extension_name, extension_value, critical = false)
27
- OpenSSL::X509::ExtensionFactory.new.create_extension(extension_name, extension_value, critical)
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 add_csr_extension(csr, extensions)
31
- attribute_values = OpenSSL::ASN1::Set [OpenSSL::ASN1::Sequence(extensions)]
32
- att = OpenSSL::X509::Attribute.new('extReq', attribute_values)
33
- csr.add_attribute(att)
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
- cert.sign(int_key, @digest)
95
- cert
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
 
@@ -1,5 +1,5 @@
1
1
  module Puppetserver
2
2
  module Ca
3
- VERSION = "0.4.3"
3
+ VERSION = "0.5.0"
4
4
  end
5
5
  end
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.3
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-29 00:00:00.000000000 Z
11
+ date: 2018-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: facter