Skip to content

Commit 7e169c6

Browse files
committed
(PUP-11471) Allow to trust system CA in a Puppet SSL Context
This allows to create HTTP clients that can connect to servers which certificates where signed by an authority trusted by the system's trust store with client authentication using the Puppet node certificate.
1 parent 0392b3b commit 7e169c6

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

lib/puppet/ssl/ssl_provider.rb

+6-4
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,20 @@ def create_system_context(cacerts:, path: Puppet[:ssl_trust_store])
9393
# @param client_cert [OpenSSL::X509::Certificate] client's cert whose public
9494
# key matches the `private_key`
9595
# @param revocation [:chain, :leaf, false] revocation mode
96+
# @param include_system_store [true, false] Also trust system CA
9697
# @return [Puppet::SSL::SSLContext] A context to use to create connections
9798
# @raise [Puppet::SSL::CertVerifyError] There was an issue with
9899
# one of the certs or CRLs.
99100
# @raise [Puppet::SSL::SSLError] There was an issue with the
100101
# `private_key`.
101102
# @api private
102-
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)
103104
raise ArgumentError, _("CA certs are missing") unless cacerts
104105
raise ArgumentError, _("CRLs are missing") unless crls
105106
raise ArgumentError, _("Private key is missing") unless private_key
106107
raise ArgumentError, _("Client cert is missing") unless client_cert
107108

108-
store = create_x509_store(cacerts, crls, revocation)
109+
store = create_x509_store(cacerts, crls, revocation, include_system_store: include_system_store)
109110
client_chain = verify_cert_with_store(store, client_cert)
110111

111112
if !private_key.is_a?(OpenSSL::PKey::RSA) && !private_key.is_a?(OpenSSL::PKey::EC)
@@ -133,12 +134,13 @@ def create_context(cacerts:, crls:, private_key:, client_cert:, revocation: Pupp
133134
# @param password [String, nil] If the private key is encrypted, decrypt
134135
# it using the password. If the key is encrypted, but a password is
135136
# not specified, then the key cannot be loaded.
137+
# @param include_system_store [true, false] Also trust system CA
136138
# @return [Puppet::SSL::SSLContext] A context to use to create connections
137139
# @raise [Puppet::SSL::CertVerifyError] There was an issue with
138140
# one of the certs or CRLs.
139141
# @raise [Puppet::Error] There was an issue with one of the required components.
140142
# @api private
141-
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)
142144
cert = Puppet::X509::CertProvider.new
143145
cacerts = cert.load_cacerts(required: true)
144146
crls = case revocation
@@ -150,7 +152,7 @@ def load_context(certname: Puppet[:certname], revocation: Puppet[:certificate_re
150152
private_key = cert.load_private_key(certname, required: true, password: password)
151153
client_cert = cert.load_client_cert(certname, required: true)
152154

153-
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)
154156
rescue OpenSSL::PKey::PKeyError => e
155157
raise Puppet::SSL::SSLError.new(_("Failed to load private key for host '%{name}': %{message}") % { name: certname, message: e.message }, e)
156158
end

spec/integration/http/client_spec.rb

+30
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)