@@ -42,15 +42,18 @@ def create_root_context(cacerts:, crls: [], revocation: Puppet[:certificate_revo
4242 # refers to the cacerts bundle in the puppet-agent package.
4343 #
4444 # Connections made from the returned context will authenticate the server,
45- # i.e. `VERIFY_PEER`, but will not use a client certificate and will not
46- # perform revocation checking.
45+ # i.e. `VERIFY_PEER`, but will not use a client certificate (unless requested)
46+ # and will not perform revocation checking.
4747 #
4848 # @param cacerts [Array<OpenSSL::X509::Certificate>] Array of trusted CA certs
4949 # @param path [String, nil] A file containing additional trusted CA certs.
50+ # @param include_client_cert [true, false] If true, the client cert will be added to the context
51+ # allowing mutual TLS authentication. The default is false. If the client cert doesn't exist
52+ # then the option will be ignored.
5053 # @return [Puppet::SSL::SSLContext] A context to use to create connections
5154 # @raise (see #create_context)
5255 # @api private
53- def create_system_context ( cacerts :, path : Puppet [ :ssl_trust_store ] )
56+ def create_system_context ( cacerts :, path : Puppet [ :ssl_trust_store ] , include_client_cert : false )
5457 store = create_x509_store ( cacerts , [ ] , false , include_system_store : true )
5558
5659 if path
@@ -71,6 +74,29 @@ def create_system_context(cacerts:, path: Puppet[:ssl_trust_store])
7174 end
7275 end
7376
77+ if include_client_cert
78+ cert_provider = Puppet ::X509 ::CertProvider . new
79+ private_key = cert_provider . load_private_key ( Puppet [ :certname ] , required : false )
80+ unless private_key
81+ Puppet . warning ( "Private key for '#{ Puppet [ :certname ] } ' does not exist" )
82+ end
83+
84+ client_cert = cert_provider . load_client_cert ( Puppet [ :certname ] , required : false )
85+ unless client_cert
86+ Puppet . warning ( "Client certificate for '#{ Puppet [ :certname ] } ' does not exist" )
87+ end
88+
89+ if private_key && client_cert
90+ client_chain = resolve_client_chain ( store , client_cert , private_key )
91+
92+ return Puppet ::SSL ::SSLContext . new (
93+ store : store , cacerts : cacerts , crls : [ ] ,
94+ private_key : private_key , client_cert : client_cert , client_chain : client_chain ,
95+ revocation : false
96+ ) . freeze
97+ end
98+ end
99+
74100 Puppet ::SSL ::SSLContext . new ( store : store , cacerts : cacerts , crls : [ ] , revocation : false ) . freeze
75101 end
76102
@@ -107,15 +133,7 @@ def create_context(cacerts:, crls:, private_key:, client_cert:, revocation: Pupp
107133 raise ArgumentError , _ ( "Client cert is missing" ) unless client_cert
108134
109135 store = create_x509_store ( cacerts , crls , revocation , include_system_store : include_system_store )
110- client_chain = verify_cert_with_store ( store , client_cert )
111-
112- if !private_key . is_a? ( OpenSSL ::PKey ::RSA ) && !private_key . is_a? ( OpenSSL ::PKey ::EC )
113- raise Puppet ::SSL ::SSLError , _ ( "Unsupported key '%{type}'" ) % { type : private_key . class . name }
114- end
115-
116- unless client_cert . check_private_key ( private_key )
117- raise Puppet ::SSL ::SSLError , _ ( "The certificate for '%{name}' does not match its private key" ) % { name : subject ( client_cert ) }
118- end
136+ client_chain = resolve_client_chain ( store , client_cert , private_key )
119137
120138 Puppet ::SSL ::SSLContext . new (
121139 store : store , cacerts : cacerts , crls : crls ,
@@ -241,6 +259,20 @@ def revocation_mode(mode)
241259 end
242260 end
243261
262+ def resolve_client_chain ( store , client_cert , private_key )
263+ client_chain = verify_cert_with_store ( store , client_cert )
264+
265+ if !private_key . is_a? ( OpenSSL ::PKey ::RSA ) && !private_key . is_a? ( OpenSSL ::PKey ::EC )
266+ raise Puppet ::SSL ::SSLError , _ ( "Unsupported key '%{type}'" ) % { type : private_key . class . name }
267+ end
268+
269+ unless client_cert . check_private_key ( private_key )
270+ raise Puppet ::SSL ::SSLError , _ ( "The certificate for '%{name}' does not match its private key" ) % { name : subject ( client_cert ) }
271+ end
272+
273+ client_chain
274+ end
275+
244276 def verify_cert_with_store ( store , cert )
245277 # StoreContext#initialize accepts a chain argument, but it's set to [] because
246278 # puppet requires any intermediate CA certs needed to complete the client's
0 commit comments