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

weichiu 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 0bf6cb5c39 HDDS-8573. Verify default setting for DN root dir to 
restrict non-admin access (#4682)
0bf6cb5c39 is described below

commit 0bf6cb5c397eb2bda9dd455a1edab4ef4177dcc0
Author: Arafat2198 <[email protected]>
AuthorDate: Wed May 24 22:20:50 2023 +0530

    HDDS-8573. Verify default setting for DN root dir to restrict non-admin 
access (#4682)
---
 .../apache/hadoop/hdds/recon/ReconConfigKeys.java  |   5 +
 .../org/apache/hadoop/hdds/scm/ScmConfigKeys.java  |   4 +
 .../org/apache/hadoop/hdds/server/ServerUtils.java |  96 +++++++++++-
 .../org/apache/hadoop/ozone/OzoneConfigKeys.java   |   8 +
 .../common/src/main/resources/ozone-default.xml    |  36 +++++
 .../apache/hadoop/hdds/server/TestServerUtils.java | 162 ++++++++++++++++++++-
 .../org/apache/hadoop/ozone/om/OMConfigKeys.java   |   4 +
 7 files changed, 307 insertions(+), 8 deletions(-)

diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/recon/ReconConfigKeys.java
 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/recon/ReconConfigKeys.java
index c1f36fb6de..21eb4a1a4d 100644
--- 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/recon/ReconConfigKeys.java
+++ 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/recon/ReconConfigKeys.java
@@ -32,6 +32,11 @@ public final class ReconConfigKeys {
 
   public static final String RECON_SCM_CONFIG_PREFIX = "ozone.recon.scmconfig";
 
+  public static final String OZONE_RECON_DB_DIR = "ozone.recon.db.dir";
+
+  public static final String OZONE_RECON_DB_DIRS_PERMISSIONS =
+      "ozone.recon.db.dirs.permissions";
+
   public static final String OZONE_RECON_DATANODE_ADDRESS_KEY =
       "ozone.recon.datanode.address";
   public static final String OZONE_RECON_ADDRESS_KEY =
diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/ScmConfigKeys.java
 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/ScmConfigKeys.java
index 2c80c74f29..1ed7e0a806 100644
--- 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/ScmConfigKeys.java
+++ 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/ScmConfigKeys.java
@@ -36,6 +36,10 @@ public final class ScmConfigKeys {
   // performance.
   public static final String OZONE_SCM_DB_DIRS = "ozone.scm.db.dirs";
 
+  // SCM DB directory permission
+  public static final String OZONE_SCM_DB_DIRS_PERMISSIONS =
+      "ozone.scm.db.dirs.permissions";
+
   public static final String DFS_CONTAINER_RATIS_ENABLED_KEY
       = "dfs.container.ratis.enabled";
   public static final boolean DFS_CONTAINER_RATIS_ENABLED_DEFAULT
diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/server/ServerUtils.java
 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/server/ServerUtils.java
index 08f04f1cde..6c0272e256 100644
--- 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/server/ServerUtils.java
+++ 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/server/ServerUtils.java
@@ -19,14 +19,20 @@ package org.apache.hadoop.hdds.server;
 
 import java.io.File;
 import java.net.InetSocketAddress;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermissions;
 import java.util.Collection;
 
+import org.apache.hadoop.fs.permission.FsPermission;
 import org.apache.hadoop.hdds.HddsConfigKeys;
 import org.apache.hadoop.hdds.conf.ConfigurationSource;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.recon.ReconConfigKeys;
 import org.apache.hadoop.hdds.scm.ScmConfigKeys;
 import org.apache.hadoop.ipc.RPC;
 import org.apache.hadoop.ipc.Server;
+import org.apache.hadoop.ozone.OzoneConfigKeys;
 import org.apache.hadoop.security.UserGroupInformation;
 
 import org.apache.http.client.methods.HttpRequestBase;
@@ -41,6 +47,7 @@ public final class ServerUtils {
   private static final Logger LOG = LoggerFactory.getLogger(
       ServerUtils.class);
 
+
   private ServerUtils() {
   }
 
@@ -152,8 +159,10 @@ public final class ServerUtils {
   }
 
   /**
-   * Utility method to get value of a given key that corresponds to a DB
-   * directory.
+   * Utility method to retrieve the value of a key representing a DB directory
+   * and create a File object for the directory. The method also sets the
+   * directory permissions based on the configuration.
+   *
    * @param conf configuration bag
    * @param key Key to test
    * @param componentName Which component's key is this
@@ -163,7 +172,6 @@ public final class ServerUtils {
                                             String key,
                                             String componentName) {
     final Collection<String> metadirs = conf.getTrimmedStringCollection(key);
-
     if (metadirs.size() > 1) {
       throw new IllegalArgumentException(
           "Bad config setting " + key +
@@ -178,12 +186,90 @@ public final class ServerUtils {
             dbDirPath + " specified in configuration setting " +
             key);
       }
+      try {
+        Path path = dbDirPath.toPath();
+        // Fetch the permissions for the respective component from the config
+        String permissionValue = getPermissions(key, conf);
+        String symbolicPermission = getSymbolicPermission(permissionValue);
+
+        // Set the permissions for the directory
+        Files.setPosixFilePermissions(path,
+            PosixFilePermissions.fromString(symbolicPermission));
+      } catch (Exception e) {
+        throw new RuntimeException("Failed to set directory permissions for " +
+            dbDirPath + ": " + e.getMessage(), e);
+      }
       return dbDirPath;
     }
 
     return null;
   }
 
+  /**
+   * Fetches the symbolic representation of the permission value.
+   *
+   * @param permissionValue the permission value (octal or symbolic)
+   * @return the symbolic representation of the permission value
+   */
+  private static String getSymbolicPermission(String permissionValue) {
+    if (isSymbolic(permissionValue)) {
+      // For symbolic representation, use it directly
+      return permissionValue;
+    } else {
+      // For octal representation, convert it to FsPermission object and then
+      // to symbolic representation
+      short octalPermission = Short.parseShort(permissionValue, 8);
+      FsPermission fsPermission = new FsPermission(octalPermission);
+      return fsPermission.toString();
+    }
+  }
+
+  /**
+   * Checks if the permission value is in symbolic representation.
+   *
+   * @param permissionValue the permission value to check
+   * @return true if the permission value is in symbolic representation,
+   * false otherwise
+   */
+  private static boolean isSymbolic(String permissionValue) {
+    return permissionValue.matches(".*[rwx].*");
+  }
+
+
+  /**
+   * Retrieves the permissions' configuration value for a given config key.
+   *
+   * @param key  The configuration key.
+   * @param conf The ConfigurationSource object containing the config
+   * @return The permissions' configuration value for the specified key.
+   * @throws IllegalArgumentException If the configuration value is not defined
+   */
+  public static String getPermissions(String key, ConfigurationSource conf) {
+    String configName = "";
+
+    // Assign the appropriate config name based on the KEY
+    if (key.equals(ReconConfigKeys.OZONE_RECON_DB_DIR)) {
+      configName = ReconConfigKeys.OZONE_RECON_DB_DIRS_PERMISSIONS;
+    } else if (key.equals(ScmConfigKeys.OZONE_SCM_DB_DIRS)) {
+      configName = ScmConfigKeys.OZONE_SCM_DB_DIRS_PERMISSIONS;
+    } else if (key.equals(OzoneConfigKeys.OZONE_OM_DB_DIRS)) {
+      configName = OzoneConfigKeys.OZONE_OM_DB_DIRS_PERMISSIONS;
+    } else {
+      // If the permissions are not defined for the config, we make it fall
+      // back to the default permissions for metadata files and directories
+      configName = OzoneConfigKeys.OZONE_METADATA_DIRS_PERMISSIONS;
+    }
+
+    String configValue = conf.get(configName);
+    if (configValue != null) {
+      return configValue;
+    }
+
+    throw new IllegalArgumentException(
+        "Invalid configuration value for key: " + key);
+  }
+
+
   /**
    * Checks and creates Ozone Metadir Path if it does not exist.
    *
@@ -202,7 +288,7 @@ public final class ServerUtils {
   }
 
   public static void setOzoneMetaDirPath(OzoneConfiguration conf,
-      String path) {
+                                         String path) {
     conf.set(HddsConfigKeys.OZONE_METADATA_DIRS, path);
   }
 
@@ -212,7 +298,7 @@ public final class ServerUtils {
    * If the directory is missing the method tries to create it.
    *
    * @param conf The ozone configuration object
-   * @param key The configuration key which specify the directory.
+   * @param key  The configuration key which specify the directory.
    * @return The path of the directory.
    */
   public static File getDBPath(ConfigurationSource conf, String key) {
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 9493dcb1a7..587f45db5d 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
@@ -42,6 +42,14 @@ public final class OzoneConfigKeys {
 
   public static final String OZONE_METADATA_DIRS = "ozone.metadata.dirs";
 
+  public static final String OZONE_METADATA_DIRS_PERMISSIONS =
+      "ozone.metadata.dirs.permissions";
+  public static final String OZONE_OM_DB_DIRS_PERMISSIONS =
+      "ozone.om.db.dirs.permissions";
+
+
+  public static final String OZONE_OM_DB_DIRS = "ozone.om.db.dirs";
+
   /**
    *
    * When set to true, allocate a random free port for ozone container,
diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml 
b/hadoop-hdds/common/src/main/resources/ozone-default.xml
index 5457dc04eb..c79112d3bb 100644
--- a/hadoop-hdds/common/src/main/resources/ozone-default.xml
+++ b/hadoop-hdds/common/src/main/resources/ozone-default.xml
@@ -680,6 +680,15 @@
       production environments.
     </description>
   </property>
+  <property>
+    <name>ozone.om.db.dirs.permissions</name>
+    <value>750</value>
+    <description>
+      Permissions for the metadata directories for Ozone Manager. The
+      permissions have to be octal or symbolic. If the default permissions are 
not set
+      then the default value of 750 will be used.
+    </description>
+  </property>
   <property>
     <name>ozone.metadata.dirs</name>
     <value/>
@@ -694,6 +703,15 @@
       dfs.container.ratis.datanode.storage.dir be configured separately.
     </description>
   </property>
+  <property>
+    <name>ozone.metadata.dirs.permissions</name>
+    <value>750</value>
+    <description>
+      Permissions for the metadata directories for fallback location for SCM, 
OM, Recon and DataNodes
+      to store their metadata. The permissions have to be octal or symbolic. 
This is the fallback used in case the
+      default permissions for OM,SCM,Recon,Datanode are not set.
+    </description>
+  </property>
 
   <property>
     <name>ozone.metastore.rocksdb.statistics</name>
@@ -731,6 +749,15 @@
       production environments.
     </description>
   </property>
+  <property>
+    <name>ozone.scm.db.dirs.permissions</name>
+    <value>750</value>
+    <description>
+      Permissions for the metadata directories for Storage Container Manager. 
The
+      permissions can either be octal or symbolic. If the default permissions 
are not set
+      then the default value of 750 will be used.
+    </description>
+  </property>
   <property>
     <name>ozone.scm.block.client.address</name>
     <value/>
@@ -2786,6 +2813,15 @@
       production environments.
     </description>
   </property>
+  <property>
+    <name>ozone.recon.db.dirs.permissions</name>
+    <value>750</value>
+    <description>
+      Permissions for the metadata directories for Recon. The
+      permissions can either be octal or symbolic. If the default permissions 
are not set
+      then the default value of 750 will be used.
+    </description>
+  </property>
   <property>
     <name>ozone.scm.network.topology.schema.file</name>
     <value>network-topology-default.xml</value>
diff --git 
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/server/TestServerUtils.java
 
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/server/TestServerUtils.java
index 61b1fe4b57..2799ec0878 100644
--- 
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/server/TestServerUtils.java
+++ 
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/server/TestServerUtils.java
@@ -18,25 +18,181 @@
 package org.apache.hadoop.hdds.server;
 
 import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Set;
 
 import org.apache.hadoop.hdds.HddsConfigKeys;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.recon.ReconConfigKeys;
 import org.apache.hadoop.hdds.scm.ScmConfigKeys;
+import org.apache.hadoop.ozone.OzoneConfigKeys;
 import org.apache.hadoop.test.PathUtils;
 
 import org.apache.commons.io.FileUtils;
-import org.junit.jupiter.api.Test;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+
 
 /**
  * Unit tests for {@link ServerUtils}.
  */
 public class TestServerUtils {
 
+  @Rule
+  public TemporaryFolder folder = new TemporaryFolder();
+
+  /**
+   * Test case for {@link ServerUtils#getPermissions}.
+   * Verifies the retrieval of permissions for different configs.
+   */
+  @Test
+  public void testGetPermissions() {
+    // Create an OzoneConfiguration object and set the permissions
+    // for different keys
+    OzoneConfiguration conf = new OzoneConfiguration();
+    conf.set(ReconConfigKeys.OZONE_RECON_DB_DIRS_PERMISSIONS, "750");
+    conf.set(ScmConfigKeys.OZONE_SCM_DB_DIRS_PERMISSIONS, "775");
+    conf.set(OzoneConfigKeys.OZONE_METADATA_DIRS_PERMISSIONS, "770");
+    conf.set(OzoneConfigKeys.OZONE_OM_DB_DIRS_PERMISSIONS, "700");
+
+    // Test getPermissions for different config names and assert the
+    // returned permissions
+    assertEquals("750",
+        ServerUtils.getPermissions(ReconConfigKeys.OZONE_RECON_DB_DIR, conf));
+    assertEquals("775",
+        ServerUtils.getPermissions(ScmConfigKeys.OZONE_SCM_DB_DIRS, conf));
+    assertEquals("770",
+        ServerUtils.getPermissions(OzoneConfigKeys.OZONE_METADATA_DIRS, conf));
+    assertEquals("700",
+        ServerUtils.getPermissions(OzoneConfigKeys.OZONE_OM_DB_DIRS, conf));
+
+  }
+
+  @Test
+  public void testGetDirectoryFromConfigWithOctalPermissions()
+      throws IOException {
+    // Create a temporary directory
+    String filePath = folder.getRoot().getAbsolutePath();
+
+    // Create an OzoneConfiguration object
+    OzoneConfiguration conf = new OzoneConfiguration();
+    String key = OzoneConfigKeys.OZONE_METADATA_DIRS;
+    String componentName = "Ozone";
+    conf.set(key, filePath);
+
+    // Set octal permissions
+    String octalPermissionValue = "750";
+    String octalExpectedPermissions = "rwxr-x---";
+    conf.set(OzoneConfigKeys.OZONE_METADATA_DIRS_PERMISSIONS,
+        octalPermissionValue);
+
+    // Get the directory using ServerUtils method
+    File directory =
+        ServerUtils.getDirectoryFromConfig(conf, key, componentName);
+
+    // Assert that the directory is not null and exists
+    assertNotNull(directory);
+    assertTrue(directory.exists());
+    assertTrue(directory.isDirectory());
+
+    // Get the path of the directory
+    Path directoryPath = directory.toPath();
+
+    // Convert the expected octal permissions string to a
+    // set of PosixFilePermission
+    Set<PosixFilePermission> octalExpectedPermissionSet =
+        PosixFilePermissions.fromString(octalExpectedPermissions);
+
+    // Get the actual permissions of the directory
+    Set<PosixFilePermission> actualPermissionSet =
+        Files.getPosixFilePermissions(directoryPath);
+
+    // Assert that the expected and actual permissions sets are equal
+    assertEquals(octalExpectedPermissionSet, actualPermissionSet);
+  }
+
+  @Test
+  public void testGetDirectoryFromConfigWithSymbolicPermissions()
+      throws IOException {
+    // Create a temporary directory
+    String filePath = folder.getRoot().getAbsolutePath();
+
+    OzoneConfiguration conf = new OzoneConfiguration();
+    String key = OzoneConfigKeys.OZONE_METADATA_DIRS;
+    String componentName = "Ozone";
+    conf.set(key, filePath);
+
+    // Set symbolic permissions
+    String symbolicPermissionValue = "rwx------";
+    String symbolicExpectedPermissions = "rwx------";
+    conf.set(OzoneConfigKeys.OZONE_METADATA_DIRS_PERMISSIONS,
+        symbolicPermissionValue);
+
+    File directory =
+        ServerUtils.getDirectoryFromConfig(conf, key, componentName);
+
+    assertNotNull(directory);
+    assertTrue(directory.exists());
+    assertTrue(directory.isDirectory());
+
+    Path directoryPath = directory.toPath();
+
+    Set<PosixFilePermission> symbolicExpectedPermissionSet =
+        PosixFilePermissions.fromString(symbolicExpectedPermissions);
+
+    Set<PosixFilePermission> actualPermissionSet =
+        Files.getPosixFilePermissions(directoryPath);
+
+    assertEquals(symbolicExpectedPermissionSet, actualPermissionSet);
+  }
+
+  @Test
+  public void testGetDirectoryFromConfigWithMultipleDirectories()
+      throws IOException {
+    // Create temporary directories
+    File dir1 = folder.newFolder("dir1");
+    File dir2 = folder.newFolder("dir2");
+
+    // Create an OzoneConfiguration object
+    OzoneConfiguration conf = new OzoneConfiguration();
+    String key = OzoneConfigKeys.OZONE_METADATA_DIRS;
+    String componentName = "Ozone";
+    conf.set(key, dir1.getAbsolutePath() + "," + dir2.getAbsolutePath());
+
+    // Assert that an IllegalArgumentException is thrown because Recon does not
+    // support multiple metadata dirs currently
+    assertThrows(IllegalArgumentException.class, () -> {
+      ServerUtils.getDirectoryFromConfig(conf, key, componentName);
+    });
+  }
+
+  @Test
+  public void testGetDirectoryFromConfigWithNoDirectory() {
+    // Create an empty OzoneConfiguration object
+    OzoneConfiguration conf = new OzoneConfiguration();
+    String key = OzoneConfigKeys.OZONE_METADATA_DIRS;
+    String componentName = "Ozone";
+
+    // Get the directory using ServerUtils method
+    File directory =
+        ServerUtils.getDirectoryFromConfig(conf, key, componentName);
+
+    // Assert that the directory is null
+    assertNull(directory);
+  }
+
   /**
    * Test {@link ServerUtils#getScmDbDir}.
    */
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
index 5c6a39d198..4442b6d061 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
@@ -53,6 +53,10 @@ public final class OMConfigKeys {
   // multiple entries for performance (sharding)..
   public static final String OZONE_OM_DB_DIRS = "ozone.om.db.dirs";
 
+  // SCM DB directory permission
+  public static final String OZONE_OM_DB_DIRS_PERMISSIONS =
+      "ozone.om.db.dirs.permissions";
+
   public static final String OZONE_OM_HANDLER_COUNT_KEY =
       "ozone.om.handler.count.key";
   public static final int OZONE_OM_HANDLER_COUNT_DEFAULT = 100;


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

Reply via email to