[ 
https://issues.apache.org/jira/browse/HDDS-16915?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Siyao Meng updated HDDS-16915:
------------------------------
    Attachment: MC-6-complete-prepared-rotation-after-checkpoint-install.patch

> SCM follower that installs a checkpoint taken after a root CA rotation commit 
> stays on the old root certificate
> ---------------------------------------------------------------------------------------------------------------
>
>                 Key: HDDS-16915
>                 URL: https://issues.apache.org/jira/browse/HDDS-16915
>             Project: Apache Ozone
>          Issue Type: Bug
>            Reporter: Siyao Meng
>            Priority: Major
>         Attachments: 
> MC-6-complete-prepared-rotation-after-checkpoint-install.patch, 
> TestBugMC6CheckpointSkipsRotation.java
>
>
> h3. Mechanism
> {{RootCARotationHandlerImpl.rotationCommit}} and {{rotationCommitted}} are 
> replicated requests, but what they change is local to each SCM: they move 
> {{scm/sub-ca-next}} to {{scm/sub-ca}}, write the new certificate ID to the 
> VERSION file and reload the certificate client. None of this is in the SCM 
> DB. When a follower catches up by installing a checkpoint, 
> {{SCMHAManagerImpl.installCheckpoint}} replaces the DB and reloads the DB 
> backed managers. The applied index moves past the two requests and the 
> follower never applies them. It keeps its sub CA certificate under the old 
> root, and the certificate it prepared for the new root stays in 
> {{scm/sub-ca-next}}.
> h3. Trigger
> # Secure SCM HA with {{hdds.x509.ca.rotation.enabled=true}} and 
> {{ozone.scm.ha.ratis.log.purge.enabled=true}} (both false by default).
> # A root CA rotation starts. Every SCM prepares a new sub CA certificate and 
> acks.
> # One follower stops receiving from the leader before the {{rotationCommit}} 
> request reaches it.
> # The other SCMs commit the rotation. The leader takes a snapshot and purges 
> its log past the rotation requests.
> # The follower is reachable again and installs the checkpoint.
> The follower has to drop out between its ack and the commit and stay away 
> until the leader has purged. By code reading, with the default segment size 
> (64MB) and purge gap (1000000) that needs a long outage. Without log purging 
> the leader keeps the requests and the follower applies them from the log 
> (code reading, not run).
> h3. Impact
> Observed on a mini cluster, where Ratis runs without TLS. Wherever the 
> follower becomes leader below, the test moved leadership to it: with the 
> admin command after the polling interval, and with a Ratis leadership 
> transfer inside it, because the admin command is refused there.
> * The follower keeps following the log, with its replicated state past the 
> rotation and its certificates on the old root. Until it is restarted it 
> trusts only the old root.
> * A restart does not repair it. After one restart it also trusts the new 
> root. After a second restart {{SCMCertificateClient.getCACertificate}} 
> returns the new root, while its own certificate chain, the VERSION file and 
> {{scm/sub-ca}} are still on the old one.
> * If it becomes leader inside 
> {{hdds.x509.rootca.certificate.polling.interval}} after the rotation (run 
> after one restart), it terminates at leader ready with "Root CA certificate 
> ID 7 in stateful storage is bigger than current scm's root CA certificate ID 
> 1". Another SCM took over 10s later.
> * If it becomes leader later (run without a restart; after one restart by 
> code reading, because {{getCACertificate}} still returns the old root then), 
> {{RootCARotationManager.MonitorTask}} sees the old root inside its grace 
> period and starts another rotation. All SCMs end on a third root and the root 
> that was just introduced is replaced. This is the only way the SCM recovers. 
> The rotation started at once in the test. By code reading it is scheduled for 
> the next {{hdds.x509.ca.rotation.time-of-day}} when the old root is still 
> valid at that time.
> * If it becomes leader later and was restarted twice (run), no rotation 
> starts. It stays leader and signs with the sub CA certificate under the old 
> root.
> Argued, not run: the sub CA certificate of the follower expires with the old 
> root, at most twice {{hdds.x509.renew.grace.duration}} after the rotation, 
> and nothing but a rotation replaces an SCM sub CA certificate. With 
> {{hdds.grpc.tls.enabled=true}}, Ratis between SCMs and the checkpoint 
> download ({{InterSCMGrpcClient}}, {{InterSCMGrpcProtocolService}}) both use 
> the trust manager of the certificate client and require a client certificate. 
> A follower that was cut off and still trusts only the old root can then 
> neither accept requests from the leader nor download the checkpoint, so step 
> 5 of the trigger needs a restart of the follower first, which makes it fetch 
> the new root at startup. That order was not run, with or without the patch.
> By code reading, not run: the release tags ozone-1.4.0 to ozone-2.2.1 carry 
> the same {{rotationCommit}} and the same checkpoint install.
> h3. Reproduction
> PASS, unmodified source. [^TestBugMC6CheckpointSkipsRotation.java] 
> ({{hadoop-ozone/integration-test}}) starts a secure mini cluster with three 
> SCMs. Certificate lifetimes are shortened so that the leader rotates the root 
> CA three minutes after SCM init, and log purging is enabled with a small 
> segment size, purge gap and snapshot threshold. The only intervention is a 
> partition: the Ratis test hooks of {{RaftServerImpl.appendEntries}} and 
> {{RaftServerImpl.installSnapshot}} reject what is sent to the first follower 
> that is offered the {{rotationCommit}} entry, until the leader has purged 
> that entry. After the follower has installed the checkpoint, the new root is 
> in its certificate store while its root certificate and VERSION file are the 
> old ones and {{scm/sub-ca-next}} is still present. A passing test means the 
> defect is present. It takes about three minutes.
> h3. Patch
> [^MC-6-complete-prepared-rotation-after-checkpoint-install.patch], against 
> b9b33851b20fa11baf4b07982de9afb8ea44f85a. It also applies to master at 
> 5108acff480.
> After a checkpoint install, 
> {{RootCARotationManager.completePreparedRotation}} looks at the certificate 
> prepared in {{scm/sub-ca-next}}. If its root is newer than the current one 
> and is present in the certificate store, the rotation to it is committed, 
> because the leader stores a new root there only after {{rotationCommitted}}. 
> The SCM then runs the commit steps locally, through a 
> {{RootCARotationHandlerImpl}} that is not wrapped in the Ratis proxy. 
> Otherwise nothing changes.
> A failure while reading the prepared certificate or deciding is logged and 
> leaves the SCM as it is today. A failed commit step is not caught, so the SCM 
> never keeps running with its directories half switched. At this commit the 
> handler stops the SCM itself, as it does for a commit applied from the log 
> (run: no further step is executed and the directories are untouched; the stop 
> does not finish when it is called on this thread, which is the existing 
> behaviour of the handler, HDDS-16911, and not changed here). An exception 
> from the handler fails {{installCheckpoint}} and 
> {{SCMStateMachine.reinitialize}}, Ratis closes the server and the SCM 
> terminates (run with the handler changed to throw).
> Covered by {{testCompletePreparedRotation}} and 
> {{testCompletePreparedRotationThrowsFailedCommitStep}} in the existing 
> {{TestRootCARotationManager}}, for a prepared rotation that is not committed, 
> for one that is, and for a commit step that fails. The call from 
> {{installCheckpoint}} is exercised by the attached reproduction only, because 
> no existing suite combines a secure SCM HA cluster with a root CA rotation. 
> The reproduction with its last three assertions inverted can serve as an 
> integration test if one is wanted. With the patch the SCM HA and root CA 
> rotation unit suites of {{hadoop-hdds/server-scm}} (114 tests), 
> {{TestSCMInstallSnapshot}}, {{TestSCMInstallSnapshotWithHA}} and 
> {{TestSecretKeySnapshot}} pass and checkstyle is clean. The reproduction then 
> fails at the assertion that expects the old root: the follower logs "Rotation 
> to root certificate 7 is committed, switching to the prepared sub CA 
> certificate 10" during the install and ends on the new root with 
> {{scm/sub-ca-next}} gone. In a further run it became leader inside the 
> polling interval and kept running without starting a rotation.
> Not covered by the patch: an SCM that is already in this state is not 
> repaired at startup. Nothing happens when the new root is not yet, or never, 
> in the certificate store of the installed checkpoint, that is a checkpoint 
> taken before the leader has stored it, or a leader that failed before storing 
> it. A follower that applied {{rotationCommit}} but not {{rotationCommitted}} 
> before it was cut off is not completed either.
> Found by TLA+ model checking and code review of the SCM root CA rotation 
> paths under HDDS-15926, on commit b9b33851b20fa11baf4b07982de9afb8ea44f85a. 
> Checked against HDDS issues and apache/ozone pull requests for duplicates 
> before filing. The attached patch is a proposal for review. Generated with 
> Specula (Claude Opus 5.5).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to