You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by si...@apache.org on 2026/10/17 18:35:10 UTC
(ozone) 12/13: Improve certificate request handling in SCMSecurityProtocolServer and DefaultCAServer.
This is an automated email from the ASF dual-hosted git repository.
smengcl pushed a commit to branch ozone-2.2
in repository https://gitbox.apache.org/repos/asf/ozone.git
commit 803d09e128860fc03cd4ca931d33b8d330bdc950
Author: Siyao Meng <50...@users.noreply.github.com>
AuthorDate: Fri Oct 16 21:18:49 2026 -0700
Improve certificate request handling in SCMSecurityProtocolServer and DefaultCAServer.
(cherry picked from commit 7f9b6237deb2b06d73d1ac2b78cccaa60780c3ef)
Conflicts:
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
Co-Authored-By: Claude Opus 5.5 <no...@anthropic.com>
---
.../org/apache/hadoop/hdds/recon/ReconConfig.java | 3 +-
.../common/src/main/resources/ozone-default.xml | 6 ++-
.../certificate/authority/DefaultCAServer.java | 15 +++++-
.../authority/profile/DefaultCAProfile.java | 14 ++++--
.../authority/profile/DefaultProfile.java | 2 +-
.../certificate/authority/TestDefaultCAServer.java | 3 +-
.../certificate/authority/TestDefaultProfile.java | 15 ++++++
.../hdds/scm/server/SCMSecurityProtocolServer.java | 34 ++++++++++++++
.../apache/hadoop/hdds/scm/TestHddsServerUtil.java | 16 +++++++
.../hadoop/ozone/TestSecureOzoneCluster.java | 54 ++++++++++++++++++++++
10 files changed, 151 insertions(+), 11 deletions(-)
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/recon/ReconConfig.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/recon/ReconConfig.java
index de6e0697a9f..78b4d7d4372 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/recon/ReconConfig.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/recon/ReconConfig.java
@@ -32,7 +32,8 @@ public class ReconConfig {
type = ConfigType.STRING,
defaultValue = "",
tags = { ConfigTag.SECURITY, ConfigTag.RECON, ConfigTag.OZONE },
- description = "This Kerberos principal is used by the Recon service."
+ description = "This Kerberos principal is used by the Recon service. SCM issues a Recon certificate only to this"
+ + " service user, so the property must be set in the configuration of SCM as well."
)
private String principal;
diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml b/hadoop-hdds/common/src/main/resources/ozone-default.xml
index eef89592300..38ce72f7707 100644
--- a/hadoop-hdds/common/src/main/resources/ozone-default.xml
+++ b/hadoop-hdds/common/src/main/resources/ozone-default.xml
@@ -2438,8 +2438,8 @@
<value>OM/_HOST@REALM</value>
<tag>OZONE, SECURITY, KERBEROS</tag>
<description>The OzoneManager service principal. Ex om/_HOST@REALM.COM
- SCM allocates and deletes blocks only for this service user or an SCM admin, so the property must be set in the
- configuration of SCM as well.
+ SCM issues an OM certificate only to this service user, and allocates and deletes blocks only for this service
+ user or an SCM admin, so the property must be set in the configuration of SCM as well.
</description>
</property>
<property>
@@ -5005,6 +5005,8 @@
dn/_HOST@REALM.TLD. Each Datanode will substitute _HOST with its
own fully qualified hostname at startup. The _HOST placeholder
allows using the same configuration setting on all Datanodes.
+ SCM issues a Datanode certificate only to this service user, so the
+ property must be set in the configuration of SCM as well.
</description>
</property>
<property>
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java
index 9e67e0d0b72..c2ca72ad419 100644
--- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java
@@ -49,6 +49,7 @@
import org.apache.hadoop.hdds.scm.metadata.SCMMetadataStore;
import org.apache.hadoop.hdds.security.SecurityConfig;
import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
+import org.apache.hadoop.hdds.security.x509.certificate.authority.profile.DefaultProfile;
import org.apache.hadoop.hdds.security.x509.certificate.authority.profile.PKIProfile;
import org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateCodec;
import org.apache.hadoop.hdds.security.x509.certificate.utils.SelfSignedCertificate;
@@ -120,6 +121,7 @@ public class DefaultCAServer implements CertificateServer {
*/
private PKIProfile profile;
private CertificateApprover approver;
+ private CertificateApprover leafApprover;
private CertificateStore store;
private Lock lock;
private BigInteger rootCertificateId;
@@ -157,6 +159,7 @@ public void init(SecurityConfig securityConfig, CAType type)
caRootX509Path = securityConfig.getCertificateLocation(componentName);
this.config = securityConfig;
this.approver = new DefaultApprover(profile, this.config);
+ this.leafApprover = new DefaultApprover(new DefaultProfile(), this.config);
/* In future we will split this code to have different kind of CAs.
* Right now, we have only self-signed CertificateServer.
@@ -210,7 +213,7 @@ public Future<CertPath> requestCertificate(
String certSerialId) {
Duration certDuration = getDuration(role);
- CompletableFuture<Void> csrInspection = approver.inspectCSR(csr);
+ CompletableFuture<Void> csrInspection = getApprover(role).inspectCSR(csr);
CompletableFuture<CertPath> certPathPromise = new CompletableFuture<>();
// An inspection that has not completed is not an approval.
if (!csrInspection.isDone()) {
@@ -254,6 +257,14 @@ public Future<CertPath> requestCertificate(
return certPathPromise;
}
+ /**
+ * Only an SCM can be issued a CA certificate, by this CA whether it is the root CA or the intermediate CA of another
+ * SCM (HDDS-8286). The other roles get a leaf certificate whatever the profile of this CA.
+ */
+ private CertificateApprover getApprover(NodeType role) {
+ return role == NodeType.SCM ? approver : leafApprover;
+ }
+
private Duration getDuration(NodeType role) {
// When issuing certificates for sub-ca use the max certificate duration similar to self-signed root certificate.
if (role == NodeType.SCM) {
@@ -273,7 +284,7 @@ private X509Certificate signAndStoreCertificate(
X509Certificate xcert;
try {
Preconditions.checkState(!Strings.isNullOrEmpty(certSerialId));
- xcert = approver.sign(config,
+ xcert = getApprover(role).sign(config,
getPrivateKey(),
getCACertificate(),
Date.from(beginDate.toInstant()),
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/profile/DefaultCAProfile.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/profile/DefaultCAProfile.java
index 5aa87d5aea1..1a38e9c341d 100644
--- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/profile/DefaultCAProfile.java
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/profile/DefaultCAProfile.java
@@ -17,6 +17,7 @@
package org.apache.hadoop.hdds.security.x509.certificate.authority.profile;
+import java.util.HashMap;
import java.util.Map;
import java.util.function.BiPredicate;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -33,6 +34,14 @@ public class DefaultCAProfile extends DefaultProfile {
private static final BiPredicate<Extension, PKIProfile>
VALIDATE_BASIC_CONSTRAINTS = DefaultCAProfile::validateBasicExtensions;
+ // Basic constraints are accepted by the CA profile only.
+ private static final Map<ASN1ObjectIdentifier, BiPredicate<Extension, PKIProfile>> CA_EXTENSIONS_MAP =
+ new HashMap<>(EXTENSIONS_MAP);
+
+ static {
+ CA_EXTENSIONS_MAP.put(Extension.basicConstraints, VALIDATE_BASIC_CONSTRAINTS);
+ }
+
private static boolean validateBasicExtensions(Extension ext,
PKIProfile pkiProfile) {
BasicConstraints constraints =
@@ -48,10 +57,7 @@ public boolean isCA() {
@Override
public Map<ASN1ObjectIdentifier,
BiPredicate<Extension, PKIProfile>> getExtensionsMap() {
- // Add basic constraint.
- EXTENSIONS_MAP.putIfAbsent(Extension.basicConstraints,
- VALIDATE_BASIC_CONSTRAINTS);
- return EXTENSIONS_MAP;
+ return CA_EXTENSIONS_MAP;
}
@Override
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/profile/DefaultProfile.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/profile/DefaultProfile.java
index b4c7ad5d4e1..8e2fa736550 100644
--- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/profile/DefaultProfile.java
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/profile/DefaultProfile.java
@@ -269,7 +269,7 @@ public boolean validateExtension(Extension extension) {
return false;
}
- return EXTENSIONS_MAP.getOrDefault(extension.getExtnId(), (x, y) -> false)
+ return getExtensionsMap().getOrDefault(extension.getExtnId(), (x, y) -> false)
.test(extension, this);
}
diff --git a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/authority/TestDefaultCAServer.java b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/authority/TestDefaultCAServer.java
index 77776499841..83989c68c5c 100644
--- a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/authority/TestDefaultCAServer.java
+++ b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/authority/TestDefaultCAServer.java
@@ -266,7 +266,8 @@ public void testBasicConstraintsAllowedForScmRoleOnly() throws Exception {
ExecutionException execution = assertThrows(ExecutionException.class,
() -> testCA.requestCertificate(caCsr, CertificateApprover.ApprovalType.TESTING_AUTOMATIC, OM,
String.valueOf(System.nanoTime())).get());
- assertThat(execution.getCause().getMessage()).contains("allowed for the SCM role only, not for OM");
+ // The leaf profile used for other roles rejects the CA constraint before the role check.
+ assertThat(execution.getCause().getMessage()).contains("Failed to verify the CSR");
assertThat(caStore.getStoredCertificates()).isEmpty();
X509Certificate scmCert = CertificateCodec.firstCertificateFrom(testCA.requestCertificate(caCsr,
diff --git a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/authority/TestDefaultProfile.java b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/authority/TestDefaultProfile.java
index 06ca02857e9..c0ec1d8a88c 100644
--- a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/authority/TestDefaultProfile.java
+++ b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/authority/TestDefaultProfile.java
@@ -34,13 +34,16 @@
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.security.SecurityConfig;
import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
+import org.apache.hadoop.hdds.security.x509.certificate.authority.profile.DefaultCAProfile;
import org.apache.hadoop.hdds.security.x509.certificate.authority.profile.DefaultProfile;
import org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateSignRequest;
import org.apache.hadoop.hdds.security.x509.keys.HDDSKeyGenerator;
+import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
@@ -352,6 +355,18 @@ private PKCS10CertificationRequest getInvalidCSR(KeyPair kPair,
return p10Builder.build(signer);
}
+ /**
+ * Basic constraints, which make a certificate a CA, are accepted by the CA profile only.
+ */
+ @Test
+ void testBasicConstraintsOnlyInCAProfile() throws IOException {
+ Extension ca = new Extension(Extension.basicConstraints, true, new DEROctetString(new BasicConstraints(true)));
+
+ assertThat(new DefaultCAProfile().validateExtension(ca)).isTrue();
+ assertThat(defaultProfile.getSupportedExtensions()).doesNotContain(Extension.basicConstraints);
+ assertThat(defaultProfile.validateExtension(ca)).isFalse();
+ }
+
/**
* Generate an Extension with rfc822Name.
* @param extensionCode - Extension Code.
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java
index f1cc6fb9c27..bed276bc912 100644
--- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java
@@ -17,12 +17,16 @@
package org.apache.hadoop.hdds.scm.server;
+import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_DATANODE_KERBEROS_PRINCIPAL_KEY;
+import static org.apache.hadoop.hdds.recon.ReconConfig.ConfigStrings.OZONE_RECON_KERBEROS_PRINCIPAL_KEY;
+import static org.apache.hadoop.hdds.scm.ScmConfig.ConfigStrings.HDDS_SCM_KERBEROS_PRINCIPAL_KEY;
import static org.apache.hadoop.hdds.scm.ScmUtils.checkIfCertSignRequestAllowed;
import static org.apache.hadoop.hdds.security.exception.SCMSecretKeyException.ErrorCode.SECRET_KEY_NOT_ENABLED;
import static org.apache.hadoop.hdds.security.exception.SCMSecretKeyException.ErrorCode.SECRET_KEY_NOT_INITIALIZED;
import static org.apache.hadoop.hdds.security.exception.SCMSecurityException.ErrorCode.CERTIFICATE_NOT_FOUND;
import static org.apache.hadoop.hdds.security.exception.SCMSecurityException.ErrorCode.GET_CA_CERT_FAILED;
import static org.apache.hadoop.hdds.security.exception.SCMSecurityException.ErrorCode.GET_CERTIFICATE_FAILED;
+import static org.apache.hadoop.hdds.security.exception.SCMSecurityException.ErrorCode.UNABLE_TO_ISSUE_CERTIFICATE;
import static org.apache.hadoop.hdds.security.x509.certificate.authority.CertificateApprover.ApprovalType.KERBEROS_TRUSTED;
import static org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateCodec.getPEMEncodedString;
import static org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateSignRequest.getCertificationRequest;
@@ -342,6 +346,7 @@ private synchronized String getEncodedCertToString(String certSignReq,
// SCM role certificates are CA certificates and are issued to SCM admins only, the check addSCM applies.
storageContainerManager.checkAdminAccess(getRpcRemoteUser(), false);
}
+ checkRequesterIsService(nodeType);
Future<CertPath> future;
PKCS10CertificationRequest csr = getCertificationRequest(certSignReq);
if (nodeType == NodeType.SCM && rootCertificateServer != null) {
@@ -369,6 +374,35 @@ private synchronized String getEncodedCertToString(String certSignReq,
}
}
+ /**
+ * A certificate for a role is only issued to the service user of that role.
+ */
+ private void checkRequesterIsService(NodeType role) throws IOException {
+ final String principalKey = getKerberosPrincipalKey(role);
+ final UserGroupInformation user = HddsServerUtil.getRemoteUser();
+ if (principalKey == null || !HddsServerUtil.isServiceUser(user, config.get(principalKey, ""))) {
+ throw new SCMSecurityException("User " + user.getUserName() + " is not allowed to request a " + role
+ + " certificate, it does not match the principal configured as " + principalKey + " on SCM.",
+ UNABLE_TO_ISSUE_CERTIFICATE);
+ }
+ }
+
+ private static String getKerberosPrincipalKey(NodeType role) {
+ switch (role) {
+ case DATANODE:
+ return HDDS_DATANODE_KERBEROS_PRINCIPAL_KEY;
+ case OM:
+ // TODO: move OMConfigKeys.OZONE_OM_KERBEROS_PRINCIPAL_KEY to hdds-common.
+ return "ozone.om.kerberos.principal";
+ case SCM:
+ return HDDS_SCM_KERBEROS_PRINCIPAL_KEY;
+ case RECON:
+ return OZONE_RECON_KERBEROS_PRINCIPAL_KEY;
+ default:
+ return null;
+ }
+ }
+
private SCMSecurityException generateException(Exception ex, NodeType role) {
SCMSecurityException.ErrorCode errorCode;
if (role == NodeType.SCM) {
diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/TestHddsServerUtil.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/TestHddsServerUtil.java
index 2878304f386..0837274ea6e 100644
--- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/TestHddsServerUtil.java
+++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/TestHddsServerUtil.java
@@ -21,6 +21,9 @@
import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_DATANODE_PORT_DEFAULT;
import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_DATANODE_PORT_KEY;
import static org.apache.hadoop.hdds.utils.HddsServerUtil.getSCMAddressForDatanodes;
+import static org.apache.hadoop.hdds.utils.HddsServerUtil.isServiceUser;
+import static org.apache.hadoop.security.UserGroupInformation.createProxyUser;
+import static org.apache.hadoop.security.UserGroupInformation.createRemoteUser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -39,6 +42,7 @@
import org.apache.hadoop.hdds.utils.HddsServerUtil;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.ozone.ha.ConfUtils;
+import org.apache.hadoop.security.UserGroupInformation;
import org.junit.jupiter.api.Test;
/**
@@ -306,4 +310,16 @@ void testGetSCMAddressesWithHAConfig() {
}
+ @Test
+ void testIsServiceUser() {
+ UserGroupInformation.setConfiguration(new OzoneConfiguration());
+ String principal = "om/_HOST@EXAMPLE.COM";
+ assertThat(isServiceUser(createRemoteUser("om/host1@EXAMPLE.COM"), principal)).isTrue();
+ for (String user : new String[] {"om@EXAMPLE.COM", "om/host1@OTHER.COM", "om/host1", "alice/om@EXAMPLE.COM"}) {
+ assertThat(isServiceUser(createRemoteUser(user), principal)).as(user).isFalse();
+ }
+ assertThat(isServiceUser(createProxyUser("om/host1@EXAMPLE.COM", createRemoteUser("alice")), principal)).isFalse();
+ // No user matches a principal that is not configured.
+ assertThat(isServiceUser(createRemoteUser("om/host1@EXAMPLE.COM"), "")).isFalse();
+ }
}
diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
index f02fe8e6469..a707c4ef143 100644
--- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
+++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
@@ -57,6 +57,7 @@
import static org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.KERBEROS;
import static org.apache.ozone.test.GenericTestUtils.PortAllocator.getFreePort;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -98,6 +99,7 @@
import org.apache.commons.validator.routines.DomainValidator;
import org.apache.hadoop.hdds.HddsConfigKeys;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.protocol.MockDatanodeDetails;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertResponseProto;
@@ -395,6 +397,7 @@ void testSCMSecurityProtocol() throws Exception {
() -> securityClient.getCertificate("1"));
assertThat(ioException).hasMessageContaining(cannotAuthMessage);
}
+ assertScmIssuesCertificateOnlyToServiceUserOfRole();
} finally {
if (scm != null) {
scm.stop();
@@ -402,6 +405,57 @@ void testSCMSecurityProtocol() throws Exception {
}
}
+ /** SCM security protocol - a certificate is only issued to the service user of the requested role. */
+ private void assertScmIssuesCertificateOnlyToServiceUserOfRole() throws Exception {
+ String csr = newCsr(false);
+ HddsProtos.ScmNodeDetailsProto scmInfo = HddsProtos.ScmNodeDetailsProto.newBuilder()
+ .setClusterId(clusterId).setHostName("localhost").setScmNodeId(scmId).build();
+
+ UserGroupInformation testUser = UserGroupInformation.loginUserFromKeytabAndReturnUGI(
+ testUserPrincipal, testUserKeytab.getCanonicalPath());
+ try (SCMSecurityProtocolClientSideTranslatorPB securityClient = getScmSecurityClient(conf, testUser)) {
+ assertThatThrownBy(() -> securityClient.getOMCertificate(omInfo, csr))
+ .hasMessageContaining("is not allowed to request a OM certificate");
+ assertThatThrownBy(() -> securityClient.getDataNodeCertificate(
+ MockDatanodeDetails.randomDatanodeDetails().getProtoBufMessage(), csr))
+ .hasMessageContaining("is not allowed to request a DATANODE certificate");
+ // SCM role certificates are refused to non admins before the service user check.
+ assertThatThrownBy(() -> securityClient.getSCMCertificate(scmInfo, csr)).hasMessageContaining("Access denied");
+ assertThatThrownBy(() -> securityClient.getCertificate(HddsProtos.NodeDetailsProto.newBuilder()
+ .setUuid(omId).setClusterId(clusterId).setHostName("localhost").setNodeType(HddsProtos.NodeType.RECON)
+ .build(), csr))
+ .hasMessageContaining("is not allowed to request a RECON certificate");
+ }
+
+ UserGroupInformation omUser = UserGroupInformation.loginUserFromKeytabAndReturnUGI(
+ conf.get(OZONE_OM_KERBEROS_PRINCIPAL_KEY), conf.get(OZONE_OM_KERBEROS_KEYTAB_FILE_KEY));
+ try (SCMSecurityProtocolClientSideTranslatorPB securityClient = getScmSecurityClient(conf, omUser)) {
+ // OM gets a certificate for itself, but not one that can sign other certificates.
+ String caCsr = newCsr(true);
+ assertThatThrownBy(() -> securityClient.getOMCertificate(omInfo, caCsr))
+ .hasMessageContaining("Failed to verify the CSR");
+ assertThat(securityClient.getOMCertificate(omInfo, csr)).isNotEmpty();
+ // The service user of one role does not get a certificate of another role.
+ assertThatThrownBy(() -> securityClient.getDataNodeCertificate(
+ MockDatanodeDetails.randomDatanodeDetails().getProtoBufMessage(), csr))
+ .hasMessageContaining("is not allowed to request a DATANODE certificate");
+ }
+ }
+
+ private String newCsr(boolean ca) throws Exception {
+ CertificateSignRequest.Builder csrBuilder = new CertificateSignRequest.Builder()
+ .setKey(keyPair)
+ .setConfiguration(new SecurityConfig(conf))
+ .setScmID(scmId)
+ .setClusterID(clusterId)
+ .setSubject("test")
+ .setCA(ca)
+ .setDigitalSignature(true)
+ .setDigitalEncryption(true);
+ addIpAndDnsDataToBuilder(csrBuilder);
+ return csrBuilder.build().toEncodedFormat();
+ }
+
@Test
void testAdminAccessControlException() throws Exception {
initSCM();
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ozone.apache.org
For additional commands, e-mail: commits-help@ozone.apache.org