diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/SecurityConfig.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/SecurityConfig.java index 645b3e0b663a..2b0efc4d6ab9 100644 --- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/SecurityConfig.java +++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/SecurityConfig.java @@ -71,6 +71,8 @@ import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_SIGNATURE_ALGO; import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_SIGNATURE_ALGO_DEFAULT; import static org.apache.hadoop.hdds.HddsConfigKeys.OZONE_METADATA_DIRS; +import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_AUTHORIZATION_ENABLED; +import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_AUTHORIZATION_ENABLED_DEFAULT; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SECURITY_ENABLED_DEFAULT; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SECURITY_ENABLED_KEY; @@ -104,6 +106,15 @@ public class SecurityConfig { private static final Logger LOG = LoggerFactory.getLogger(SecurityConfig.class); private static volatile Provider provider; + + /** + * Test-only configuration property to enable authorization checks without + * requiring full security (Kerberos) setup. This is for testing purposes + * only. + */ + public static final String OZONE_TEST_AUTHORIZATION_ENABLED = "ozone.test.authorization.enabled"; + public static final boolean OZONE_TEST_AUTHORIZATION_ENABLED_DEFAULT = false; + private final int size; private final String keyAlgo; private final String providerString; @@ -136,6 +147,7 @@ public class SecurityConfig { private final Duration rootCaCertificatePollingInterval; private final boolean autoCARotationEnabled; private final Duration expiredCertificateCheckInterval; + private final boolean authorizationEnabled; /** * Constructs a SecurityConfig. @@ -200,6 +212,14 @@ public SecurityConfig(ConfigurationSource configuration) { OZONE_SECURITY_ENABLED_KEY, OZONE_SECURITY_ENABLED_DEFAULT); + // Authorization is only effective when security is enabled, unless test mode is enabled + boolean testAuthorizationEnabled = configuration.getBoolean( + OZONE_TEST_AUTHORIZATION_ENABLED, + OZONE_TEST_AUTHORIZATION_ENABLED_DEFAULT); + this.authorizationEnabled = (isSecurityEnabled || testAuthorizationEnabled) && + configuration.getBoolean(OZONE_AUTHORIZATION_ENABLED, + OZONE_AUTHORIZATION_ENABLED_DEFAULT); + String certDurationString = configuration.get(HDDS_X509_DEFAULT_DURATION, HDDS_X509_DEFAULT_DURATION_DEFAULT); @@ -608,4 +628,15 @@ public boolean useTestCert() { public boolean isTokenEnabled() { return blockTokenEnabled || containerTokenEnabled; } + + /** + * Check if authorization checks should be performed in Ozone. + * Authorization is only effective when security is enabled, unless test mode is enabled. + * This controls both admin privilege checks and ACL checks. + * + * @return true if authorization checks should be performed + */ + public boolean isAuthorizationEnabled() { + return authorizationEnabled; + } } diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConfigKeys.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConfigKeys.java index 853a98314aa7..456f44bac113 100644 --- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConfigKeys.java +++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConfigKeys.java @@ -469,6 +469,8 @@ public final class OzoneConfigKeys { "ozone.acl.enabled"; public static final boolean OZONE_ACL_ENABLED_DEFAULT = false; + public static final String OZONE_AUTHORIZATION_ENABLED = "ozone.authorization.enabled"; + public static final boolean OZONE_AUTHORIZATION_ENABLED_DEFAULT = true; public static final String OZONE_S3_VOLUME_NAME = "ozone.s3g.volume.name"; public static final String OZONE_S3_VOLUME_NAME_DEFAULT = diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneSecurityUtil.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneSecurityUtil.java index 71e09bd4ca5b..6e9da83b2bb3 100644 --- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneSecurityUtil.java +++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneSecurityUtil.java @@ -17,6 +17,10 @@ package org.apache.hadoop.ozone; +import static org.apache.hadoop.hdds.security.SecurityConfig.OZONE_TEST_AUTHORIZATION_ENABLED; +import static org.apache.hadoop.hdds.security.SecurityConfig.OZONE_TEST_AUTHORIZATION_ENABLED_DEFAULT; +import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_AUTHORIZATION_ENABLED; +import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_AUTHORIZATION_ENABLED_DEFAULT; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_HTTP_SECURITY_ENABLED_DEFAULT; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_HTTP_SECURITY_ENABLED_KEY; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SECURITY_ENABLED_DEFAULT; @@ -60,6 +64,23 @@ public static boolean isHttpSecurityEnabled(ConfigurationSource conf) { OZONE_HTTP_SECURITY_ENABLED_DEFAULT); } + /** + * Check if authorization checks should be performed in Ozone. + * Authorization is only effective when security is enabled, unless test mode is enabled. + * This controls both admin privilege checks and ACL checks. + * + * @param conf Configuration source + * @return true if authorization checks should be performed + */ + public static boolean isAuthorizationEnabled(ConfigurationSource conf) { + // Check if test mode is enabled (allows authorization without full security) + boolean testAuthorizationEnabled = conf.getBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, + OZONE_TEST_AUTHORIZATION_ENABLED_DEFAULT); + return (isSecurityEnabled(conf) || testAuthorizationEnabled) && + conf.getBoolean(OZONE_AUTHORIZATION_ENABLED, + OZONE_AUTHORIZATION_ENABLED_DEFAULT); + } + /** * Returns Keys status. * diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml b/hadoop-hdds/common/src/main/resources/ozone-default.xml index dc52800642ae..afbb718ecdf3 100644 --- a/hadoop-hdds/common/src/main/resources/ozone-default.xml +++ b/hadoop-hdds/common/src/main/resources/ozone-default.xml @@ -2376,6 +2376,20 @@ OZONE, SECURITY, ACL Key to enable/disable ozone acls. + + ozone.authorization.enabled + true + OZONE, SECURITY, AUTHORIZATION + + Master switch to enable/disable authorization checks in Ozone + (admin privilege checks and ACL checks). + This property only takes effect when ozone.security.enabled is true. + When true: admin privilege checks are always performed, and object + ACL checks are controlled by ozone.acl.enabled. + When false: no authorization checks are performed. + Default is true. + + ozone.om.kerberos.keytab.file /etc/security/keytabs/OM.keytab diff --git a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/HddsDatanodeService.java b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/HddsDatanodeService.java index b0fbfe30b93f..f899b8633577 100644 --- a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/HddsDatanodeService.java +++ b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/HddsDatanodeService.java @@ -669,6 +669,11 @@ public boolean isStopped() { */ private void checkAdminPrivilege(String operation) throws IOException { + // Skip check if authorization is disabled + if (secConf == null || !secConf.isAuthorizationEnabled()) { + return; + } + final UserGroupInformation ugi = getRemoteUser(); admins.checkAdminUserPrivilege(ugi); } diff --git a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/SCMTestUtils.java b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/SCMTestUtils.java index 11fa95734fd2..e29f46cd74e0 100644 --- a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/SCMTestUtils.java +++ b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/SCMTestUtils.java @@ -17,6 +17,7 @@ package org.apache.hadoop.ozone.container.common; +import static org.apache.hadoop.hdds.security.SecurityConfig.OZONE_TEST_AUTHORIZATION_ENABLED; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -137,6 +138,7 @@ public static OzoneConfiguration getConf(File testDir) { conf.setClass(SpaceUsageCheckFactory.Conf.configKeyForClassName(), MockSpaceUsageCheckFactory.None.class, SpaceUsageCheckFactory.class); + conf.setBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, true); return conf; } diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/OzoneAdmins.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/OzoneAdmins.java index cf4cf2af550f..cfbb189212fe 100644 --- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/OzoneAdmins.java +++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/OzoneAdmins.java @@ -103,6 +103,8 @@ public static OzoneAdmins getReadonlyAdmins( /** * Check ozone admin privilege, throws exception if not admin. + * Note: This method does NOT check if authorization is enabled. + * Callers should check authorization before calling this method. */ public void checkAdminUserPrivilege(UserGroupInformation ugi) throws AccessControlException { diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/DBCheckpointServlet.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/DBCheckpointServlet.java index 38e986583800..a133e5188a2d 100644 --- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/DBCheckpointServlet.java +++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/DBCheckpointServlet.java @@ -73,14 +73,14 @@ public class DBCheckpointServlet extends HttpServlet private transient DBStore dbStore; private transient DBCheckpointMetrics dbMetrics; - private boolean aclEnabled; + private boolean authorizationEnabled; private boolean isSpnegoEnabled; private transient OzoneAdmins admins; private transient BootstrapStateHandler.Lock lock; private transient File bootstrapTempData; public void initialize(DBStore store, DBCheckpointMetrics metrics, - boolean omAclEnabled, + boolean isAuthorizationEnabled, Collection allowedAdminUsers, Collection allowedAdminGroups, boolean isSpnegoAuthEnabled) @@ -94,7 +94,7 @@ public void initialize(DBStore store, DBCheckpointMetrics metrics, throw new ServletException("DB Store is null"); } - this.aclEnabled = omAclEnabled; + this.authorizationEnabled = isAuthorizationEnabled; this.admins = new OzoneAdmins(allowedAdminUsers, allowedAdminGroups); this.isSpnegoEnabled = isSpnegoAuthEnabled; lock = new NoOpLock(); @@ -129,9 +129,9 @@ public File getBootstrapTempData() { } private boolean hasPermission(UserGroupInformation user) { - // Check ACL for dbCheckpoint only when global Ozone ACL and SPNEGO is + // Check admin access for dbCheckpoint only when authorization and SPNEGO is // enabled - if (aclEnabled && isSpnegoEnabled) { + if (authorizationEnabled && isSpnegoEnabled) { return admins.isAdmin(user); } else { return true; @@ -165,8 +165,8 @@ private void generateSnapshotCheckpoint(HttpServletRequest request, return; } - // Check ACL for dbCheckpoint only when global Ozone ACL is enabled - if (aclEnabled) { + // Check authorization for dbCheckpoint only when authorization is enabled + if (authorizationEnabled) { final java.security.Principal userPrincipal = request.getUserPrincipal(); if (userPrincipal == null) { final String remoteUser = request.getRemoteUser(); diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMDBCheckpointServlet.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMDBCheckpointServlet.java index 5c90409efd8d..04cb600d8107 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMDBCheckpointServlet.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMDBCheckpointServlet.java @@ -27,10 +27,10 @@ /** * Provides the current checkpoint Snapshot of the SCM DB. (tar.gz) * - * When Ozone ACL is enabled (`ozone.acl.enabled`=`true`), only users/principals - * configured in `ozone.administrator` (along with the user that starts OM, - * which automatically becomes an Ozone administrator but not necessarily in - * the config) are allowed to access this endpoint. + * When Ozone authorization is enabled (`ozone.authorization.enabled`=`true`), + * only users/principals configured in `ozone.administrator` (along with the + * user that starts SCM, which automatically becomes an Ozone administrator + * but not necessarily in the config) are allowed to access this endpoint. * * If Kerberos is enabled, the principal should be appended to * `ozone.administrator`, e.g. `scm/scm@EXAMPLE.COM` @@ -56,7 +56,7 @@ public void init() throws ServletException { initialize(scm.getScmMetadataStore().getStore(), scm.getMetrics().getDBCheckpointMetrics(), - false, + scm.isAdminAuthorizationEnabled(), Collections.emptyList(), Collections.emptyList(), false); diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java index 32673e87279b..f521909faa74 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java @@ -1952,8 +1952,23 @@ private void checkAdminAccess(String op) throws IOException { checkAdminAccess(getRemoteUser(), false); } + /** + * Check if admin privilege authorization should be enforced. + * This controls system-level admin operations (upgrades, decommission, etc.) + * + * @return true if admin authorization checks should be performed + */ + public boolean isAdminAuthorizationEnabled() { + return securityConfig != null && securityConfig.isAuthorizationEnabled(); + } + public void checkAdminAccess(UserGroupInformation remoteUser, boolean isRead) throws IOException { + // Skip check if authorization is disabled + if (!isAdminAuthorizationEnabled()) { + return; + } + if (remoteUser != null && !scmAdmins.isAdmin(remoteUser)) { if (!isRead || !scmReadOnlyAdmins.isAdmin(remoteUser)) { throw new AccessControlException( diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/hdds/scm/TestStorageContainerManager.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/hdds/scm/TestStorageContainerManager.java index 7afaa10c35b0..37c4cfd380d9 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/hdds/scm/TestStorageContainerManager.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/hdds/scm/TestStorageContainerManager.java @@ -24,6 +24,7 @@ import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_SCM_SAFEMODE_PIPELINE_CREATION; import static org.apache.hadoop.hdds.scm.HddsTestUtils.mockRemoteUser; import static org.apache.hadoop.hdds.scm.HddsWhiteboxTestUtils.setInternalState; +import static org.apache.hadoop.hdds.security.SecurityConfig.OZONE_TEST_AUTHORIZATION_ENABLED; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_BLOCK_DELETING_SERVICE_INTERVAL; import static org.apache.hadoop.ozone.common.BlockGroup.SIZE_NOT_AVAILABLE; import static org.assertj.core.api.Assertions.assertThat; @@ -167,6 +168,7 @@ public class TestStorageContainerManager { @Test void test(@TempDir Path tempDir) throws Exception { OzoneConfiguration conf = new OzoneConfiguration(); + conf.setBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, true); configureTopology(conf); configureBlockDeletion(conf); Path scmPath = tempDir.resolve("scm-meta"); diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/TestOzoneRpcClient.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/TestOzoneRpcClient.java index 5559ed2b2289..7e6a64ea098f 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/TestOzoneRpcClient.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/TestOzoneRpcClient.java @@ -17,6 +17,8 @@ package org.apache.hadoop.ozone.client.rpc; +import static org.apache.hadoop.hdds.security.SecurityConfig.OZONE_TEST_AUTHORIZATION_ENABLED; + import java.io.IOException; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.scm.ScmConfigKeys; @@ -32,6 +34,7 @@ class TestOzoneRpcClient extends OzoneRpcClientTests { @BeforeAll public static void init() throws Exception { OzoneConfiguration conf = new OzoneConfiguration(); + conf.setBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, true); conf.setInt(ScmConfigKeys.OZONE_SCM_PIPELINE_OWNER_CONTAINER_COUNT, 1); conf.setBoolean(OzoneConfigKeys.OZONE_ACL_ENABLED, true); conf.set(OzoneConfigKeys.OZONE_ACL_AUTHORIZER_CLASS, diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/TestSecureOzoneRpcClient.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/TestSecureOzoneRpcClient.java index 773ea96c3c7b..772ec0383fbe 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/TestSecureOzoneRpcClient.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/TestSecureOzoneRpcClient.java @@ -19,6 +19,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.apache.hadoop.hdds.HddsConfigKeys.OZONE_METADATA_DIRS; +import static org.apache.hadoop.hdds.security.SecurityConfig.OZONE_TEST_AUTHORIZATION_ENABLED; import static org.apache.hadoop.ozone.OzoneConsts.FORCE_LEASE_RECOVERY_ENV; import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OFS_URI_SCHEME; import static org.apache.hadoop.ozone.OzoneConsts.OZONE_ROOT; @@ -116,6 +117,7 @@ public static void init() throws Exception { conf.setBoolean(HddsConfigKeys.HDDS_BLOCK_TOKEN_ENABLED, true); conf.set(OZONE_METADATA_DIRS, testDir.getAbsolutePath()); conf.setBoolean(OzoneConfigKeys.OZONE_ACL_ENABLED, true); + conf.setBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, true); conf.set(OzoneConfigKeys.OZONE_ACL_AUTHORIZER_CLASS, OzoneConfigKeys.OZONE_ACL_AUTHORIZER_CLASS_NATIVE); CertificateClientTestImpl certificateClientTest = diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestAddRemoveOzoneManager.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestAddRemoveOzoneManager.java index 2376a0e93ff0..c891ca99ff4d 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestAddRemoveOzoneManager.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestAddRemoveOzoneManager.java @@ -17,6 +17,7 @@ package org.apache.hadoop.ozone.om; +import static org.apache.hadoop.hdds.security.SecurityConfig.OZONE_TEST_AUTHORIZATION_ENABLED; import static org.apache.hadoop.ozone.OzoneConsts.SCM_DUMMY_SERVICE_ID; import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_DECOMMISSIONED_NODES_KEY; import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RATIS_SERVER_REQUEST_TIMEOUT_DEFAULT; @@ -89,7 +90,14 @@ public class TestAddRemoveOzoneManager { private OzoneClient client; private void setupCluster(int numInitialOMs) throws Exception { + setupCluster(numInitialOMs, false); + } + + private void setupCluster(int numInitialOMs, boolean enableTestAuthorization) throws Exception { conf = new OzoneConfiguration(); + if (enableTestAuthorization) { + conf.setBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, true); + } conf.setInt(OzoneConfigKeys.OZONE_CLIENT_FAILOVER_MAX_ATTEMPTS_KEY, 5); cluster = MiniOzoneCluster.newHABuilder(conf) .setSCMServiceId(SCM_DUMMY_SERVICE_ID) @@ -408,41 +416,45 @@ public void testBootstrapListenerOM() throws Exception { */ @Test public void testDecommission() throws Exception { - setupCluster(3); - - user = UserGroupInformation.createUserForTesting("user", new String[]{}); - // Stop the 3rd OM and decommission it using non-privileged user - String omNodeId3 = cluster.getOzoneManager(2).getOMNodeId(); - cluster.stopOzoneManager(omNodeId3); - // decommission should fail - assertThrows(IOException.class, () -> decommissionOM(omNodeId3)); - - // Switch to admin user - user = UserGroupInformation.getCurrentUser(); - // Stop the 3rd OM and decommission it - cluster.stopOzoneManager(omNodeId3); - decommissionOM(omNodeId3); - - // Decommission the non leader OM and then stop it. Stopping OM before will - // lead to no quorum and there will not be a elected leader OM to process - // the decommission request. - String omNodeId2; - if (cluster.getOMLeader().getOMNodeId().equals( - cluster.getOzoneManager(1).getOMNodeId())) { - omNodeId2 = cluster.getOzoneManager(0).getOMNodeId(); - } else { - omNodeId2 = cluster.getOzoneManager(1).getOMNodeId(); - } - decommissionOM(omNodeId2); - cluster.stopOzoneManager(omNodeId2); + try { + setupCluster(3, true); + + user = UserGroupInformation.createUserForTesting("user", new String[]{}); + // Stop the 3rd OM and decommission it using non-privileged user + String omNodeId3 = cluster.getOzoneManager(2).getOMNodeId(); + cluster.stopOzoneManager(omNodeId3); + // decommission should fail + assertThrows(IOException.class, () -> decommissionOM(omNodeId3)); + + // Switch to admin user + user = UserGroupInformation.getCurrentUser(); + // Stop the 3rd OM and decommission it + cluster.stopOzoneManager(omNodeId3); + decommissionOM(omNodeId3); + + // Decommission the non leader OM and then stop it. Stopping OM before will + // lead to no quorum and there will not be a elected leader OM to process + // the decommission request. + String omNodeId2; + if (cluster.getOMLeader().getOMNodeId().equals( + cluster.getOzoneManager(1).getOMNodeId())) { + omNodeId2 = cluster.getOzoneManager(0).getOMNodeId(); + } else { + omNodeId2 = cluster.getOzoneManager(1).getOMNodeId(); + } + decommissionOM(omNodeId2); + cluster.stopOzoneManager(omNodeId2); - // Verify that we can read/ write to the cluster with only 1 OM. - OzoneVolume volume = objectStore.getVolume(VOLUME_NAME); - OzoneBucket bucket = volume.getBucket(BUCKET_NAME); - String key = createKey(bucket); + // Verify that we can read/ write to the cluster with only 1 OM. + OzoneVolume volume = objectStore.getVolume(VOLUME_NAME); + OzoneBucket bucket = volume.getBucket(BUCKET_NAME); + String key = createKey(bucket); - assertNotNull(bucket.getKey(key)); + assertNotNull(bucket.getKey(key)); + } finally { + conf.setBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, false); + } } /** diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServlet.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServlet.java index df15d50e1506..ac12189b1f62 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServlet.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServlet.java @@ -18,6 +18,7 @@ package org.apache.hadoop.ozone.om; import static org.apache.hadoop.hdds.recon.ReconConfig.ConfigStrings.OZONE_RECON_KERBEROS_PRINCIPAL_KEY; +import static org.apache.hadoop.hdds.security.SecurityConfig.OZONE_TEST_AUTHORIZATION_ENABLED; import static org.apache.hadoop.hdds.utils.HddsServerUtil.OZONE_RATIS_SNAPSHOT_COMPLETE_FLAG_NAME; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS; @@ -260,7 +261,7 @@ private void testEndpoint(String method) throws Exception { doCallRealMethod().when(omDbCheckpointServletMock).initialize( om.getMetadataManager().getStore(), om.getMetrics().getDBCheckpointMetrics(), - om.getAclsEnabled(), + om.isAdminAuthorizationEnabled(), om.getOmAdminUsernames(), om.getOmAdminGroups(), om.isSpnegoEnabled()); @@ -300,7 +301,7 @@ private void testDoPostWithInvalidContentType() throws Exception { doCallRealMethod().when(omDbCheckpointServletMock).initialize( om.getMetadataManager().getStore(), om.getMetrics().getDBCheckpointMetrics(), - om.getAclsEnabled(), + om.isAdminAuthorizationEnabled(), om.getOmAdminUsernames(), om.getOmAdminGroups(), om.isSpnegoEnabled()); @@ -320,6 +321,7 @@ private void testDoPostWithInvalidContentType() throws Exception { @Test void testSpnegoEnabled() throws Exception { + conf.setBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, true); conf.setBoolean(OZONE_ACL_ENABLED, true); conf.set(OZONE_ADMINISTRATORS, ""); conf.set(OZONE_OM_HTTP_AUTH_TYPE, "kerberos"); diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMHALeaderSpecificACLEnforcement.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMHALeaderSpecificACLEnforcement.java index 57a7a6f4f22a..43acb0f823d9 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMHALeaderSpecificACLEnforcement.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMHALeaderSpecificACLEnforcement.java @@ -18,6 +18,7 @@ package org.apache.hadoop.ozone.om; import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.hadoop.hdds.security.SecurityConfig.OZONE_TEST_AUTHORIZATION_ENABLED; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_AUTHORIZER_CLASS; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS; @@ -185,6 +186,8 @@ private void setupCluster() throws Exception { private OzoneConfiguration createBaseConfiguration() throws IOException { OzoneConfiguration conf = new OzoneConfiguration(); + conf.setBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, true); + // Enable ACL for proper permission testing conf.setBoolean(OZONE_ACL_ENABLED, true); diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmAcls.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmAcls.java index 350cc09c7bab..8faf7d973cff 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmAcls.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmAcls.java @@ -17,6 +17,7 @@ package org.apache.hadoop.ozone.om; +import static org.apache.hadoop.hdds.security.SecurityConfig.OZONE_TEST_AUTHORIZATION_ENABLED; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_AUTHORIZER_CLASS; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS; @@ -73,6 +74,7 @@ public class TestOmAcls { @BeforeAll public static void init() throws Exception { OzoneConfiguration conf = new OzoneConfiguration(); + conf.setBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, true); conf.setBoolean(OZONE_ACL_ENABLED, true); conf.setClass(OZONE_ACL_AUTHORIZER_CLASS, OzoneAccessAuthorizerTest.class, IAccessAuthorizer.class); diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestOzoneManagerSnapshotAcl.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestOzoneManagerSnapshotAcl.java index 455f1430d997..ae6cddba7cf7 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestOzoneManagerSnapshotAcl.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestOzoneManagerSnapshotAcl.java @@ -19,6 +19,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.apache.hadoop.fs.FileSystem.FS_DEFAULT_NAME_KEY; +import static org.apache.hadoop.hdds.security.SecurityConfig.OZONE_TEST_AUTHORIZATION_ENABLED; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_AUTHORIZER_CLASS; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_AUTHORIZER_CLASS_NATIVE; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED; @@ -107,6 +108,7 @@ public class TestOzoneManagerSnapshotAcl { public static void init() throws Exception { UserGroupInformation.setLoginUser(ADMIN_UGI); final OzoneConfiguration conf = new OzoneConfiguration(); + conf.setBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, true); conf.setBoolean(OZONE_ACL_ENABLED, true); conf.set(OZONE_ACL_AUTHORIZER_CLASS, OZONE_ACL_AUTHORIZER_CLASS_NATIVE); diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/ozone/test/AclTests.java b/hadoop-ozone/integration-test/src/test/java/org/apache/ozone/test/AclTests.java index 9136e159c6c6..fa51d5b9b4f2 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/ozone/test/AclTests.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/ozone/test/AclTests.java @@ -17,6 +17,7 @@ package org.apache.ozone.test; +import static org.apache.hadoop.hdds.security.SecurityConfig.OZONE_TEST_AUTHORIZATION_ENABLED; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_AUTHORIZER_CLASS; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_AUTHORIZER_CLASS_NATIVE; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED; @@ -49,7 +50,9 @@ protected MiniOzoneCluster.Builder newClusterBuilder() { @Override protected OzoneConfiguration createOzoneConfig() { loginAdmin(); + // Enable test security mode to allow ACL checks without Kerberos OzoneConfiguration conf = super.createOzoneConfig(); + conf.setBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, true); conf.setBoolean(OZONE_ACL_ENABLED, true); conf.set(OZONE_ACL_AUTHORIZER_CLASS, OZONE_ACL_AUTHORIZER_CLASS_NATIVE); conf.setBoolean(OMConfigKeys.OZONE_OM_ENABLE_FILESYSTEM_PATHS, true); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMDBCheckpointServlet.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMDBCheckpointServlet.java index a90ee336b001..db5de6b5b8ec 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMDBCheckpointServlet.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMDBCheckpointServlet.java @@ -81,10 +81,10 @@ /** * Provides the current checkpoint Snapshot of the OM DB. (tar.gz) * - * When Ozone ACL is enabled (`ozone.acl.enabled`=`true`), only users/principals - * configured in `ozone.administrator` (along with the user that starts OM, - * which automatically becomes an Ozone administrator but not necessarily in - * the config) are allowed to access this endpoint. + * When Ozone authorization is enabled (`ozone.authorization.enabled`=`true`), + * only users/principals configured in `ozone.administrator` (along with the user + * that starts OM, which automatically becomes an Ozone administrator but not + * necessarily in the config) are allowed to access this endpoint. * * If Kerberos is enabled, the principal should be appended to * `ozone.administrator`, e.g. `scm/scm@EXAMPLE.COM` @@ -125,7 +125,7 @@ public void init() throws ServletException { initialize(om.getMetadataManager().getStore(), om.getMetrics().getDBCheckpointMetrics(), - om.getAclsEnabled(), + om.isAdminAuthorizationEnabled(), allowedUsers, allowedGroups, om.isSpnegoEnabled()); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMDBCheckpointServletInodeBasedXfer.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMDBCheckpointServletInodeBasedXfer.java index 5e3052043280..a4a63dde7adc 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMDBCheckpointServletInodeBasedXfer.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMDBCheckpointServletInodeBasedXfer.java @@ -128,7 +128,7 @@ public void init() throws ServletException { initialize(om.getMetadataManager().getStore(), om.getMetrics().getDBCheckpointMetrics(), - om.getAclsEnabled(), + om.isAdminAuthorizationEnabled(), allowedUsers, allowedGroups, om.isSpnegoEnabled()); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java index ff0066f712ff..f2449a3c68e4 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java @@ -897,6 +897,10 @@ private void loadTenantCacheFromDB() { @Override public void checkAdmin() throws OMException { + // Skip check if authorization is disabled + if (!ozoneManager.isAdminAuthorizationEnabled()) { + return; + } final UserGroupInformation ugi = ProtobufRpcEngine.Server.getRemoteUser(); if (!ozoneManager.isAdmin(ugi)) { diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java index 7a9d66f86dff..f5087d873845 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java @@ -866,6 +866,9 @@ public String getThreadNamePrefix() { private void setInstanceVariablesFromConf() { this.isAclEnabled = configuration.getBoolean(OZONE_ACL_ENABLED, OZONE_ACL_ENABLED_DEFAULT); + LOG.info("Authorization enabled: {}, ACL enabled: {}", + secConfig != null ? secConfig.isAuthorizationEnabled() : false, + isAclEnabled); } /** @@ -2748,13 +2751,25 @@ public boolean checkAcls(ResourceType resType, StoreType storeType, return omMetadataReader.checkAcls(obj, context, throwIfPermissionDenied); } + /** + * Check if admin privilege authorization should be enforced. + * This controls system-level admin operations (upgrades, decommission, etc.) + * + * @return true if admin authorization checks should be performed + */ + public boolean isAdminAuthorizationEnabled() { + return secConfig != null && secConfig.isAuthorizationEnabled(); + } + /** * Return true if Ozone acl's are enabled, else false. + * ACLs are only effective when authorization is enabled. + * This controls volume/bucket/key level permissions. * * @return boolean */ public boolean getAclsEnabled() { - return isAclEnabled; + return isAdminAuthorizationEnabled() && isAclEnabled; } public UncheckedAutoCloseableSupplier getOmMetadataReader() { @@ -2781,7 +2796,7 @@ public OmVolumeArgs getVolumeInfo(String volume) throws IOException { boolean auditSuccess = true; Map auditMap = buildAuditMap(volume); try { - if (isAclEnabled) { + if (getAclsEnabled()) { omMetadataReader.checkAcls(ResourceType.VOLUME, StoreType.OZONE, ACLType.READ, volume, null, null); @@ -2817,7 +2832,7 @@ public List listVolumeByUser(String userName, String prefix, String prevKey, int maxKeys) throws IOException { UserGroupInformation remoteUserUgi = ProtobufRpcEngine.Server.getRemoteUser(); - if (isAclEnabled) { + if (getAclsEnabled()) { if (remoteUserUgi == null) { LOG.error("Rpc user UGI is null. Authorization failed."); throw new OMException("Rpc user UGI is null. Authorization failed.", @@ -2832,7 +2847,7 @@ public List listVolumeByUser(String userName, String prefix, auditMap.put(OzoneConsts.USERNAME, userName); try { metrics.incNumVolumeLists(); - if (isAclEnabled) { + if (getAclsEnabled()) { String remoteUserName = remoteUserUgi.getShortUserName(); // if not admin nor list my own volumes, check ACL. if (!remoteUserName.equals(userName) && !isAdmin(remoteUserUgi)) { @@ -2890,7 +2905,7 @@ public List listAllVolumes(String prefix, String prevKey, int auditMap.put(OzoneConsts.USERNAME, null); try { metrics.incNumVolumeLists(); - if (isAclEnabled) { + if (getAclsEnabled()) { omMetadataReader.checkAcls(ResourceType.VOLUME, StoreType.OZONE, ACLType.LIST, OzoneConsts.OZONE_ROOT, null, null); @@ -2927,7 +2942,7 @@ public List listBuckets(String volumeName, String startKey, auditMap.put(OzoneConsts.HAS_SNAPSHOT, String.valueOf(hasSnapshot)); try { - if (isAclEnabled) { + if (getAclsEnabled()) { omMetadataReader.checkAcls(ResourceType.VOLUME, StoreType.OZONE, ACLType.LIST, volumeName, null, null); @@ -2963,7 +2978,7 @@ public OmBucketInfo getBucketInfo(String volume, String bucket) Map auditMap = buildAuditMap(volume); auditMap.put(OzoneConsts.BUCKET, bucket); try { - if (isAclEnabled) { + if (getAclsEnabled()) { omMetadataReader.checkAcls(ResourceType.BUCKET, StoreType.OZONE, ACLType.READ, volume, bucket, null); @@ -3087,7 +3102,7 @@ public SnapshotInfo getSnapshotInfo(String volumeName, String bucketName, ResolvedBucket resolvedBucket = resolveBucketLink(Pair.of(volumeName, bucketName)); auditMap = buildAuditMap(resolvedBucket.realVolume()); auditMap.put(OzoneConsts.BUCKET, resolvedBucket.realBucket()); - if (isAclEnabled) { + if (getAclsEnabled()) { omMetadataReader.checkAcls(ResourceType.BUCKET, StoreType.OZONE, ACLType.READ, resolvedBucket.realVolume(), resolvedBucket.realBucket(), null); } @@ -3118,7 +3133,7 @@ public ListSnapshotResponse listSnapshot( ResolvedBucket resolvedBucket = resolveBucketLink(Pair.of(volumeName, bucketName)); auditMap = buildAuditMap(resolvedBucket.realVolume()); auditMap.put(OzoneConsts.BUCKET, resolvedBucket.realBucket()); - if (isAclEnabled) { + if (getAclsEnabled()) { omMetadataReader.checkAcls(ResourceType.BUCKET, StoreType.OZONE, ACLType.LIST, resolvedBucket.realVolume(), resolvedBucket.realBucket(), null); } @@ -3525,7 +3540,7 @@ public boolean triggerRangerBGSync(boolean noWait) throws IOException { final UserGroupInformation ugi = getRemoteUser(); // Check Ozone admin privilege - if (!isAdmin(ugi)) { + if (isAdminAuthorizationEnabled() && !isAdmin(ugi)) { throw new OMException("Only Ozone admins are allowed to trigger " + "Ranger background sync manually", PERMISSION_DENIED); } @@ -3565,7 +3580,7 @@ public boolean triggerSnapshotDefrag(boolean noWait) throws IOException { final UserGroupInformation ugi = getRemoteUser(); // Check Ozone admin privilege - if (!isAdmin(ugi)) { + if (isAdminAuthorizationEnabled() && !isAdmin(ugi)) { throw new OMException("Only Ozone admins are allowed to trigger " + "snapshot defragmentation manually", PERMISSION_DENIED); } @@ -3622,7 +3637,7 @@ public TenantStateList listTenant() throws IOException { metrics.incNumTenantLists(); final UserGroupInformation ugi = getRemoteUser(); - if (!isAdmin(ugi)) { + if (isAdminAuthorizationEnabled() && !isAdmin(ugi)) { final OMException omEx = new OMException( "Only Ozone admins are allowed to list tenants.", PERMISSION_DENIED); AUDIT.logReadFailure(buildAuditMessageForFailure( @@ -4590,8 +4605,14 @@ public boolean isAdmin(UserGroupInformation callerUgi) { /** * Check ozone admin privilege, throws exception if not admin. + * Only checks admin privilege if authorization is enabled. */ private void checkAdminUserPrivilege(String operation) throws IOException { + // Skip check if authorization is disabled + if (!isAdminAuthorizationEnabled()) { + return; + } + final UserGroupInformation ugi = getRemoteUser(); if (!isAdmin(ugi)) { throw new OMException("Only Ozone admins are allowed to " + operation, @@ -4629,7 +4650,7 @@ public ResolvedBucket resolveBucketLink(Pair requested, OMClientRequest omClientRequest) throws IOException { OmBucketInfo resolved; - if (isAclEnabled) { + if (getAclsEnabled()) { resolved = resolveBucketLink(requested, new HashSet<>(), omClientRequest.createUGIForApi(), omClientRequest.getRemoteAddress(), @@ -4645,7 +4666,7 @@ public ResolvedBucket resolveBucketLink(Pair requested, public ResolvedBucket resolveBucketLink(Pair requested, boolean allowDanglingBuckets) throws IOException { - return resolveBucketLink(requested, allowDanglingBuckets, isAclEnabled); + return resolveBucketLink(requested, allowDanglingBuckets, getAclsEnabled()); } public ResolvedBucket resolveBucketLink(Pair requested, @@ -4681,7 +4702,7 @@ private OmBucketInfo resolveBucketLink( String hostName, boolean allowDanglingBuckets) throws IOException { return resolveBucketLink(volumeAndBucket, visited, userGroupInformation, remoteAddress, hostName, - allowDanglingBuckets, isAclEnabled); + allowDanglingBuckets, getAclsEnabled()); } /** @@ -5191,7 +5212,7 @@ public SnapshotDiffResponse snapshotDiff(String volume, // Updating the volumeName & bucketName in case the bucket is a linked bucket. We need to do this before a // permission check, since linked bucket permissions and source bucket permissions could be different. ResolvedBucket resolvedBucket = resolveBucketLink(Pair.of(volume, bucket), false); - if (isAclEnabled) { + if (getAclsEnabled()) { omMetadataReader.checkAcls(ResourceType.BUCKET, StoreType.OZONE, ACLType.READ, resolvedBucket.realVolume(), resolvedBucket.realBucket(), null); } @@ -5228,7 +5249,7 @@ public CancelSnapshotDiffResponse cancelSnapshotDiff(String volume, try { ResolvedBucket resolvedBucket = this.resolveBucketLink(Pair.of(volume, bucket), false); - if (isAclEnabled) { + if (getAclsEnabled()) { omMetadataReader.checkAcls(ResourceType.BUCKET, StoreType.OZONE, ACLType.READ, resolvedBucket.realVolume(), resolvedBucket.realBucket(), null); } @@ -5265,7 +5286,7 @@ public ListSnapshotDiffJobResponse listSnapshotDiffJobs( try { ResolvedBucket resolvedBucket = this.resolveBucketLink(Pair.of(volume, bucket), false); - if (isAclEnabled) { + if (getAclsEnabled()) { omMetadataReader.checkAcls(ResourceType.BUCKET, StoreType.OZONE, ACLType.LIST, volume, bucket, null); } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java index ddad03652916..50c7ddf47a8b 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java @@ -368,7 +368,7 @@ public TransactionContext preAppendTransaction(TransactionContext trx) UserGroupInformation userGroupInformation = UserGroupInformation.createRemoteUser( request.getUserInfo().getUserName()); - if (ozoneManager.getAclsEnabled() + if (ozoneManager.isAdminAuthorizationEnabled() && !ozoneManager.isAdmin(userGroupInformation)) { String message = "Access denied for user " + userGroupInformation + ". Superuser privilege is required to prepare upgrade/downgrade."; diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/bucket/OMBucketDeleteRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/bucket/OMBucketDeleteRequest.java index 3c5f028bb5d9..deb2c8a05b32 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/bucket/OMBucketDeleteRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/bucket/OMBucketDeleteRequest.java @@ -126,7 +126,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, Execut if (omBucketInfo == null) { LOG.debug("bucket: {} not found ", bucketName); - throw new OMException("Bucket not exists", BUCKET_NOT_FOUND); + throw new OMException("Bucket not found", BUCKET_NOT_FOUND); } //Check if bucket is empty diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotCreateRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotCreateRequest.java index 07a8aeed3139..77858c0a50b5 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotCreateRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotCreateRequest.java @@ -113,11 +113,13 @@ public OMRequest preExecute(OzoneManager ozoneManager) throws IOException { UserGroupInformation ugi = createUGIForApi(); String bucketOwner = ozoneManager.getBucketOwner(volumeName, bucketName, IAccessAuthorizer.ACLType.READ, OzoneObj.ResourceType.BUCKET); - if (!ozoneManager.isAdmin(ugi) && - !ozoneManager.isOwner(ugi, bucketOwner)) { - throw new OMException( - "Only bucket owners and Ozone admins can create snapshots", - OMException.ResultCodes.PERMISSION_DENIED); + if (ozoneManager.isAdminAuthorizationEnabled()) { + if (!ozoneManager.isAdmin(ugi) && + !ozoneManager.isOwner(ugi, bucketOwner)) { + throw new OMException( + "Only bucket owners and Ozone admins can create snapshots", + OMException.ResultCodes.PERMISSION_DENIED); + } } // verify snapshot limit ozoneManager.getOmSnapshotManager().snapshotLimitCheck(); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotDeleteRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotDeleteRequest.java index 3f8bae61c530..9313bc815d9b 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotDeleteRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotDeleteRequest.java @@ -92,11 +92,13 @@ public OMRequest preExecute(OzoneManager ozoneManager) throws IOException { UserGroupInformation ugi = createUGIForApi(); String bucketOwner = ozoneManager.getBucketOwner(volumeName, bucketName, IAccessAuthorizer.ACLType.READ, OzoneObj.ResourceType.BUCKET); - if (!ozoneManager.isAdmin(ugi) && - !ozoneManager.isOwner(ugi, bucketOwner)) { - throw new OMException( - "Only bucket owners and Ozone admins can delete snapshots", - OMException.ResultCodes.PERMISSION_DENIED); + if (ozoneManager.isAdminAuthorizationEnabled()) { + if (!ozoneManager.isAdmin(ugi) && + !ozoneManager.isOwner(ugi, bucketOwner)) { + throw new OMException( + "Only bucket owners and Ozone admins can delete snapshots", + OMException.ResultCodes.PERMISSION_DENIED); + } } // Set deletion time here so OM leader and follower would have the diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotRenameRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotRenameRequest.java index 7a4cdc640dce..2d9bd5c21ab1 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotRenameRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotRenameRequest.java @@ -88,11 +88,13 @@ public OMRequest preExecute(OzoneManager ozoneManager) throws IOException { UserGroupInformation ugi = createUGIForApi(); String bucketOwner = ozoneManager.getBucketOwner(volumeName, bucketName, IAccessAuthorizer.ACLType.READ, OzoneObj.ResourceType.BUCKET); - if (!ozoneManager.isAdmin(ugi) && - !ozoneManager.isOwner(ugi, bucketOwner)) { - throw new OMException( - "Only bucket owners and Ozone admins can rename snapshots", - OMException.ResultCodes.PERMISSION_DENIED); + if (ozoneManager.isAdminAuthorizationEnabled()) { + if (!ozoneManager.isAdmin(ugi) && + !ozoneManager.isOwner(ugi, bucketOwner)) { + throw new OMException( + "Only bucket owners and Ozone admins can rename snapshots", + OMException.ResultCodes.PERMISSION_DENIED); + } } // Set rename time here so OM leader and follower would have the diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMCancelPrepareRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMCancelPrepareRequest.java index efbdf6bcd189..2a9d36940e64 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMCancelPrepareRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMCancelPrepareRequest.java @@ -66,7 +66,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, Execut try { UserGroupInformation ugi = createUGIForApi(); - if (ozoneManager.getAclsEnabled() && !ozoneManager.isAdmin(ugi)) { + if (ozoneManager.isAdminAuthorizationEnabled() && !ozoneManager.isAdmin(ugi)) { throw new OMException("Access denied for user " + ugi + ". " + "Superuser privilege is required to cancel ozone manager " + diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMFinalizeUpgradeRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMFinalizeUpgradeRequest.java index b37d1ee6d1dc..e401af95d582 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMFinalizeUpgradeRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMFinalizeUpgradeRequest.java @@ -68,7 +68,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, Execut Exception exception = null; try { - if (ozoneManager.getAclsEnabled()) { + if (ozoneManager.isAdminAuthorizationEnabled()) { UserGroupInformation ugi = createUGIForApi(); if (!ozoneManager.isAdmin(ugi)) { throw new OMException("Access denied for user " + ugi + ". " diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/volume/OMQuotaRepairRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/volume/OMQuotaRepairRequest.java index b4c05a1263ed..08b38cb2174b 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/volume/OMQuotaRepairRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/volume/OMQuotaRepairRequest.java @@ -62,7 +62,7 @@ public OMQuotaRepairRequest(OMRequest omRequest) { @Override public OMRequest preExecute(OzoneManager ozoneManager) throws IOException { UserGroupInformation ugi = createUGIForApi(); - if (ozoneManager.getAclsEnabled() && !ozoneManager.isAdmin(ugi)) { + if (ozoneManager.isAdminAuthorizationEnabled() && !ozoneManager.isAdmin(ugi)) { throw new OMException("Access denied for user " + ugi + ". Admin privilege is required for quota repair.", OMException.ResultCodes.ACCESS_DENIED); } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OMAdminProtocolServerSideImpl.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OMAdminProtocolServerSideImpl.java index 7cd3a3e3d71d..8b76f6c0fe43 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OMAdminProtocolServerSideImpl.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OMAdminProtocolServerSideImpl.java @@ -98,7 +98,8 @@ public DecommissionOMResponse decommission(RpcController controller, } try { - if (!ozoneManager.isAdmin(getRemoteUser())) { + if (ozoneManager.isAdminAuthorizationEnabled() && + !ozoneManager.isAdmin(getRemoteUser())) { throw new OMException("Only administrators are authorized to perform decommission.", PERMISSION_DENIED); } omRatisServer.removeOMFromRatisRing(decommNode); diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerStateMachine.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerStateMachine.java index d799556a6f91..36d7a80aeea9 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerStateMachine.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerStateMachine.java @@ -223,7 +223,7 @@ public void testPreAppendTransactionAclDenied() { OzoneConfiguration conf = new OzoneConfiguration(); OzoneManagerPrepareState ps = new OzoneManagerPrepareState(conf); when(om.getPrepareState()).thenReturn(ps); - when(om.getAclsEnabled()).thenReturn(true); + when(om.isAdminAuthorizationEnabled()).thenReturn(true); when(om.isAdmin(any(UserGroupInformation.class))).thenReturn(false); OMRequest prepareRequest = OMRequest.newBuilder() diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotCreateRequest.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotCreateRequest.java index 4136f4da20f5..80cfba97bb80 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotCreateRequest.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotCreateRequest.java @@ -144,6 +144,7 @@ public void testPreExecuteFailure(String snapshotName) { @Test public void testPreExecuteBadOwner() { + when(getOzoneManager().isAdminAuthorizationEnabled()).thenReturn(true); // Owner is not set for the request. OMRequest omRequest = createSnapshotRequest(getVolumeName(), getBucketName(), snapshotName1); diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotDeleteRequest.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotDeleteRequest.java index 267e99829acf..d007b1ae29ec 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotDeleteRequest.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotDeleteRequest.java @@ -128,6 +128,8 @@ public void testPreExecuteFailure(String deleteSnapshotName) { @Test public void testPreExecuteBadOwner() { + when(getOzoneManager().isAdminAuthorizationEnabled()).thenReturn(true); + // Owner is not set for the request. OMRequest omRequest = deleteSnapshotRequest(getVolumeName(), getBucketName(), snapshotName); diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotRenameRequest.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotRenameRequest.java index 6430b9a7f50c..87de986a2a12 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotRenameRequest.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotRenameRequest.java @@ -138,6 +138,8 @@ public void testPreExecuteFailure(String toSnapshotName) { @Test public void testPreExecuteBadOwner() { + when(getOzoneManager().isAdminAuthorizationEnabled()).thenReturn(true); + // Owner is not set for the request. OzoneManagerProtocolProtos.OMRequest omRequest = renameSnapshotRequest(getVolumeName(), getBucketName(), snapshotName1, snapshotName2); diff --git a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconRestServletModule.java b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconRestServletModule.java index 9ec9621ed329..d3b631cac3f6 100644 --- a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconRestServletModule.java +++ b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconRestServletModule.java @@ -17,9 +17,6 @@ package org.apache.hadoop.ozone.recon; -import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED; -import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED_DEFAULT; - import com.google.inject.Injector; import com.google.inject.Scopes; import com.google.inject.servlet.ServletModule; @@ -118,9 +115,8 @@ private void addFilters(String basePath, Set adminSubPaths) { LOG.debug("Added authentication filter to path {}", authPath); } - boolean aclEnabled = conf.getBoolean(OZONE_ACL_ENABLED, - OZONE_ACL_ENABLED_DEFAULT); - if (aclEnabled) { + boolean authorizationEnabled = OzoneSecurityUtil.isAuthorizationEnabled(conf); + if (authorizationEnabled) { for (String path: adminSubPaths) { String adminPath = UriBuilder.fromPath(basePath).path(path + "*").build().toString(); diff --git a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/filters/ReconAdminFilter.java b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/filters/ReconAdminFilter.java index f4ae82b7d613..5cc82f384583 100644 --- a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/filters/ReconAdminFilter.java +++ b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/filters/ReconAdminFilter.java @@ -29,6 +29,8 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.hadoop.hdds.conf.OzoneConfiguration; +import org.apache.hadoop.ozone.OzoneSecurityUtil; import org.apache.hadoop.ozone.recon.ReconServer; import org.apache.hadoop.security.UserGroupInformation; import org.slf4j.Logger; @@ -45,10 +47,12 @@ public class ReconAdminFilter implements Filter { LoggerFactory.getLogger(ReconAdminFilter.class); private final ReconServer reconServer; + private final OzoneConfiguration conf; @Inject - ReconAdminFilter(ReconServer reconServer) { + ReconAdminFilter(ReconServer reconServer, OzoneConfiguration conf) { this.reconServer = reconServer; + this.conf = conf; } @Override @@ -98,6 +102,15 @@ public void doFilter(ServletRequest servletRequest, public void destroy() { } private boolean hasPermission(UserGroupInformation user) { + // Check authorization first - only check admin if authorization is enabled + if (!isAdminAuthorizationEnabled()) { + return true; // Authorization disabled, allow all + } + return reconServer.isAdmin(user); } + + private boolean isAdminAuthorizationEnabled() { + return OzoneSecurityUtil.isAuthorizationEnabled(conf); + } } diff --git a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/filters/TestAdminFilter.java b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/filters/TestAdminFilter.java index c8c8797b1986..75e06a007898 100644 --- a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/filters/TestAdminFilter.java +++ b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/filters/TestAdminFilter.java @@ -198,6 +198,7 @@ public void testAdminFilterStarterUserPlusConfiguredAdmins() throws Exception { private void testAdminFilterWithPrincipal(OzoneConfiguration conf, String principalToUse, boolean shouldPass) throws Exception { + conf.setBoolean(OzoneConfigKeys.OZONE_SECURITY_ENABLED_KEY, true); ReconServer mockReconServer = createMockReconServer(conf); Principal mockPrincipal = mock(Principal.class); @@ -208,7 +209,7 @@ private void testAdminFilterWithPrincipal(OzoneConfiguration conf, HttpServletResponse mockResponse = mock(HttpServletResponse.class); FilterChain mockFilterChain = mock(FilterChain.class); - ReconAdminFilter filter = new ReconAdminFilter(mockReconServer); + ReconAdminFilter filter = new ReconAdminFilter(mockReconServer, conf); filter.init(null); filter.doFilter(mockRequest, mockResponse, mockFilterChain); diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3secret/S3SecretAdminFilter.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3secret/S3SecretAdminFilter.java index 0130f31e9dd3..889f466e7dfb 100644 --- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3secret/S3SecretAdminFilter.java +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3secret/S3SecretAdminFilter.java @@ -27,6 +27,7 @@ import javax.ws.rs.ext.Provider; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.server.OzoneAdmins; +import org.apache.hadoop.ozone.OzoneSecurityUtil; import org.apache.hadoop.security.UserGroupInformation; /** @@ -46,6 +47,11 @@ public class S3SecretAdminFilter implements ContainerRequestFilter { @Override public void filter(ContainerRequestContext requestContext) throws IOException { + // Skip check if authorization is disabled + if (!OzoneSecurityUtil.isAuthorizationEnabled(conf)) { + return; + } + final Principal userPrincipal = requestContext.getSecurityContext().getUserPrincipal(); if (null != userPrincipal) { UserGroupInformation user = UserGroupInformation.createRemoteUser(userPrincipal.getName());