Siyao Meng created HDDS-16936:
---------------------------------
Summary: SCM issues SCM role certificates without the SCM admin
check and copies the CA basic constraint for every role
Key: HDDS-16936
URL: https://issues.apache.org/jira/browse/HDDS-16936
Project: Apache Ozone
Issue Type: Bug
Reporter: Siyao Meng
Attachments: CR-3-require-scm-admin-for-scm-role-certificates.patch
h3. Mechanism
{{SCMSecurityProtocolServer.getEncodedCertToString}} signs every CSR for the
node type the request names. For {{NodeType.SCM}} it uses the root CA server
when the SCM holds one and the sub CA server otherwise, and
{{DefaultCAServer.getDuration}} gives that role {{hdds.x509.max.duration}}; the
result is an SCM sub CA certificate, stored with role SCM.
Two RPCs reach it with that role.
{{SCMSecurityProtocolServer.getSCMCertificate}} compares the cluster id of the
request with its own and calls {{ScmUtils.checkIfCertSignRequestAllowed}} (the
root CA rotation window). {{SCMSecurityProtocolServer.getCertificate}} (the
{{NodeDetailsProto}} form, added for Recon in HDDS-6212) forwards
{{nodeDetails.getNodeType()}} and checks nothing else. Neither consults
{{StorageContainerManager.checkAdminAccess}}; in this server only
{{removeExpiredCertificates}} does, while {{SCMBlockProtocolServer.addSCM}},
the other operation an SCM peer performs when it joins, does.
Separately, the sub CA server that issues OM, datanode and Recon certificates
is built with {{DefaultCAProfile}} ({{StorageContainerManager}}, HDDS-5052),
whose extension map accepts the basic constraints extension, and
{{DefaultApprover.sign}} copies every extension the profile lists from the CSR
into the certificate. So a basic constraints extension in a CSR is copied into
the certificate for every role, not only SCM.
{{DefaultCAServer.requestCertificate}} also completes its future exceptionally
when {{DefaultApprover.inspectCSR}} rejects the CSR and then continues into
{{signAndStoreCertificate}}, so a rejected CSR is still signed and persisted.
h3. Trigger
# An SCM role signing request ({{GetSCMCertificate}}, or {{GetCertificate}}
with node type {{SCM}}) whose remote user is not in the SCM admin set.
# A CSR for a role other than SCM that carries the basic constraints extension,
handled by a {{DefaultCAServer}} built with {{DefaultCAProfile}}.
# A CSR that {{DefaultApprover.inspectCSR}} rejects.
By history, not run on older code: {{getSCMCertificate}} and the
{{DefaultCAProfile}} sub CA arrived with HDDS-4861 and HDDS-5052 (1.2.0), the
{{NodeDetailsProto}} form with HDDS-6212 (1.3.0). The three core files are byte
identical between the pin and the 2.2.1 tag.
h3. Impact
* The SCM admin check that gates the other SCM peer operation ({{addSCM}}) does
not gate the issuance of SCM role certificates, which are CA certificates under
the cluster root with the maximum duration, and the CA basic constraint is not
limited to the SCM role.
* A CSR the approver rejected is still persisted in the SCM certificate store
and returned by {{listCertificate}}.
h3. Reproduction
The regression tests in the attached patch cover it and fail on unmodified
source. There is no standalone reproduction.
* {{TestDefaultCAServer.testBasicConstraintsAllowedForScmRoleOnly}}
({{hadoop-hdds/framework}}): a basic constraints CSR for the OM role is
rejected and not stored, the same CSR for the SCM role is issued with the
constraint and stored, and a CSR the profile rejects is neither signed nor
stored ({{MockCAStore}} now records what is stored). Without the change it
fails with {{Expected java.util.concurrent.ExecutionException to be thrown, but
nothing was thrown}}.
*
{{TestSecureOzoneCluster.assertScmSecurityProtocolRequiresAdminForScmRoleCertificate}}
({{hadoop-ozone/integration-test}}, inside
{{testSecureScmAndOmStartupAndAccessControl}} next to the existing SCM admin
protocol checks): both SCM role signing RPCs answer {{Access denied}} for a
remote user outside the SCM admin set. Without the change it fails with
{{Expected java.io.IOException to be thrown, but nothing was thrown}}.
h3. Patch
[^CR-3-require-scm-admin-for-scm-role-certificates.patch], against
b9b33851b20fa11baf4b07982de9afb8ea44f85a. It also applies to master at
084c60d4fb9.
{{SCMSecurityProtocolServer.getEncodedCertToString}} calls {{checkAdminAccess}}
with the RPC remote user when the role is SCM, so both RPCs are covered in one
place. {{DefaultCAServer.requestCertificate}} rejects a CSR that carries the
basic constraints extension for any role other than SCM before signing, and
returns without signing when the approver rejects the CSR. {{MockCAStore}}
records stored certificates for the test.
No wire or protocol change and no configuration change. A bootstrapping or
renewing SCM requests its certificate under its own login user
({{SCMCertificateClient}}), and the SCM admin set always contains the starter
user plus {{ozone.administrators}}, the same membership {{addSCM}} already
requires of it, so no new deployment requirement. OM, datanode and Recon CSRs
never carry the basic constraints extension (only {{SCMCertificateClient}} sets
it), so the second change rejects no legitimate CSR. In a mixed version cluster
an older SCM against a patched leader passes the same check it already passes
for {{addSCM}}, and a patched SCM against an older primary meets no new check.
Outside RPC, with no remote user, the check is a no op, as for
{{removeExpiredCertificates}}; with {{ozone.authorization.enabled}} false it
returns early, as every SCM admin check does.
With the patch {{TestDefaultCAServer}}, {{TestDefaultProfile}}
({{hadoop-hdds/framework}}) and {{TestRootCARotationManager}}
({{hadoop-hdds/server-scm}}) pass, the SCM part of
{{TestSecureOzoneCluster.testSecureScmAndOmStartupAndAccessControl}} passes,
and checkstyle on {{hadoop-hdds/framework}}, {{hadoop-hdds/server-scm}} and
{{hadoop-ozone/integration-test}} is clean. Not run: a secure multi SCM
bootstrap or certificate renewal with the check in place ({{TestSecretKeysApi}}
did not start on the test machine for a host name resolution reason unrelated
to the change), and the OM part of
{{testSecureScmAndOmStartupAndAccessControl}} times out on that machine with
and without the patch for the same reason. That bootstrap and renewal keep
working follows from the shared login user by code reading and should be
confirmed on CI before merge.
Found by code review of the SCM root CA rotation paths, as part of the TLA+
verification effort 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 Fable 5.1).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]