Skip to content

Commit 6182816

Browse files
authored
Merge pull request #8887 from smortex/include_system_store-in-load_context
(PUP-11471) Allow mixing system CA and Puppet client certificate authentication
2 parents 68a96a9 + 7e169c6 commit 6182816

File tree

2 files changed

+40
-7
lines changed

2 files changed

+40
-7
lines changed

lib/puppet/ssl/ssl_provider.rb

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ def create_root_context(cacerts:, crls: [], revocation: Puppet[:certificate_revo
5151
# @raise (see #create_context)
5252
# @api private
5353
def create_system_context(cacerts:, path: Puppet[:ssl_trust_store])
54-
store = create_x509_store(cacerts, [], false)
55-
store.set_default_paths
54+
store = create_x509_store(cacerts, [], false, include_system_store: true)
5655

5756
if path
5857
stat = Puppet::FileSystem.stat(path)
@@ -94,19 +93,20 @@ def create_system_context(cacerts:, path: Puppet[:ssl_trust_store])
9493
# @param client_cert [OpenSSL::X509::Certificate] client's cert whose public
9594
# key matches the `private_key`
9695
# @param revocation [:chain, :leaf, false] revocation mode
96+
# @param include_system_store [true, false] Also trust system CA
9797
# @return [Puppet::SSL::SSLContext] A context to use to create connections
9898
# @raise [Puppet::SSL::CertVerifyError] There was an issue with
9999
# one of the certs or CRLs.
100100
# @raise [Puppet::SSL::SSLError] There was an issue with the
101101
# `private_key`.
102102
# @api private
103-
def create_context(cacerts:, crls:, private_key:, client_cert:, revocation: Puppet[:certificate_revocation])
103+
def create_context(cacerts:, crls:, private_key:, client_cert:, revocation: Puppet[:certificate_revocation], include_system_store: false)
104104
raise ArgumentError, _("CA certs are missing") unless cacerts
105105
raise ArgumentError, _("CRLs are missing") unless crls
106106
raise ArgumentError, _("Private key is missing") unless private_key
107107
raise ArgumentError, _("Client cert is missing") unless client_cert
108108

109-
store = create_x509_store(cacerts, crls, revocation)
109+
store = create_x509_store(cacerts, crls, revocation, include_system_store: include_system_store)
110110
client_chain = verify_cert_with_store(store, client_cert)
111111

112112
if !private_key.is_a?(OpenSSL::PKey::RSA) && !private_key.is_a?(OpenSSL::PKey::EC)
@@ -134,12 +134,13 @@ def create_context(cacerts:, crls:, private_key:, client_cert:, revocation: Pupp
134134
# @param password [String, nil] If the private key is encrypted, decrypt
135135
# it using the password. If the key is encrypted, but a password is
136136
# not specified, then the key cannot be loaded.
137+
# @param include_system_store [true, false] Also trust system CA
137138
# @return [Puppet::SSL::SSLContext] A context to use to create connections
138139
# @raise [Puppet::SSL::CertVerifyError] There was an issue with
139140
# one of the certs or CRLs.
140141
# @raise [Puppet::Error] There was an issue with one of the required components.
141142
# @api private
142-
def load_context(certname: Puppet[:certname], revocation: Puppet[:certificate_revocation], password: nil)
143+
def load_context(certname: Puppet[:certname], revocation: Puppet[:certificate_revocation], password: nil, include_system_store: false)
143144
cert = Puppet::X509::CertProvider.new
144145
cacerts = cert.load_cacerts(required: true)
145146
crls = case revocation
@@ -151,7 +152,7 @@ def load_context(certname: Puppet[:certname], revocation: Puppet[:certificate_re
151152
private_key = cert.load_private_key(certname, required: true, password: password)
152153
client_cert = cert.load_client_cert(certname, required: true)
153154

154-
create_context(cacerts: cacerts, crls: crls, private_key: private_key, client_cert: client_cert, revocation: revocation)
155+
create_context(cacerts: cacerts, crls: crls, private_key: private_key, client_cert: client_cert, revocation: revocation, include_system_store: include_system_store)
155156
rescue OpenSSL::PKey::PKeyError => e
156157
raise Puppet::SSL::SSLError.new(_("Failed to load private key for host '%{name}': %{message}") % { name: certname, message: e.message }, e)
157158
end
@@ -186,14 +187,16 @@ def default_flags
186187
end
187188
end
188189

189-
def create_x509_store(roots, crls, revocation)
190+
def create_x509_store(roots, crls, revocation, include_system_store: false)
190191
store = OpenSSL::X509::Store.new
191192
store.purpose = OpenSSL::X509::PURPOSE_ANY
192193
store.flags = default_flags | revocation_mode(revocation)
193194

194195
roots.each { |cert| store.add_cert(cert) }
195196
crls.each { |crl| store.add_crl(crl) }
196197

198+
store.set_default_paths if include_system_store
199+
197200
store
198201
end
199202

spec/integration/http/client_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@
7777
}
7878
}
7979

80+
let(:systemstore) do
81+
res = tmpfile('systemstore')
82+
File.write(res, https_server.ca_cert)
83+
res
84+
end
85+
8086
it "mutually authenticates the connection" do
8187
client_context = ssl_provider.create_context(
8288
cacerts: [https_server.ca_cert], crls: [https_server.ca_crl],
@@ -88,6 +94,30 @@
8894
expect(res).to be_success
8995
end
9096
end
97+
98+
it "connects when the server's CA is in the system store and the connection is mutually authenticated using create_context" do
99+
Puppet::Util.withenv("SSL_CERT_FILE" => systemstore) do
100+
client_context = ssl_provider.create_context(
101+
cacerts: [https_server.ca_cert], crls: [https_server.ca_crl],
102+
client_cert: https_server.server_cert, private_key: https_server.server_key,
103+
revocation: false, include_system_store: true
104+
)
105+
https_server.start_server(ctx_proc: ctx_proc) do |port|
106+
res = client.get(URI("https://127.0.0.1:#{port}"), options: {ssl_context: client_context})
107+
expect(res).to be_success
108+
end
109+
end
110+
end
111+
112+
it "connects when the server's CA is in the system store and the connection is mutually authenticated uning load_context" do
113+
Puppet::Util.withenv("SSL_CERT_FILE" => systemstore) do
114+
client_context = ssl_provider.load_context(revocation: false, include_system_store: true)
115+
https_server.start_server(ctx_proc: ctx_proc) do |port|
116+
res = client.get(URI("https://127.0.0.1:#{port}"), options: {ssl_context: client_context})
117+
expect(res).to be_success
118+
end
119+
end
120+
end
91121
end
92122

93123
context "with a system trust store" do

0 commit comments

Comments
 (0)