Skip to content

Commit 3f7f830

Browse files
committed
(PUP-11522) Support mTLS with an external CA
Passing "include_system_store: true" to any of the http client methods will now attempt to include the client cert for mutual TLS authentication. The client cert will be ignored if it doesn't exist, e.g. when running puppet apply.
1 parent 8c41acb commit 3f7f830

File tree

3 files changed

+20
-3
lines changed

3 files changed

+20
-3
lines changed

lib/puppet/http/client.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ def system_ssl_context
426426
cacerts = cert_provider.load_cacerts || []
427427

428428
ssl = Puppet::SSL::SSLProvider.new
429-
@default_system_ssl_context = ssl.create_system_context(cacerts: cacerts)
429+
@default_system_ssl_context = ssl.create_system_context(cacerts: cacerts, include_client_cert: true)
430430
ssl.print(@default_system_ssl_context)
431431
@default_system_ssl_context
432432
end

spec/integration/http/client_spec.rb

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
res
8484
end
8585

86-
it "mutually authenticates the connection" do
86+
it "mutually authenticates the connection using an explicit context" do
8787
client_context = ssl_provider.create_context(
8888
cacerts: [https_server.ca_cert], crls: [https_server.ca_crl],
8989
client_cert: https_server.server_cert, private_key: https_server.server_key
@@ -95,6 +95,23 @@
9595
end
9696
end
9797

98+
it "mutually authenticates the connection when the client and server certs are issued from different CAs" do
99+
# this is the client cert's CA, key and cert
100+
Puppet[:localcacert] = fixtures('ssl/unknown-ca.pem')
101+
Puppet[:hostprivkey] = fixtures('ssl/unknown-127.0.0.1-key.pem')
102+
Puppet[:hostcert] = fixtures('ssl/unknown-127.0.0.1.pem')
103+
104+
# this is the server cert's CA that the client needs in order to authenticate the server
105+
Puppet[:ssl_trust_store] = fixtures('ssl/ca.pem')
106+
107+
# need to pass both the client and server CAs. The former is needed so the server can authenticate our client cert
108+
https_server = PuppetSpec::HTTPSServer.new(ca_cert: [cert_fixture('ca.pem'), cert_fixture('unknown-ca.pem')])
109+
https_server.start_server(ctx_proc: ctx_proc) do |port|
110+
res = client.get(URI("https://127.0.0.1:#{port}"), options: {include_system_store: true})
111+
expect(res).to be_success
112+
end
113+
end
114+
98115
it "connects when the server's CA is in the system store and the connection is mutually authenticated using create_context" do
99116
Puppet::Util.withenv("SSL_CERT_FILE" => cert_file) do
100117
client_context = ssl_provider.create_context(

spec/lib/puppet_spec/https.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def start_server(ctx_proc: nil, response_proc: nil, &block)
4040

4141
IO.pipe {|stop_pipe_r, stop_pipe_w|
4242
store = OpenSSL::X509::Store.new
43-
store.add_cert(@ca_cert)
43+
Array(@ca_cert).each { |c| store.add_cert(c) }
4444
store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
4545
ctx = OpenSSL::SSL::SSLContext.new
4646
ctx.cert_store = store

0 commit comments

Comments
 (0)