Jira (PUP-11856) Update agent state machine to renew client cert

0 views
Skip to first unread message

Josh Cooper (Jira)

unread,
May 18, 2023, 2:38:03 PM5/18/23
to puppe...@googlegroups.com
Josh Cooper created an issue
 
Puppet / Task PUP-11856
Update agent state machine to renew client cert
Issue Type: Task Task
Assignee: Unassigned
Created: 2023/05/18 11:37 AM
Priority: Normal Normal
Reporter: Josh Cooper

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
Add Comment Add Comment
 
This message was sent by Atlassian Jira (v8.20.21#820021-sha1:38274c8)
Atlassian logo

Josh Cooper (Jira)

unread,
Jun 1, 2023, 12:32:03 PM6/1/23
to puppe...@googlegroups.com
Josh Cooper updated an issue
Change By: Josh Cooper
Sprint: Phoenix 2023-06-21

Josh Cooper (Jira)

unread,
Jun 1, 2023, 1:46:01 PM6/1/23
to puppe...@googlegroups.com

Josh Cooper (Jira)

unread,
Jun 1, 2023, 1:48:02 PM6/1/23
to puppe...@googlegroups.com
Josh Cooper updated an issue
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|https://github.com/puppetlabs/puppet/blob/ad7d75b08dfff5e308fde199407d84308d74e538/lib/puppet/ssl/state_machine.rb#L183] 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 (calling {{@cert_provider . save_client_cert(Puppet[:certname], cert)}}. 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:
* Verify it returns NeedRenewedCert if the current time plus the renewal interval is greater than cert's "notAfter" time, similar to https://github.com/puppetlabs/puppet/blob/ad7d75b08dfff5e308fde199407d84308d74e538/spec/unit/ssl/state_machine_spec.rb#L601-L607
* Verify it returns Done if the current time plus the renewal interval is less than or equal to the cert's "notAfter" time.

In NeedRenewedCert tests:
* If successful, verify it returns a Done state with the renewed client cert, e.g.
{noformat}

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)
{noformat}
* If not successful, verify it returns a Done state with the old client cert, e.g.
{noformat}

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)
{noformat}
* If not successful with 404, verify it logs an info message
* If not successful due to other code, verify it logs an error message

Josh Cooper (Jira)

unread,
Jun 7, 2023, 1:05:01 PM6/7/23
to puppe...@googlegroups.com
Josh Cooper updated an issue
Change By: Josh Cooper
Sprint: Phoenix 2023-06-21

Josh Cooper (Jira)

unread,
Jun 14, 2023, 11:52:02 PM6/14/23
to puppe...@googlegroups.com
Josh Cooper updated an issue
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 : {{Time.now >= (notAfter - hostcert_refresh_interval)}}
THEN
transition to a new state "NeedRenewedCert" passing along the [next_ctx|https://github.com/puppetlabs/puppet/blob/ad7d75b08dfff5e308fde199407d84308d74e538/lib/puppet/ssl/state_machine.rb#L183] 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 (calling {{
{} @cert_provider.save_client_cert(Puppet[:certname], cert) { }} } . 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

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

In NeedKey tests:
* Verify it returns NeedRenewedCert if the current time plus the renewal interval is greater than cert's "notAfter" time, similar to
* Verify it returns Done if the current time plus the renewal interval is less than or equal to the cert's "notAfter" time.

In NeedRenewedCert tests:
* If successful, verify it returns a Done state with the renewed client cert, e.g.
{noformat}
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)
{noformat}
* If not successful, verify it returns a Done state with the old client cert, e.g.
{noformat}

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)
{noformat}
* If not successful with 404, verify it logs an info message
* If not successful due to other code, verify it logs an error message

Josh Cooper (Jira)

unread,
Jun 15, 2023, 12:42:02 PM6/15/23
to puppe...@googlegroups.com
Josh Cooper updated an issue
Change By: Josh Cooper
Sprint: Phoenix 2023-07-05

Aria Li (Jira)

unread,
Jun 20, 2023, 12:25:02 PM6/20/23
to puppe...@googlegroups.com
Aria Li assigned an issue to Aria Li
Change By: Aria Li
Assignee: Aria Li

Aria Li (Jira)

unread,
Jun 20, 2023, 12:40:02 PM6/20/23
to puppe...@googlegroups.com
Aria Li assigned an issue to Unassigned

Michael Hashizume (Jira)

unread,
Jun 21, 2023, 2:13:03 PM6/21/23
to puppe...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages