@@ -50,14 +50,28 @@ def initialize(machine)
5050 def next_state
5151 Puppet . debug ( "Loading CA certs" )
5252
53+ force_crl_refresh = false
54+
5355 cacerts = @cert_provider . load_cacerts
5456 if cacerts
5557 next_ctx = @ssl_provider . create_root_context ( cacerts : cacerts , revocation : false )
58+
59+ now = Time . now
60+ last_update = @cert_provider . ca_last_update
61+ if needs_refresh? ( now , last_update )
62+ # set last updated time first, then make a best effort to refresh
63+ @cert_provider . ca_last_update = now
64+
65+ # If we refresh the CA, then we need to force the CRL to be refreshed too,
66+ # since if there is a new CA in the chain, then we need its CRL to check
67+ # the full chain for revocation status.
68+ next_ctx , force_crl_refresh = refresh_ca ( next_ctx , last_update )
69+ end
5670 else
5771 route = @machine . session . route_to ( :ca , ssl_context : @ssl_context )
5872 _ , pem = route . get_certificate ( Puppet ::SSL ::CA_NAME , ssl_context : @ssl_context )
5973 if @machine . ca_fingerprint
60- actual_digest = Puppet :: SSL :: Digest . new ( @machine . digest , pem ) . to_hex
74+ actual_digest = @machine . digest_as_hex ( pem )
6175 expected_digest = @machine . ca_fingerprint . scan ( /../ ) . join ( ':' ) . upcase
6276 if actual_digest == expected_digest
6377 Puppet . info ( _ ( "Verified CA bundle with digest (%{digest_type}) %{actual_digest}" ) %
@@ -74,7 +88,7 @@ def next_state
7488 @cert_provider . save_cacerts ( cacerts )
7589 end
7690
77- NeedCRLs . new ( @machine , next_ctx )
91+ NeedCRLs . new ( @machine , next_ctx , force_crl_refresh )
7892 rescue OpenSSL ::X509 ::CertificateError => e
7993 Error . new ( @machine , e . message , e )
8094 rescue Puppet ::HTTP ::ResponseError => e
@@ -84,6 +98,51 @@ def next_state
8498 to_error ( _ ( 'Could not download CA certificate: %{message}' ) % { message : e . message } , e )
8599 end
86100 end
101+
102+ private
103+
104+ def needs_refresh? ( now , last_update )
105+ return true if last_update . nil?
106+
107+ ca_ttl = Puppet [ :ca_refresh_interval ]
108+ return false unless ca_ttl
109+
110+ now . to_i > last_update . to_i + ca_ttl
111+ end
112+
113+ def refresh_ca ( ssl_ctx , last_update )
114+ Puppet . info ( _ ( "Refreshing CA certificate" ) )
115+
116+ # return the next_ctx containing the updated ca
117+ [ download_ca ( ssl_ctx , last_update ) , true ]
118+ rescue Puppet ::HTTP ::ResponseError => e
119+ if e . response . code == 304
120+ Puppet . info ( _ ( "CA certificate is unmodified, using existing CA certificate" ) )
121+ else
122+ Puppet . info ( _ ( "Failed to refresh CA certificate, using existing CA certificate: %{message}" ) % { message : e . message } )
123+ end
124+
125+ # return the original ssl_ctx
126+ [ ssl_ctx , false ]
127+ rescue Puppet ::HTTP ::HTTPError => e
128+ Puppet . warning ( _ ( "Failed to refresh CA certificate, using existing CA certificate: %{message}" ) % { message : e . message } )
129+
130+ # return the original ssl_ctx
131+ [ ssl_ctx , false ]
132+ end
133+
134+ def download_ca ( ssl_ctx , last_update )
135+ route = @machine . session . route_to ( :ca , ssl_context : ssl_ctx )
136+ _ , pem = route . get_certificate ( Puppet ::SSL ::CA_NAME , if_modified_since : last_update , ssl_context : ssl_ctx )
137+ cacerts = @cert_provider . load_cacerts_from_pem ( pem )
138+ # verify cacerts before saving
139+ next_ctx = @ssl_provider . create_root_context ( cacerts : cacerts , revocation : false )
140+ @cert_provider . save_cacerts ( cacerts )
141+
142+ Puppet . info ( "Refreshed CA certificate: #{ @machine . digest_as_hex ( pem ) } " )
143+
144+ next_ctx
145+ end
87146 end
88147
89148 # If revocation is enabled, load CRLs or download them, using the CA bundle
@@ -93,6 +152,13 @@ def next_state
93152 # for which we don't have a CRL
94153 #
95154 class NeedCRLs < SSLState
155+ attr_reader :force_crl_refresh
156+
157+ def initialize ( machine , ssl_context , force_crl_refresh = false )
158+ super ( machine , ssl_context )
159+ @force_crl_refresh = force_crl_refresh
160+ end
161+
96162 def next_state
97163 Puppet . debug ( "Loading CRLs" )
98164
@@ -102,15 +168,12 @@ def next_state
102168 if crls
103169 next_ctx = @ssl_provider . create_root_context ( cacerts : ssl_context [ :cacerts ] , crls : crls )
104170
105- crl_ttl = Puppet [ :crl_refresh_interval ]
106- if crl_ttl
107- last_update = @cert_provider . crl_last_update
108- now = Time . now
109- if last_update . nil? || now . to_i > last_update . to_i + crl_ttl
110- # set last updated time first, then make a best effort to refresh
111- @cert_provider . crl_last_update = now
112- next_ctx = refresh_crl ( next_ctx , last_update )
113- end
171+ now = Time . now
172+ last_update = @cert_provider . crl_last_update
173+ if needs_refresh? ( now , last_update )
174+ # set last updated time first, then make a best effort to refresh
175+ @cert_provider . crl_last_update = now
176+ next_ctx = refresh_crl ( next_ctx , last_update )
114177 end
115178 else
116179 next_ctx = download_crl ( @ssl_context , nil )
@@ -133,6 +196,15 @@ def next_state
133196
134197 private
135198
199+ def needs_refresh? ( now , last_update )
200+ return true if @force_crl_refresh || last_update . nil?
201+
202+ crl_ttl = Puppet [ :crl_refresh_interval ]
203+ return false unless crl_ttl
204+
205+ now . to_i > last_update . to_i + crl_ttl
206+ end
207+
136208 def refresh_crl ( ssl_ctx , last_update )
137209 Puppet . info ( _ ( "Refreshing CRL" ) )
138210
@@ -162,6 +234,8 @@ def download_crl(ssl_ctx, last_update)
162234 next_ctx = @ssl_provider . create_root_context ( cacerts : ssl_ctx [ :cacerts ] , crls : crls )
163235 @cert_provider . save_crls ( crls )
164236
237+ Puppet . info ( "Refreshed CRL: #{ @machine . digest_as_hex ( pem ) } " )
238+
165239 next_ctx
166240 end
167241 end
@@ -441,6 +515,10 @@ def unlock
441515 @lockfile . unlock
442516 end
443517
518+ def digest_as_hex ( str )
519+ Puppet ::SSL ::Digest . new ( digest , str ) . to_hex
520+ end
521+
444522 private
445523
446524 def run_machine ( state , stop )
0 commit comments