@@ -59,15 +59,18 @@ def create_root_context(cacerts:, crls: [], revocation: Puppet[:certificate_revo
5959 # refers to the cacerts bundle in the puppet-agent package.
6060 #
6161 # Connections made from the returned context will authenticate the server,
62- # i.e. `VERIFY_PEER`, but will not use a client certificate and will not
63- # perform revocation checking.
62+ # i.e. `VERIFY_PEER`, but will not use a client certificate (unless requested)
63+ # and will not perform revocation checking.
6464 #
6565 # @param cacerts [Array<OpenSSL::X509::Certificate>] Array of trusted CA certs
6666 # @param path [String, nil] A file containing additional trusted CA certs.
67+ # @param include_client_cert [true, false] If true, the client cert will be added to the context
68+ # allowing mutual TLS authentication. The default is false. If the client cert doesn't exist
69+ # then the option will be ignored.
6770 # @return [Puppet::SSL::SSLContext] A context to use to create connections
6871 # @raise (see #create_context)
6972 # @api private
70- def create_system_context ( cacerts :, path : Puppet [ :ssl_trust_store ] )
73+ def create_system_context ( cacerts :, path : Puppet [ :ssl_trust_store ] , include_client_cert : false )
7174 store = create_x509_store ( cacerts , [ ] , false , include_system_store : true )
7275
7376 if path
@@ -88,6 +91,29 @@ def create_system_context(cacerts:, path: Puppet[:ssl_trust_store])
8891 end
8992 end
9093
94+ if include_client_cert
95+ cert_provider = Puppet ::X509 ::CertProvider . new
96+ private_key = cert_provider . load_private_key ( Puppet [ :certname ] , required : false )
97+ unless private_key
98+ Puppet . warning ( "Private key for '#{ Puppet [ :certname ] } ' does not exist" )
99+ end
100+
101+ client_cert = cert_provider . load_client_cert ( Puppet [ :certname ] , required : false )
102+ unless client_cert
103+ Puppet . warning ( "Client certificate for '#{ Puppet [ :certname ] } ' does not exist" )
104+ end
105+
106+ if private_key && client_cert
107+ client_chain = resolve_client_chain ( store , client_cert , private_key )
108+
109+ return Puppet ::SSL ::SSLContext . new (
110+ store : store , cacerts : cacerts , crls : [ ] ,
111+ private_key : private_key , client_cert : client_cert , client_chain : client_chain ,
112+ revocation : false
113+ ) . freeze
114+ end
115+ end
116+
91117 Puppet ::SSL ::SSLContext . new ( store : store , cacerts : cacerts , crls : [ ] , revocation : false ) . freeze
92118 end
93119
@@ -124,15 +150,7 @@ def create_context(cacerts:, crls:, private_key:, client_cert:, revocation: Pupp
124150 raise ArgumentError , _ ( "Client cert is missing" ) unless client_cert
125151
126152 store = create_x509_store ( cacerts , crls , revocation , include_system_store : include_system_store )
127- client_chain = verify_cert_with_store ( store , client_cert )
128-
129- if !private_key . is_a? ( OpenSSL ::PKey ::RSA ) && !private_key . is_a? ( OpenSSL ::PKey ::EC )
130- raise Puppet ::SSL ::SSLError , _ ( "Unsupported key '%{type}'" ) % { type : private_key . class . name }
131- end
132-
133- unless client_cert . check_private_key ( private_key )
134- raise Puppet ::SSL ::SSLError , _ ( "The certificate for '%{name}' does not match its private key" ) % { name : subject ( client_cert ) }
135- end
153+ client_chain = resolve_client_chain ( store , client_cert , private_key )
136154
137155 Puppet ::SSL ::SSLContext . new (
138156 store : store , cacerts : cacerts , crls : crls ,
@@ -258,6 +276,20 @@ def revocation_mode(mode)
258276 end
259277 end
260278
279+ def resolve_client_chain ( store , client_cert , private_key )
280+ client_chain = verify_cert_with_store ( store , client_cert )
281+
282+ if !private_key . is_a? ( OpenSSL ::PKey ::RSA ) && !private_key . is_a? ( OpenSSL ::PKey ::EC )
283+ raise Puppet ::SSL ::SSLError , _ ( "Unsupported key '%{type}'" ) % { type : private_key . class . name }
284+ end
285+
286+ unless client_cert . check_private_key ( private_key )
287+ raise Puppet ::SSL ::SSLError , _ ( "The certificate for '%{name}' does not match its private key" ) % { name : subject ( client_cert ) }
288+ end
289+
290+ client_chain
291+ end
292+
261293 def verify_cert_with_store ( store , cert )
262294 # StoreContext#initialize accepts a chain argument, but it's set to [] because
263295 # puppet requires any intermediate CA certs needed to complete the client's
0 commit comments