The agent's state machine is responsible for loading CA certs, CRLs, private keys and client certs. Along the way, it may need to download the CA certs, submit a CSR, etc. Each of those steps is represented by an SSL state: If the agent successfully loads its client cert in https://github.com/puppetlabs/puppet/blob/ad7d75b08dfff5e308fde199407d84308d74e538/lib/puppet/ssl/state_machine.rb#L181-L186 AND If the client cert's expiration date (notAfter) is within the expiration window THEN transition to a new state "NeedRenewedCert" passing along the next_ctx containing the loaded client cert. The NeedRenewedCert state's "next_state" method should submit a REST request to the new CA endpoint to renew its client cert using the newly added method in https://github.com/puppetlabs/puppet/blob/main/lib/puppet/http/service/ca.rb. The state must pass the "ssl_context" containing the "client cert to be renewed" to the HTTP client, so that it's sent as part of the TLS handshake. If the request is successful, the client MUST verify the RENEWED client cert is valid before committing it to disk. See how this is done here for newly provisioned agents: https://github.com/puppetlabs/puppet/blob/ad7d75b08dfff5e308fde199407d84308d74e538/lib/puppet/ssl/state_machine.rb#L250-L254. The "create_context" method will either return a valid "ssl_context" or raise. If an "ssl_context" is returned, then the NeedRenewedCert state should return a "Done" state containing the new ssl_context, like https://github.com/puppetlabs/puppet/blob/ad7d75b08dfff5e308fde199407d84308d74e538/lib/puppet/ssl/state_machine.rb#LL256C1-L257C1 If an exception is raised, the "NeedRenewedCert" state should 404 - log an info message that autorenewal is disabled on the sever 4xx/5xx - log a warning AND return a "Done" state containing the ORIGINAL ssl_context/client cert Add unit tests in https://github.com/puppetlabs/puppet/blob/ad7d75b08dfff5e308fde199407d84308d74e538/spec/unit/ssl/state_machine_spec.rb In NeedKey tests:
In NeedRenewedCert tests:
- If successful, verify it returns a Done state with the renewed client cert, e.g.
st = state.next_state |
expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Done) |
expect(st.ssl_context[:client_cert]).to eq(NEW CERT)
|
- If not successful, verify it returns a Done state with the old client cert, e.g.
st = state.next_state |
expect(st).to be_an_instance_of(Puppet::SSL::StateMachine::Done) |
expect(st.ssl_context[:client_cert]).to eq(OLD CERT)
|
- If not successful with 404, verify it logs an info message
- If not successful due to other code, verify it logs an error message
|