You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by sa...@apache.org on 2023/04/21 08:44:20 UTC

[ozone] branch master updated: HDDS-8455. Om supports read only administrators. (#4565)

This is an automated email from the ASF dual-hosted git repository.

sammichen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new e7cd92c467 HDDS-8455. Om supports read only administrators. (#4565)
e7cd92c467 is described below

commit e7cd92c4671fa4e49ea9c2bb816744c2b3fd61ea
Author: z-bb <35...@users.noreply.github.com>
AuthorDate: Fri Apr 21 16:44:13 2023 +0800

    HDDS-8455. Om supports read only administrators. (#4565)
---
 .../org/apache/hadoop/ozone/OzoneConfigKeys.java   | 10 ++-
 .../common/src/main/resources/ozone-default.xml    | 18 +++++
 .../apache/hadoop/ozone/om/OmMetadataReader.java   | 17 ++++-
 .../apache/hadoop/ozone/om/OzoneConfigUtil.java    | 16 +++++
 .../org/apache/hadoop/ozone/om/OzoneManager.java   |  7 ++
 .../ozone/security/acl/OzoneNativeAuthorizer.java  | 28 ++++++--
 .../security/acl/TestOzoneAdministrators.java      | 80 ++++++++++++++++++++++
 7 files changed, 164 insertions(+), 12 deletions(-)

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 ede1b4fd87..e4e4d4e85d 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
@@ -179,9 +179,15 @@ public final class OzoneConfigKeys {
       "ozone.administrators.groups";
 
   public static final String OZONE_S3_ADMINISTRATORS =
-          "ozone.s3.administrators";
+      "ozone.s3.administrators";
   public static final String OZONE_S3_ADMINISTRATORS_GROUPS =
-          "ozone.s3.administrators.groups";
+      "ozone.s3.administrators.groups";
+
+  public static final String OZONE_READONLY_ADMINISTRATORS =
+      "ozone.readonly.administrators";
+  public static final String OZONE_READONLY_ADMINISTRATORS_GROUPS =
+      "ozone.readonly.administrators.groups";
+
   /**
    * Used only for testing purpose. Results in making every user an admin.
    * */
diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml b/hadoop-hdds/common/src/main/resources/ozone-default.xml
index 1879ac4f6c..c5186258e3 100644
--- a/hadoop-hdds/common/src/main/resources/ozone-default.xml
+++ b/hadoop-hdds/common/src/main/resources/ozone-default.xml
@@ -1635,6 +1635,24 @@
     </description>
   </property>
 
+  <property>
+    <name>ozone.readonly.administrators</name>
+    <value/>
+    <description>
+      Ozone read only admin users delimited by the comma.
+      If set, This is the list of users are allowed to read operations skip checkAccess.
+    </description>
+  </property>
+
+  <property>
+    <name>ozone.readonly.administrators.groups</name>
+    <value/>
+    <description>
+      Ozone read only admin groups delimited by the comma.
+      If set, This is the list of groups are allowed to read operations skip checkAccess.
+    </description>
+  </property>
+
   <property>
     <name>ozone.s3g.volume.name</name>
     <value>s3v</value>
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataReader.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataReader.java
index 0d25596f9a..5818b64da2 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataReader.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataReader.java
@@ -44,6 +44,7 @@ import org.apache.hadoop.util.ReflectionUtils;
 import org.apache.hadoop.util.Time;
 import org.slf4j.Logger;
 import java.net.InetAddress;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
@@ -114,8 +115,8 @@ public class OmMetadataReader implements IOmMetadataReader, Auditor {
         authorizer.setBucketManager(bucketManager);
         authorizer.setKeyManager(keyManager);
         authorizer.setPrefixManager(prefixManager);
-        authorizer.setOzoneAdmins(
-            new OzoneAdmins(ozoneManager.getOmAdminUsernames()));
+        authorizer.setOzoneAdmins(ozoneManager.getOmAdmins());
+        authorizer.setOzoneReadOnlyAdmins(getOmReadOnlyAdmins(configuration));
         authorizer.setAllowListAllVolumes(allowListAllVolumes);
       } else {
         isNativeAuthorizerEnabled = false;
@@ -580,5 +581,15 @@ public class OmMetadataReader implements IOmMetadataReader, Auditor {
     return ResourceType.KEY;
   }
 
-  
+  private OzoneAdmins getOmReadOnlyAdmins(OzoneConfiguration configuration) {
+    // Get read only admin list
+    Collection<String> omReadOnlyAdmins =
+        OzoneConfigUtil.getOzoneReadOnlyAdminsFromConfig(
+            configuration);
+    Collection<String> omReadOnlyAdminsGroups =
+        OzoneConfigUtil.getOzoneReadOnlyAdminsGroupsFromConfig(
+            configuration);
+    return new OzoneAdmins(omReadOnlyAdmins,
+        omReadOnlyAdminsGroups);
+  }
 }
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneConfigUtil.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneConfigUtil.java
index 7a54a9e211..fcb74ae6b9 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneConfigUtil.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneConfigUtil.java
@@ -33,6 +33,8 @@ import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS;
 import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS_GROUPS;
 import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_S3_ADMINISTRATORS;
 import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_S3_ADMINISTRATORS_GROUPS;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_READONLY_ADMINISTRATORS;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_READONLY_ADMINISTRATORS_GROUPS;
 
 /**
  * Utility class for ozone configurations.
@@ -77,6 +79,14 @@ public final class OzoneConfigUtil {
     return ozAdmins;
   }
 
+  /**
+   * Return list of Ozone Read only admin Usernames from config.
+   */
+  static Collection<String> getOzoneReadOnlyAdminsFromConfig(
+      OzoneConfiguration conf) {
+    return conf.getTrimmedStringCollection(OZONE_READONLY_ADMINISTRATORS);
+  }
+
   static Collection<String> getOzoneAdminsGroupsFromConfig(
       OzoneConfiguration conf) {
     return conf.getTrimmedStringCollection(OZONE_ADMINISTRATORS_GROUPS);
@@ -94,6 +104,12 @@ public final class OzoneConfigUtil {
     return s3AdminsGroup;
   }
 
+  static Collection<String> getOzoneReadOnlyAdminsGroupsFromConfig(
+      OzoneConfiguration conf) {
+    return conf.getTrimmedStringCollection(
+        OZONE_READONLY_ADMINISTRATORS_GROUPS);
+  }
+
   public static ReplicationConfig resolveReplicationConfigPreference(
       HddsProtos.ReplicationType clientType,
       HddsProtos.ReplicationFactor clientFactor,
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 7f63862805..bc4162177f 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
@@ -4034,6 +4034,13 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
     return omAdmins.getAdminGroups();
   }
 
+  /**
+   * Return OzoneAdmins.
+   */
+  public OzoneAdmins getOmAdmins() {
+    return omAdmins;
+  }
+
   /**
    * Return true if a UserGroupInformation is OM admin, false otherwise.
    * @param callerUgi Caller UserGroupInformation
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneNativeAuthorizer.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneNativeAuthorizer.java
index f60f7be1fe..d7079fa952 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneNativeAuthorizer.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneNativeAuthorizer.java
@@ -34,8 +34,6 @@ import org.slf4j.LoggerFactory;
 import java.util.Objects;
 
 import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_REQUEST;
-import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.BUCKET;
-import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.VOLUME;
 
 /**
  * Public API for Ozone ACLs. Security providers providing support for Ozone
@@ -52,6 +50,7 @@ public class OzoneNativeAuthorizer implements IAccessAuthorizer {
   private KeyManager keyManager;
   private PrefixManager prefixManager;
   private OzoneAdmins ozAdmins;
+  private OzoneAdmins ozReadOnlyAdmins;
   private boolean allowListAllVolumes;
 
   public OzoneNativeAuthorizer() {
@@ -92,11 +91,21 @@ public class OzoneNativeAuthorizer implements IAccessAuthorizer {
     }
 
     // bypass all checks for admin
-    boolean isAdmin = isAdmin(context.getClientUgi());
+    boolean isAdmin = isAdmin(ozAdmins, context.getClientUgi());
     if (isAdmin) {
       return true;
     }
 
+    // bypass read checks for read only admin users
+    boolean isReadOnlyAdmin = isAdmin(ozReadOnlyAdmins,
+        context.getClientUgi());
+    if (isReadOnlyAdmin
+        && (context.getAclRights() == ACLType.READ
+        || context.getAclRights() == ACLType.READ_ACL
+        || context.getAclRights() == ACLType.LIST)) {
+      return true;
+    }
+
     boolean isOwner = isOwner(context.getClientUgi(), context.getOwnerName());
     boolean isListAllVolume = ((context.getAclRights() == ACLType.LIST) &&
         objInfo.getVolumeName().equals(OzoneConsts.OZONE_ROOT));
@@ -106,7 +115,7 @@ public class OzoneNativeAuthorizer implements IAccessAuthorizer {
 
     ACLType parentAclRight = OzoneAclUtils.getParentNativeAcl(
         context.getAclRights(), objInfo.getResourceType());
-    
+
     parentContext = RequestContext.newBuilder()
         .setClientUgi(context.getClientUgi())
         .setIp(context.getIp())
@@ -195,6 +204,10 @@ public class OzoneNativeAuthorizer implements IAccessAuthorizer {
     this.ozAdmins = ozoneAdmins;
   }
 
+  public void setOzoneReadOnlyAdmins(OzoneAdmins ozoneReadOnlyAdmins) {
+    this.ozReadOnlyAdmins = ozoneReadOnlyAdmins;
+  }
+
   public OzoneAdmins getOzoneAdmins() {
     return ozAdmins;
   }
@@ -217,13 +230,14 @@ public class OzoneNativeAuthorizer implements IAccessAuthorizer {
     return false;
   }
 
-  private boolean isAdmin(UserGroupInformation callerUgi) {
+  private boolean isAdmin(OzoneAdmins pOzAdmins,
+      UserGroupInformation callerUgi) {
     Preconditions.checkNotNull(callerUgi, "callerUgi should not be null!");
 
-    if (ozAdmins == null) {
+    if (pOzAdmins == null) {
       return false;
     }
 
-    return ozAdmins.isAdmin(callerUgi);
+    return pOzAdmins.isAdmin(callerUgi);
   }
 }
diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/acl/TestOzoneAdministrators.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/acl/TestOzoneAdministrators.java
index 896415b86b..68fb289c22 100644
--- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/acl/TestOzoneAdministrators.java
+++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/acl/TestOzoneAdministrators.java
@@ -57,6 +57,48 @@ public class TestOzoneAdministrators {
     }
   }
 
+  @Test
+  public void testBucketOperation() throws OMException {
+    UserGroupInformation.createUserForTesting("testuser",
+        new String[]{"testgroup"});
+    try {
+      OzoneObj obj = getTestBucketobj("testbucket");
+      RequestContext context = getUserRequestContext("testuser",
+          IAccessAuthorizer.ACLType.LIST);
+      nativeAuthorizer.setOzoneReadOnlyAdmins(new OzoneAdmins(
+          Collections.singletonList("testuser"), null));
+      Assert.assertTrue("matching read only admins are allowed to preform" +
+          "read operations", nativeAuthorizer.checkAccess(obj, context));
+
+      context = getUserRequestContext("testuser",
+          IAccessAuthorizer.ACLType.READ);
+      Assert.assertTrue("matching read only admins are allowed to preform" +
+          "read operations", nativeAuthorizer.checkAccess(obj, context));
+
+      context = getUserRequestContext("testuser",
+          IAccessAuthorizer.ACLType.READ_ACL);
+      Assert.assertTrue("matching read only admins are allowed to preform" +
+          "read operations", nativeAuthorizer.checkAccess(obj, context));
+
+      context = getUserRequestContext("testuser",
+          IAccessAuthorizer.ACLType.WRITE);
+      RequestContext finalContext = context;
+      // ACLType is WRITE
+      // execute volumeManager.checkAccess volumeManager is null
+      Assert.assertThrows(NullPointerException.class,
+          () -> nativeAuthorizer.checkAccess(obj, finalContext));
+
+      nativeAuthorizer.setOzoneReadOnlyAdmins(new OzoneAdmins(
+          null, Collections.singletonList("testgroup")));
+      context = getUserRequestContext("testuser",
+          IAccessAuthorizer.ACLType.READ_ACL);
+      Assert.assertTrue("matching read only admins are allowed to preform" +
+          "read operations", nativeAuthorizer.checkAccess(obj, context));
+    } finally {
+      UserGroupInformation.reset();
+    }
+  }
+
   @Test
   public void testListAllVolume() throws Exception {
     UserGroupInformation.createUserForTesting("testuser",
@@ -97,6 +139,21 @@ public class TestOzoneAdministrators {
         asList(new String[]{"testuser2", "testuser3"})));
     Assert.assertFalse("mismatching admins are not allowed perform " +
         "admin operations", nativeAuthorizer.checkAccess(obj, context));
+
+    nativeAuthorizer.setOzoneReadOnlyAdmins(new OzoneAdmins(
+        Collections.singletonList("testuser"), null));
+    if (context.getAclRights() == IAccessAuthorizer.ACLType.LIST) {
+      Assert.assertTrue("matching read only user are allowed to preform" +
+          "read operations", nativeAuthorizer.checkAccess(obj, context));
+    } else if (context.getAclRights() == IAccessAuthorizer.ACLType.CREATE) {
+      Assert.assertFalse("mismatching read only user are allowed to preform" +
+          "read operations", nativeAuthorizer.checkAccess(obj, context));
+    }
+
+    nativeAuthorizer.setOzoneReadOnlyAdmins(new OzoneAdmins(
+        Collections.singletonList("testuser1"), null));
+    Assert.assertFalse("mismatching read only user are allowed to preform" +
+        "read operations", nativeAuthorizer.checkAccess(obj, context));
   }
 
   private void testGroupAdminOperations(OzoneObj obj, RequestContext context)
@@ -112,6 +169,22 @@ public class TestOzoneAdministrators {
     Assert.assertFalse("Users from mismatching admin groups " +
         "are allowed to perform admin operations",
             nativeAuthorizer.checkAccess(obj, context));
+
+    nativeAuthorizer.setOzoneReadOnlyAdmins(new OzoneAdmins(
+        null, Collections.singletonList("testgroup")));
+    if (context.getAclRights() == IAccessAuthorizer.ACLType.LIST) {
+      Assert.assertTrue("matching read only groups are allowed to preform" +
+          "read operations", nativeAuthorizer.checkAccess(obj, context));
+    } else if (context.getAclRights() == IAccessAuthorizer.ACLType.CREATE) {
+      Assert.assertFalse("mismatching read only groups are allowed to " +
+          "preform read operations",
+              nativeAuthorizer.checkAccess(obj, context));
+    }
+
+    nativeAuthorizer.setOzoneReadOnlyAdmins(new OzoneAdmins(
+        null, Collections.singletonList("testgroup1")));
+    Assert.assertFalse("mismatching read only groups are allowed to preform" +
+        "read operations", nativeAuthorizer.checkAccess(obj, context));
   }
 
   private RequestContext getUserRequestContext(String username,
@@ -129,4 +202,11 @@ public class TestOzoneAdministrators {
         .setStoreType(OzoneObj.StoreType.OZONE)
         .setVolumeName(volumename).build();
   }
+
+  private OzoneObj getTestBucketobj(String bucketname) {
+    return OzoneObjInfo.Builder.newBuilder()
+        .setResType(OzoneObj.ResourceType.BUCKET)
+        .setStoreType(OzoneObj.StoreType.OZONE)
+        .setVolumeName(bucketname).build();
+  }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ozone.apache.org
For additional commands, e-mail: commits-help@ozone.apache.org