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

yuqi1129 pushed a commit to branch improve/job-uri-validation
in repository https://gitbox.apache.org/repos/asf/gravitino.git

commit d3dd0a94751e61d4f8529e4c48a3c91c118ae933
Author: yuqi <[email protected]>
AuthorDate: Fri Jun 5 11:30:49 2026 +0800

    feat: make local remote URI fetch configurable
---
 .../gravitino/catalog/hive/HiveConstants.java      |   2 +
 .../fileset/FilesetCatalogPropertiesMetadata.java  |  12 +++
 .../catalog/hive/HiveCatalogOperations.java        |   3 +
 .../hive/HiveCatalogPropertiesMetadata.java        |  14 +++
 .../catalog/hive/TestHiveCatalogOperations.java    |   4 +-
 .../iceberg/IcebergCatalogPropertiesMetadata.java  |  26 ++---
 .../paimon/PaimonCatalogPropertiesMetadata.java    |  21 ++--
 .../authentication/kerberos/FetchFileUtils.java    |  11 ++
 .../authentication/kerberos/KerberosClient.java    |   6 +-
 .../authentication/kerberos/KerberosConfig.java    |  29 +++++
 .../catalog/hadoop/fs/kerberos/FetchFileUtils.java |  15 +++
 .../catalog/hadoop/fs/kerberos/KerberosClient.java |   7 +-
 .../catalog/hadoop/fs/kerberos/KerberosConfig.java |  18 ++++
 .../gravitino/hive/kerberos/FetchFileUtils.java    |  18 ++++
 .../gravitino/hive/kerberos/KerberosClient.java    |   7 +-
 .../gravitino/hive/kerberos/KerberosConfig.java    |  18 ++++
 .../hive/kerberos/TestFetchFileUtils.java          |  56 ++++++++++
 .../apache/gravitino/utils/RemoteUriValidator.java |  93 ++++++++++++++++
 .../gravitino/utils/TestRemoteUriValidator.java    |  91 ++++++++++++++++
 .../main/java/org/apache/gravitino/Configs.java    |  14 +++
 .../java/org/apache/gravitino/job/JobManager.java  | 117 ++++++++++-----------
 .../org/apache/gravitino/job/TestJobManager.java   |  83 +++++++--------
 docs/apache-hive-catalog.md                        |  28 ++---
 docs/fileset-catalog.md                            |  17 +--
 docs/gravitino-server-config.md                    |  12 +++
 docs/iceberg-rest-service.md                       |  19 ++--
 docs/lakehouse-hudi-catalog.md                     |  17 +--
 docs/lakehouse-iceberg-catalog.md                  |  19 ++--
 docs/lakehouse-paimon-catalog.md                   |  51 ++++-----
 .../authentication/kerberos/FetchFileUtils.java    |  19 ++++
 .../authentication/kerberos/KerberosClient.java    |   7 +-
 .../authentication/kerberos/KerberosConfig.java    |  29 +++++
 32 files changed, 680 insertions(+), 203 deletions(-)

diff --git 
a/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/hive/HiveConstants.java
 
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/hive/HiveConstants.java
index e8459ba2a7..5c1a94c4c8 100644
--- 
a/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/hive/HiveConstants.java
+++ 
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/hive/HiveConstants.java
@@ -30,6 +30,8 @@ public class HiveConstants {
   public static final String PRINCIPAL = "kerberos.principal";
   public static final String CHECK_INTERVAL_SEC = 
"kerberos.check-interval-sec";
   public static final String FETCH_TIMEOUT_SEC = 
"kerberos.keytab-fetch-timeout-sec";
+  public static final String KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS =
+      "kerberos.keytab-fetch-allow-local-address";
   public static final String LIST_ALL_TABLES = "list-all-tables";
 
   // table properties
diff --git 
a/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogPropertiesMetadata.java
 
b/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogPropertiesMetadata.java
index 57c5a79842..dddff9359b 100644
--- 
a/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogPropertiesMetadata.java
+++ 
b/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogPropertiesMetadata.java
@@ -24,6 +24,7 @@ import static 
org.apache.gravitino.catalog.hadoop.fs.kerberos.AuthenticationConf
 import static 
org.apache.gravitino.catalog.hadoop.fs.kerberos.AuthenticationConfig.KERBEROS_DEFAULT_IMPERSONATION_ENABLE;
 import static 
org.apache.gravitino.catalog.hadoop.fs.kerberos.KerberosConfig.CHECK_INTERVAL_SEC_KEY;
 import static 
org.apache.gravitino.catalog.hadoop.fs.kerberos.KerberosConfig.FETCH_TIMEOUT_SEC_KEY;
+import static 
org.apache.gravitino.catalog.hadoop.fs.kerberos.KerberosConfig.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY;
 import static 
org.apache.gravitino.catalog.hadoop.fs.kerberos.KerberosConfig.KEY_TAB_URI_KEY;
 import static 
org.apache.gravitino.catalog.hadoop.fs.kerberos.KerberosConfig.PRINCIPAL_KEY;
 import static org.apache.gravitino.file.Fileset.LOCATION_NAME_UNKNOWN;
@@ -120,6 +121,17 @@ public class FilesetCatalogPropertiesMetadata extends 
BaseCatalogPropertiesMetad
                   false /* immutable */,
                   60 /* defaultValue */,
                   false /* hidden */))
+          .put(
+              KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY,
+              PropertyEntry.booleanPropertyEntry(
+                  KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY,
+                  "Whether to allow Kerberos keytab fetch from local or 
private addresses from "
+                      + "the Gravitino server side. This is disabled by 
default to prevent SSRF.",
+                  false /* required */,
+                  false /* immutable */,
+                  false /* defaultValue */,
+                  false /* hidden */,
+                  false /* reserved */))
           .build();
 
   public static final Map<String, PropertyEntry<?>> 
AUTHENTICATION_PROPERTY_ENTRIES =
diff --git 
a/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveCatalogOperations.java
 
b/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveCatalogOperations.java
index 87825ca913..e7af12b62c 100644
--- 
a/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveCatalogOperations.java
+++ 
b/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveCatalogOperations.java
@@ -19,6 +19,7 @@
 package org.apache.gravitino.catalog.hive;
 
 import static 
org.apache.gravitino.catalog.hive.HiveCatalogPropertiesMetadata.IMPERSONATION_ENABLE;
+import static 
org.apache.gravitino.catalog.hive.HiveCatalogPropertiesMetadata.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS;
 import static 
org.apache.gravitino.catalog.hive.HiveCatalogPropertiesMetadata.KEY_TAB_URI;
 import static 
org.apache.gravitino.catalog.hive.HiveCatalogPropertiesMetadata.LIST_ALL_TABLES;
 import static 
org.apache.gravitino.catalog.hive.HiveCatalogPropertiesMetadata.METASTORE_URIS;
@@ -30,6 +31,7 @@ import static 
org.apache.gravitino.catalog.hive.TableType.EXTERNAL_TABLE;
 import static org.apache.gravitino.connector.BaseCatalog.CATALOG_BYPASS_PREFIX;
 import static org.apache.gravitino.hive.HiveTable.SUPPORT_TABLE_TYPES;
 import static 
org.apache.gravitino.hive.kerberos.AuthenticationConfig.IMPERSONATION_ENABLE_KEY;
+import static 
org.apache.gravitino.hive.kerberos.KerberosConfig.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY;
 import static 
org.apache.gravitino.hive.kerberos.KerberosConfig.KEY_TAB_URI_KEY;
 import static org.apache.gravitino.hive.kerberos.KerberosConfig.PRINCIPAL_KEY;
 
@@ -123,6 +125,7 @@ public class HiveCatalogOperations
           METASTORE_URIS, HIVE_METASTORE_URIS,
           IMPERSONATION_ENABLE, IMPERSONATION_ENABLE_KEY,
           KEY_TAB_URI, KEY_TAB_URI_KEY,
+          KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS, 
KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY,
           PRINCIPAL, PRINCIPAL_KEY);
 
   /**
diff --git 
a/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveCatalogPropertiesMetadata.java
 
b/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveCatalogPropertiesMetadata.java
index 2577bdf944..6213eb20e8 100644
--- 
a/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveCatalogPropertiesMetadata.java
+++ 
b/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveCatalogPropertiesMetadata.java
@@ -48,6 +48,9 @@ public class HiveCatalogPropertiesMetadata extends 
BaseCatalogPropertiesMetadata
 
   public static final String FETCH_TIMEOUT_SEC = 
HiveConstants.FETCH_TIMEOUT_SEC;
 
+  public static final String KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS =
+      HiveConstants.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS;
+
   public static final String LIST_ALL_TABLES = HiveConstants.LIST_ALL_TABLES;
 
   public static final boolean DEFAULT_LIST_ALL_TABLES = false;
@@ -110,6 +113,17 @@ public class HiveCatalogPropertiesMetadata extends 
BaseCatalogPropertiesMetadata
               FETCH_TIMEOUT_SEC,
               PropertyEntry.integerOptionalPropertyEntry(
                   FETCH_TIMEOUT_SEC, "The timeout to fetch key tab", false, 
60, false))
+          .put(
+              KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS,
+              PropertyEntry.booleanPropertyEntry(
+                  KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS,
+                  "Whether to allow Kerberos keytab fetch from local or 
private addresses from "
+                      + "the Gravitino server side. This is disabled by 
default to prevent SSRF.",
+                  false /* required */,
+                  false /* immutable */,
+                  false /* defaultValue */,
+                  false /* hidden */,
+                  false /* reserved */))
           .put(
               LIST_ALL_TABLES,
               PropertyEntry.booleanPropertyEntry(
diff --git 
a/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/TestHiveCatalogOperations.java
 
b/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/TestHiveCatalogOperations.java
index a8ab3142d9..c091cef4f3 100644
--- 
a/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/TestHiveCatalogOperations.java
+++ 
b/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/TestHiveCatalogOperations.java
@@ -29,6 +29,7 @@ import static 
org.apache.gravitino.catalog.hive.HiveCatalogPropertiesMetadata.CL
 import static 
org.apache.gravitino.catalog.hive.HiveCatalogPropertiesMetadata.DEFAULT_CATALOG;
 import static 
org.apache.gravitino.catalog.hive.HiveCatalogPropertiesMetadata.FETCH_TIMEOUT_SEC;
 import static 
org.apache.gravitino.catalog.hive.HiveCatalogPropertiesMetadata.IMPERSONATION_ENABLE;
+import static 
org.apache.gravitino.catalog.hive.HiveCatalogPropertiesMetadata.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS;
 import static 
org.apache.gravitino.catalog.hive.HiveCatalogPropertiesMetadata.KEY_TAB_URI;
 import static 
org.apache.gravitino.catalog.hive.HiveCatalogPropertiesMetadata.LIST_ALL_TABLES;
 import static 
org.apache.gravitino.catalog.hive.HiveCatalogPropertiesMetadata.METASTORE_URIS;
@@ -88,7 +89,7 @@ class TestHiveCatalogOperations {
     Map<String, PropertyEntry<?>> propertyEntryMap =
         HIVE_PROPERTIES_METADATA.catalogPropertiesMetadata().propertyEntries();
 
-    Assertions.assertEquals(18, propertyEntryMap.size());
+    Assertions.assertEquals(19, propertyEntryMap.size());
     Assertions.assertTrue(propertyEntryMap.containsKey(METASTORE_URIS));
     
Assertions.assertTrue(propertyEntryMap.containsKey(Catalog.PROPERTY_PACKAGE));
     
Assertions.assertTrue(propertyEntryMap.containsKey(BaseCatalog.CATALOG_OPERATION_IMPL));
@@ -108,6 +109,7 @@ class TestHiveCatalogOperations {
     Assertions.assertFalse(propertyEntryMap.get(PRINCIPAL).isRequired());
     
Assertions.assertFalse(propertyEntryMap.get(CHECK_INTERVAL_SEC).isRequired());
     
Assertions.assertFalse(propertyEntryMap.get(FETCH_TIMEOUT_SEC).isRequired());
+    
Assertions.assertFalse(propertyEntryMap.get(KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS).isRequired());
     Assertions.assertFalse(propertyEntryMap.get(CLOUD_NAME).isRequired());
     Assertions.assertFalse(propertyEntryMap.get(CLOUD_NAME).isImmutable());
     
Assertions.assertFalse(propertyEntryMap.get(CLOUD_REGION_CODE).isRequired());
diff --git 
a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogPropertiesMetadata.java
 
b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogPropertiesMetadata.java
index 49680a512d..df2312374e 100644
--- 
a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogPropertiesMetadata.java
+++ 
b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogPropertiesMetadata.java
@@ -49,19 +49,19 @@ public class IcebergCatalogPropertiesMetadata extends 
BaseCatalogPropertiesMetad
   private static final Map<String, PropertyEntry<?>> PROPERTIES_METADATA;
 
   public static final Map<String, String> 
KERBEROS_CONFIGURATION_FOR_HIVE_BACKEND =
-      ImmutableMap.of(
-          KerberosConfig.PRINCIPAL_KEY,
-          KerberosConfig.PRINCIPAL_KEY,
-          KerberosConfig.KET_TAB_URI_KEY,
-          KerberosConfig.KET_TAB_URI_KEY,
-          KerberosConfig.CHECK_INTERVAL_SEC_KEY,
-          KerberosConfig.CHECK_INTERVAL_SEC_KEY,
-          KerberosConfig.FETCH_TIMEOUT_SEC_KEY,
-          KerberosConfig.FETCH_TIMEOUT_SEC_KEY,
-          AuthenticationConfig.IMPERSONATION_ENABLE_KEY,
-          AuthenticationConfig.IMPERSONATION_ENABLE_KEY,
-          AuthenticationConfig.AUTH_TYPE_KEY,
-          AuthenticationConfig.AUTH_TYPE_KEY);
+      ImmutableMap.<String, String>builder()
+          .put(KerberosConfig.PRINCIPAL_KEY, KerberosConfig.PRINCIPAL_KEY)
+          .put(KerberosConfig.KET_TAB_URI_KEY, KerberosConfig.KET_TAB_URI_KEY)
+          .put(KerberosConfig.CHECK_INTERVAL_SEC_KEY, 
KerberosConfig.CHECK_INTERVAL_SEC_KEY)
+          .put(KerberosConfig.FETCH_TIMEOUT_SEC_KEY, 
KerberosConfig.FETCH_TIMEOUT_SEC_KEY)
+          .put(
+              KerberosConfig.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY,
+              KerberosConfig.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY)
+          .put(
+              AuthenticationConfig.IMPERSONATION_ENABLE_KEY,
+              AuthenticationConfig.IMPERSONATION_ENABLE_KEY)
+          .put(AuthenticationConfig.AUTH_TYPE_KEY, 
AuthenticationConfig.AUTH_TYPE_KEY)
+          .build();
 
   static {
     List<PropertyEntry<?>> propertyEntries =
diff --git 
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonCatalogPropertiesMetadata.java
 
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonCatalogPropertiesMetadata.java
index b82061ac36..16c3b153d7 100644
--- 
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonCatalogPropertiesMetadata.java
+++ 
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonCatalogPropertiesMetadata.java
@@ -85,17 +85,16 @@ public class PaimonCatalogPropertiesMetadata extends 
BaseCatalogPropertiesMetada
           .build();
   private static final Map<String, PropertyEntry<?>> PROPERTIES_METADATA;
   public static final Map<String, String> KERBEROS_CONFIGURATION =
-      ImmutableMap.of(
-          KerberosConfig.PRINCIPAL_KEY,
-          KerberosConfig.PRINCIPAL_KEY,
-          KerberosConfig.KEY_TAB_URI_KEY,
-          KerberosConfig.KEY_TAB_URI_KEY,
-          KerberosConfig.CHECK_INTERVAL_SEC_KEY,
-          KerberosConfig.CHECK_INTERVAL_SEC_KEY,
-          KerberosConfig.FETCH_TIMEOUT_SEC_KEY,
-          KerberosConfig.FETCH_TIMEOUT_SEC_KEY,
-          AuthenticationConfig.AUTH_TYPE_KEY,
-          AuthenticationConfig.AUTH_TYPE_KEY);
+      ImmutableMap.<String, String>builder()
+          .put(KerberosConfig.PRINCIPAL_KEY, KerberosConfig.PRINCIPAL_KEY)
+          .put(KerberosConfig.KEY_TAB_URI_KEY, KerberosConfig.KEY_TAB_URI_KEY)
+          .put(KerberosConfig.CHECK_INTERVAL_SEC_KEY, 
KerberosConfig.CHECK_INTERVAL_SEC_KEY)
+          .put(KerberosConfig.FETCH_TIMEOUT_SEC_KEY, 
KerberosConfig.FETCH_TIMEOUT_SEC_KEY)
+          .put(
+              KerberosConfig.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY,
+              KerberosConfig.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY)
+          .put(AuthenticationConfig.AUTH_TYPE_KEY, 
AuthenticationConfig.AUTH_TYPE_KEY)
+          .build();
 
   public static final Map<String, String> S3_CONFIGURATION =
       ImmutableMap.of(
diff --git 
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/authentication/kerberos/FetchFileUtils.java
 
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/authentication/kerberos/FetchFileUtils.java
index 29cfdc37c5..03b4aeb8f4 100644
--- 
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/authentication/kerberos/FetchFileUtils.java
+++ 
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/authentication/kerberos/FetchFileUtils.java
@@ -25,6 +25,7 @@ import java.net.URISyntaxException;
 import java.nio.file.Files;
 import java.util.Optional;
 import org.apache.commons.io.FileUtils;
+import org.apache.gravitino.utils.RemoteUriValidator;
 
 public class FetchFileUtils {
 
@@ -32,6 +33,12 @@ public class FetchFileUtils {
 
   public static void fetchFileFromUri(String fileUri, File destFile, int 
timeout)
       throws IOException {
+    fetchFileFromUri(fileUri, destFile, timeout, false /* 
allowLocalAddressForRemoteUri */);
+  }
+
+  public static void fetchFileFromUri(
+      String fileUri, File destFile, int timeout, boolean 
allowLocalAddressForRemoteUri)
+      throws IOException {
     try {
       URI uri = new URI(fileUri);
       String scheme = Optional.ofNullable(uri.getScheme()).orElse("file");
@@ -40,6 +47,10 @@ public class FetchFileUtils {
         case "http":
         case "https":
         case "ftp":
+          RemoteUriValidator.validate(
+              uri,
+              allowLocalAddressForRemoteUri,
+              String.format("'%s' to true", 
KerberosConfig.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY));
           FileUtils.copyURLToFile(uri.toURL(), destFile, timeout * 1000, 
timeout * 1000);
           break;
 
diff --git 
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/authentication/kerberos/KerberosClient.java
 
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/authentication/kerberos/KerberosClient.java
index d8ae6c6438..c8bb7a3c18 100644
--- 
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/authentication/kerberos/KerberosClient.java
+++ 
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/authentication/kerberos/KerberosClient.java
@@ -100,7 +100,11 @@ public class KerberosClient implements Closeable {
     }
 
     int fetchKeytabFileTimeout = kerberosConfig.getFetchTimeoutSec();
-    FetchFileUtils.fetchFileFromUri(keyTabUri, keytabFile, 
fetchKeytabFileTimeout);
+    FetchFileUtils.fetchFileFromUri(
+        keyTabUri,
+        keytabFile,
+        fetchKeytabFileTimeout,
+        kerberosConfig.allowKeytabFetchLocalAddress());
 
     return keytabFile;
   }
diff --git 
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/authentication/kerberos/KerberosConfig.java
 
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/authentication/kerberos/KerberosConfig.java
index 93f46f7270..d99078a738 100644
--- 
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/authentication/kerberos/KerberosConfig.java
+++ 
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/authentication/kerberos/KerberosConfig.java
@@ -37,6 +37,9 @@ public class KerberosConfig extends AuthenticationConfig {
   public static final String FETCH_TIMEOUT_SEC_KEY =
       "authentication.kerberos.keytab-fetch-timeout-sec";
 
+  public static final String KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY =
+      "authentication.kerberos.keytab-fetch-allow-local-address";
+
   public static final ConfigEntry<String> PRINCIPAL_ENTRY =
       new ConfigBuilder(PRINCIPAL_KEY)
           .doc("The principal of the Kerberos connection")
@@ -69,6 +72,17 @@ public class KerberosConfig extends AuthenticationConfig {
           .checkValue(value -> value > 0, 
ConfigConstants.POSITIVE_NUMBER_ERROR_MSG)
           .createWithDefault(2);
 
+  public static final ConfigEntry<Boolean> 
KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_ENTRY =
+      new ConfigBuilder(KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY)
+          .doc(
+              "Whether to allow the Kerberos keytab URI to resolve to local, 
private, link-local, "
+                  + "or cloud metadata addresses from the Gravitino server 
side. This is disabled "
+                  + "by default to prevent SSRF. Set it to true only when the 
URI is trusted and "
+                  + "must be fetched from local or private addresses.")
+          .version(ConfigConstants.VERSION_1_3_0)
+          .booleanConf()
+          .createWithDefault(false);
+
   public KerberosConfig(Map<String, String> properties) {
     super(properties);
     loadFromMap(properties, k -> true);
@@ -90,6 +104,10 @@ public class KerberosConfig extends AuthenticationConfig {
     return get(FETCH_TIMEOUT_SEC_ENTRY);
   }
 
+  public boolean allowKeytabFetchLocalAddress() {
+    return get(KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_ENTRY);
+  }
+
   public static final Map<String, PropertyEntry<?>> KERBEROS_PROPERTY_ENTRIES =
       new ImmutableMap.Builder<String, PropertyEntry<?>>()
           .put(
@@ -124,5 +142,16 @@ public class KerberosConfig extends AuthenticationConfig {
                   false /* immutable */,
                   60 /* defaultValue */,
                   false /* hidden */))
+          .put(
+              KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY,
+              PropertyEntry.booleanPropertyEntry(
+                  KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY,
+                  "Whether to allow Kerberos keytab fetch from local or 
private addresses from "
+                      + "the Gravitino server side. This is disabled by 
default to prevent SSRF.",
+                  false /* required */,
+                  false /* immutable */,
+                  false /* defaultValue */,
+                  false /* hidden */,
+                  false /* reserved */))
           .build();
 }
diff --git 
a/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/kerberos/FetchFileUtils.java
 
b/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/kerberos/FetchFileUtils.java
index d014345c54..6d9fae3cd0 100644
--- 
a/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/kerberos/FetchFileUtils.java
+++ 
b/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/kerberos/FetchFileUtils.java
@@ -25,6 +25,7 @@ import java.net.URISyntaxException;
 import java.nio.file.Files;
 import java.util.Optional;
 import org.apache.commons.io.FileUtils;
+import org.apache.gravitino.utils.RemoteUriValidator;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
@@ -35,6 +36,16 @@ public class FetchFileUtils {
 
   public static void fetchFileFromUri(
       String fileUri, File destFile, int timeout, Configuration conf) throws 
IOException {
+    fetchFileFromUri(fileUri, destFile, timeout, conf, false /* 
allowLocalAddressForRemoteUri */);
+  }
+
+  public static void fetchFileFromUri(
+      String fileUri,
+      File destFile,
+      int timeout,
+      Configuration conf,
+      boolean allowLocalAddressForRemoteUri)
+      throws IOException {
     try {
       URI uri = new URI(fileUri);
       String scheme = Optional.ofNullable(uri.getScheme()).orElse("file");
@@ -43,6 +54,10 @@ public class FetchFileUtils {
         case "http":
         case "https":
         case "ftp":
+          RemoteUriValidator.validate(
+              uri,
+              allowLocalAddressForRemoteUri,
+              String.format("'%s' to true", 
KerberosConfig.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY));
           FileUtils.copyURLToFile(uri.toURL(), destFile, timeout * 1000, 
timeout * 1000);
           break;
 
diff --git 
a/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/kerberos/KerberosClient.java
 
b/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/kerberos/KerberosClient.java
index eb93f2ab72..3cef659bc2 100644
--- 
a/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/kerberos/KerberosClient.java
+++ 
b/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/kerberos/KerberosClient.java
@@ -122,7 +122,12 @@ public class KerberosClient implements Closeable {
     }
 
     int fetchKeytabFileTimeout = kerberosConfig.getFetchTimeoutSec();
-    FetchFileUtils.fetchFileFromUri(keyTabUri, keytabFile, 
fetchKeytabFileTimeout, hadoopConf);
+    FetchFileUtils.fetchFileFromUri(
+        keyTabUri,
+        keytabFile,
+        fetchKeytabFileTimeout,
+        hadoopConf,
+        kerberosConfig.allowKeytabFetchLocalAddress());
 
     return keytabFile;
   }
diff --git 
a/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/kerberos/KerberosConfig.java
 
b/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/kerberos/KerberosConfig.java
index 146fcf6324..eb2f2c4bef 100644
--- 
a/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/kerberos/KerberosConfig.java
+++ 
b/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/kerberos/KerberosConfig.java
@@ -39,6 +39,9 @@ public class KerberosConfig extends AuthenticationConfig {
   public static final String FETCH_TIMEOUT_SEC_KEY =
       "authentication.kerberos.keytab-fetch-timeout-sec";
 
+  public static final String KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY =
+      "authentication.kerberos.keytab-fetch-allow-local-address";
+
   public static final ConfigEntry<String> PRINCIPAL_ENTRY =
       new ConfigBuilder(PRINCIPAL_KEY)
           .doc("The principal of the Kerberos connection")
@@ -71,6 +74,17 @@ public class KerberosConfig extends AuthenticationConfig {
           .checkValue(value -> value > 0, 
ConfigConstants.POSITIVE_NUMBER_ERROR_MSG)
           .createWithDefault(2);
 
+  public static final ConfigEntry<Boolean> 
KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_ENTRY =
+      new ConfigBuilder(KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY)
+          .doc(
+              "Whether to allow the Kerberos keytab URI to resolve to local, 
private, link-local, "
+                  + "or cloud metadata addresses from the Gravitino server 
side. This is disabled "
+                  + "by default to prevent SSRF. Set it to true only when the 
URI is trusted and "
+                  + "must be fetched from local or private addresses.")
+          .version(ConfigConstants.VERSION_1_3_0)
+          .booleanConf()
+          .createWithDefault(false);
+
   public KerberosConfig(Map<String, String> properties, Configuration 
configuration) {
     super(properties, configuration);
     loadFromHdfsConfiguration(configuration);
@@ -99,4 +113,8 @@ public class KerberosConfig extends AuthenticationConfig {
   public int getFetchTimeoutSec() {
     return get(FETCH_TIMEOUT_SEC_ENTRY);
   }
+
+  public boolean allowKeytabFetchLocalAddress() {
+    return get(KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_ENTRY);
+  }
 }
diff --git 
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/FetchFileUtils.java
 
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/FetchFileUtils.java
index 7149e1c82d..543ca61549 100644
--- 
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/FetchFileUtils.java
+++ 
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/FetchFileUtils.java
@@ -27,6 +27,7 @@ import java.nio.file.StandardCopyOption;
 import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import org.apache.commons.io.FileUtils;
+import org.apache.gravitino.utils.RemoteUriValidator;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
@@ -55,6 +56,16 @@ public class FetchFileUtils {
 
   public static void fetchFileFromUri(
       String fileUri, File destFile, int timeout, Configuration conf) throws 
IOException {
+    fetchFileFromUri(fileUri, destFile, timeout, conf, false /* 
allowLocalAddressForRemoteUri */);
+  }
+
+  public static void fetchFileFromUri(
+      String fileUri,
+      File destFile,
+      int timeout,
+      Configuration conf,
+      boolean allowLocalAddressForRemoteUri)
+      throws IOException {
     try {
       URI uri = new URI(fileUri);
       String scheme = Optional.ofNullable(uri.getScheme()).orElse("file");
@@ -63,6 +74,13 @@ public class FetchFileUtils {
         case "http":
         case "https":
         case "ftp":
+          RemoteUriValidator.validate(
+              uri,
+              allowLocalAddressForRemoteUri,
+              String.format(
+                  "'%s' to true, or set 
'kerberos.keytab-fetch-allow-local-address' to true for "
+                      + "Hive catalog properties",
+                  KerberosConfig.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY));
           FileUtils.copyURLToFile(uri.toURL(), destFile, timeout * 1000, 
timeout * 1000);
           break;
 
diff --git 
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/KerberosClient.java
 
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/KerberosClient.java
index cb115048e4..ca90d4cdf8 100644
--- 
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/KerberosClient.java
+++ 
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/KerberosClient.java
@@ -173,7 +173,12 @@ public class KerberosClient implements java.io.Closeable {
     }
     File keytabFile = new File(path);
     int fetchKeytabFileTimeout = kerberosConfig.getFetchTimeoutSec();
-    FetchFileUtils.fetchFileFromUri(keyTabUri, keytabFile, 
fetchKeytabFileTimeout, hadoopConf);
+    FetchFileUtils.fetchFileFromUri(
+        keyTabUri,
+        keytabFile,
+        fetchKeytabFileTimeout,
+        hadoopConf,
+        kerberosConfig.allowKeytabFetchLocalAddress());
     return keytabFile;
   }
 
diff --git 
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/KerberosConfig.java
 
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/KerberosConfig.java
index 4495e0d7fc..f1567534b5 100644
--- 
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/KerberosConfig.java
+++ 
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/KerberosConfig.java
@@ -38,6 +38,9 @@ public class KerberosConfig extends AuthenticationConfig {
   public static final String FETCH_TIMEOUT_SEC_KEY =
       "authentication.kerberos.keytab-fetch-timeout-sec";
 
+  public static final String KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY =
+      "authentication.kerberos.keytab-fetch-allow-local-address";
+
   public static final ConfigEntry<String> PRINCIPAL_ENTRY =
       new ConfigBuilder(PRINCIPAL_KEY)
           .doc("The principal of the Kerberos connection")
@@ -70,6 +73,17 @@ public class KerberosConfig extends AuthenticationConfig {
           .checkValue(value -> value > 0, 
ConfigConstants.POSITIVE_NUMBER_ERROR_MSG)
           .createWithDefault(60);
 
+  public static final ConfigEntry<Boolean> 
KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_ENTRY =
+      new ConfigBuilder(KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY)
+          .doc(
+              "Whether to allow the Kerberos keytab URI to resolve to local, 
private, link-local, "
+                  + "or cloud metadata addresses from the Gravitino server 
side. This is disabled "
+                  + "by default to prevent SSRF. Set it to true only when the 
URI is trusted and "
+                  + "must be fetched from local or private addresses.")
+          .version(ConfigConstants.VERSION_1_3_0)
+          .booleanConf()
+          .createWithDefault(false);
+
   public KerberosConfig(Properties properties, Configuration configuration) {
     super(properties, configuration);
     loadFromMap((Map) properties, k -> true);
@@ -90,4 +104,8 @@ public class KerberosConfig extends AuthenticationConfig {
   public int getFetchTimeoutSec() {
     return get(FETCH_TIMEOUT_SEC_ENTRY);
   }
+
+  public boolean allowKeytabFetchLocalAddress() {
+    return get(KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_ENTRY);
+  }
 }
diff --git 
a/catalogs/hive-metastore-common/src/test/java/org/apache/gravitino/hive/kerberos/TestFetchFileUtils.java
 
b/catalogs/hive-metastore-common/src/test/java/org/apache/gravitino/hive/kerberos/TestFetchFileUtils.java
index c9a4bbec28..eb5900b23a 100644
--- 
a/catalogs/hive-metastore-common/src/test/java/org/apache/gravitino/hive/kerberos/TestFetchFileUtils.java
+++ 
b/catalogs/hive-metastore-common/src/test/java/org/apache/gravitino/hive/kerberos/TestFetchFileUtils.java
@@ -18,7 +18,11 @@
  */
 package org.apache.gravitino.hive.kerberos;
 
+import com.sun.net.httpserver.HttpServer;
 import java.io.File;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.List;
@@ -116,4 +120,56 @@ public class TestFetchFileUtils {
     FetchFileUtils.fetchFileFromUri(srcFileB.toURI().toString(), destFile, 10, 
conf);
     Assertions.assertEquals(srcFileB.toPath(), 
Files.readSymbolicLink(destFile.toPath()));
   }
+
+  @Test
+  public void testRemoteFetchShouldBlockLocalhostByDefault() {
+    File destFile = new File(tempDir, "blocked");
+
+    IllegalArgumentException exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () ->
+                FetchFileUtils.fetchFileFromUri(
+                    "http://127.0.0.1:8090/keytab";, destFile, 10, new 
Configuration()));
+
+    Assertions.assertTrue(exception.getMessage().contains("Gravitino server 
side"));
+    Assertions.assertTrue(
+        
exception.getMessage().contains(KerberosConfig.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY));
+  }
+
+  @Test
+  public void testRemoteFetchShouldAllowLocalhostWhenConfigured() throws 
Exception {
+    File destFile = new File(tempDir, "allowed");
+    HttpServer server = createLoopbackHttpServer("keytab");
+
+    try {
+      server.start();
+      int port = server.getAddress().getPort();
+
+      FetchFileUtils.fetchFileFromUri(
+          String.format("http://127.0.0.1:%d/keytab";, port),
+          destFile,
+          10,
+          new Configuration(),
+          true);
+
+      Assertions.assertEquals("keytab", Files.readString(destFile.toPath()));
+    } finally {
+      server.stop(0);
+    }
+  }
+
+  private HttpServer createLoopbackHttpServer(String response) throws 
Exception {
+    HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 
0), 0);
+    server.createContext(
+        "/keytab",
+        exchange -> {
+          byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
+          exchange.sendResponseHeaders(200, bytes.length);
+          try (OutputStream outputStream = exchange.getResponseBody()) {
+            outputStream.write(bytes);
+          }
+        });
+    return server;
+  }
 }
diff --git 
a/common/src/main/java/org/apache/gravitino/utils/RemoteUriValidator.java 
b/common/src/main/java/org/apache/gravitino/utils/RemoteUriValidator.java
new file mode 100644
index 0000000000..233720d363
--- /dev/null
+++ b/common/src/main/java/org/apache/gravitino/utils/RemoteUriValidator.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.utils;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URI;
+
+/** Validates remote URI hosts before server-side downloads. */
+public final class RemoteUriValidator {
+
+  private RemoteUriValidator() {}
+
+  /**
+   * Resolves the host in the given URI and rejects local or private addresses 
unless explicitly
+   * allowed.
+   *
+   * @param uri The remote URI to validate.
+   * @param allowLocalAddress Whether local and private addresses are allowed.
+   * @param allowLocalAddressHint The configuration hint that enables local 
and private addresses.
+   * @throws IOException If host resolution fails.
+   * @throws IllegalArgumentException If the URI has no host or resolves to a 
blocked address.
+   */
+  public static void validate(URI uri, boolean allowLocalAddress, String 
allowLocalAddressHint)
+      throws IOException {
+    String host = uri.getHost();
+    if (host == null) {
+      throw new IllegalArgumentException("URI has no host: " + uri);
+    }
+
+    if (allowLocalAddress) {
+      return;
+    }
+
+    InetAddress[] addresses = InetAddress.getAllByName(host);
+    for (InetAddress address : addresses) {
+      if (isBlockedAddress(address)) {
+        throw new IllegalArgumentException(
+            String.format(
+                "URI '%s' resolves to blocked address %s from the Gravitino 
server side. "
+                    + "Access to local, private, link-local, multicast, 
unspecified, and cloud "
+                    + "metadata addresses is disabled by default to prevent 
SSRF. If this URI is "
+                    + "trusted and this access is required, set %s.",
+                uri, address.getHostAddress(), allowLocalAddressHint));
+      }
+    }
+  }
+
+  private static boolean isBlockedAddress(InetAddress address) {
+    if (address.isLoopbackAddress()
+        || address.isLinkLocalAddress()
+        || address.isSiteLocalAddress()
+        || address.isMulticastAddress()
+        || address.isAnyLocalAddress()) {
+      return true;
+    }
+
+    byte[] bytes = address.getAddress();
+    if (isCloudMetadataAddress(bytes)) {
+      return true;
+    }
+
+    return isIpv6UniqueLocalAddress(bytes);
+  }
+
+  private static boolean isCloudMetadataAddress(byte[] bytes) {
+    return bytes.length == 4
+        && (bytes[0] & 0xFF) == 100
+        && (bytes[1] & 0xFF) == 100
+        && (bytes[2] & 0xFF) == 100
+        && (bytes[3] & 0xFF) == 200;
+  }
+
+  private static boolean isIpv6UniqueLocalAddress(byte[] bytes) {
+    return bytes.length == 16 && ((bytes[0] & 0xFE) == 0xFC);
+  }
+}
diff --git 
a/common/src/test/java/org/apache/gravitino/utils/TestRemoteUriValidator.java 
b/common/src/test/java/org/apache/gravitino/utils/TestRemoteUriValidator.java
new file mode 100644
index 0000000000..5a250aed6b
--- /dev/null
+++ 
b/common/src/test/java/org/apache/gravitino/utils/TestRemoteUriValidator.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.utils;
+
+import java.net.URI;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestRemoteUriValidator {
+  private static final String ALLOW_LOCAL_ADDRESS_CONFIG = 
"test.allow-local-address";
+
+  @Test
+  public void testRejectLocalAddressesByDefault() {
+    IllegalArgumentException exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () ->
+                RemoteUriValidator.validate(
+                    new URI("http://127.0.0.1/";), false, 
ALLOW_LOCAL_ADDRESS_CONFIG));
+    Assertions.assertTrue(exception.getMessage().contains("Gravitino server 
side"));
+    
Assertions.assertTrue(exception.getMessage().contains(ALLOW_LOCAL_ADDRESS_CONFIG));
+
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            RemoteUriValidator.validate(
+                new URI("http://localhost/";), false, 
ALLOW_LOCAL_ADDRESS_CONFIG));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            RemoteUriValidator.validate(
+                new URI("http://169.254.169.254/";), false, 
ALLOW_LOCAL_ADDRESS_CONFIG));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            RemoteUriValidator.validate(
+                new URI("http://10.0.0.1/";), false, 
ALLOW_LOCAL_ADDRESS_CONFIG));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            RemoteUriValidator.validate(
+                new URI("http://172.16.0.1/";), false, 
ALLOW_LOCAL_ADDRESS_CONFIG));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            RemoteUriValidator.validate(
+                new URI("http://192.168.0.1/";), false, 
ALLOW_LOCAL_ADDRESS_CONFIG));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            RemoteUriValidator.validate(
+                new URI("http://100.100.100.200/";), false, 
ALLOW_LOCAL_ADDRESS_CONFIG));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            RemoteUriValidator.validate(
+                new URI("http://[fd00::1]/";), false, 
ALLOW_LOCAL_ADDRESS_CONFIG));
+  }
+
+  @Test
+  public void testAllowLocalAddressesWhenConfigured() {
+    Assertions.assertDoesNotThrow(
+        () ->
+            RemoteUriValidator.validate(
+                new URI("http://127.0.0.1/";), true, 
ALLOW_LOCAL_ADDRESS_CONFIG));
+    Assertions.assertDoesNotThrow(
+        () ->
+            RemoteUriValidator.validate(
+                new URI("http://localhost/";), true, 
ALLOW_LOCAL_ADDRESS_CONFIG));
+    Assertions.assertDoesNotThrow(
+        () ->
+            RemoteUriValidator.validate(
+                new URI("http://192.168.0.1/";), true, 
ALLOW_LOCAL_ADDRESS_CONFIG));
+  }
+}
diff --git a/core/src/main/java/org/apache/gravitino/Configs.java 
b/core/src/main/java/org/apache/gravitino/Configs.java
index 516c2c9106..bd3a7578cf 100644
--- a/core/src/main/java/org/apache/gravitino/Configs.java
+++ b/core/src/main/java/org/apache/gravitino/Configs.java
@@ -523,6 +523,20 @@ public class Configs {
           .checkValue(value -> value > 0, 
ConfigConstants.POSITIVE_NUMBER_ERROR_MSG)
           .createWithDefault(5 * 60 * 1000L); // Default is 5 minutes
 
+  public static final String JOB_REMOTE_URI_ALLOW_LOCAL_ADDRESS_KEY =
+      "gravitino.job.remoteUri.allowLocalAddress";
+
+  public static final ConfigEntry<Boolean> JOB_REMOTE_URI_ALLOW_LOCAL_ADDRESS =
+      new ConfigBuilder(JOB_REMOTE_URI_ALLOW_LOCAL_ADDRESS_KEY)
+          .doc(
+              "Whether to allow job file remote URIs to resolve to local, 
private, link-local, "
+                  + "or cloud metadata addresses from the Gravitino server 
side. This is disabled "
+                  + "by default to prevent SSRF. Set it to true only when the 
URI is trusted and "
+                  + "must be fetched from local or private addresses.")
+          .version(ConfigConstants.VERSION_1_3_0)
+          .booleanConf()
+          .createWithDefault(false);
+
   public static final ConfigEntry<String> SCHEMA_SEPARATOR =
       new ConfigBuilder("gravitino.schema.separator")
           .doc(
diff --git a/core/src/main/java/org/apache/gravitino/job/JobManager.java 
b/core/src/main/java/org/apache/gravitino/job/JobManager.java
index 2be112fd25..7c7a846073 100644
--- a/core/src/main/java/org/apache/gravitino/job/JobManager.java
+++ b/core/src/main/java/org/apache/gravitino/job/JobManager.java
@@ -25,7 +25,6 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import java.io.File;
 import java.io.IOException;
-import java.net.InetAddress;
 import java.net.URI;
 import java.nio.file.Files;
 import java.time.Instant;
@@ -65,6 +64,7 @@ import org.apache.gravitino.storage.IdGenerator;
 import org.apache.gravitino.utils.NameIdentifierUtil;
 import org.apache.gravitino.utils.NamespaceUtil;
 import org.apache.gravitino.utils.PrincipalUtils;
+import org.apache.gravitino.utils.RemoteUriValidator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -99,6 +99,8 @@ public class JobManager implements JobOperationDispatcher {
 
   private final long jobStagingDirKeepTimeInMs;
 
+  private final boolean allowLocalAddressForRemoteUri;
+
   @VisibleForTesting final ScheduledExecutorService cleanUpExecutor;
 
   @VisibleForTesting final ScheduledExecutorService statusPullExecutor;
@@ -134,6 +136,7 @@ public class JobManager implements JobOperationDispatcher {
     }
 
     this.jobStagingDirKeepTimeInMs = 
config.get(Configs.JOB_STAGING_DIR_KEEP_TIME_IN_MS);
+    this.allowLocalAddressForRemoteUri = 
config.get(Configs.JOB_REMOTE_URI_ALLOW_LOCAL_ADDRESS);
     if (jobStagingDirKeepTimeInMs < JOB_STAGING_DIR_CLEANUP_MIN_TIME_IN_MS) {
       LOG.warn(
           "The job staging directory keep time is set to {} ms, the number is 
too small, "
@@ -436,7 +439,9 @@ public class JobManager implements JobOperationDispatcher {
 
     // Create a JobTemplate by replacing the template parameters with the 
jobConf values, and
     // also downloading any necessary files from the URIs specified in the job 
template.
-    JobTemplate jobTemplate = createRuntimeJobTemplate(jobTemplateEntity, 
jobConf, jobStagingDir);
+    JobTemplate jobTemplate =
+        createRuntimeJobTemplate(
+            jobTemplateEntity, jobConf, jobStagingDir, 
allowLocalAddressForRemoteUri);
 
     // Submit the job template to the job executor
     String jobExecutionId;
@@ -670,13 +675,26 @@ public class JobManager implements JobOperationDispatcher 
{
   @VisibleForTesting
   public static JobTemplate createRuntimeJobTemplate(
       JobTemplateEntity jobTemplateEntity, Map<String, String> jobConf, File 
stagingDir) {
+    return createRuntimeJobTemplate(
+        jobTemplateEntity, jobConf, stagingDir, false /* 
allowLocalAddressForRemoteUri */);
+  }
+
+  @VisibleForTesting
+  static JobTemplate createRuntimeJobTemplate(
+      JobTemplateEntity jobTemplateEntity,
+      Map<String, String> jobConf,
+      File stagingDir,
+      boolean allowLocalAddressForRemoteUri) {
     String name = jobTemplateEntity.name();
     String comment = jobTemplateEntity.comment();
 
     JobTemplateEntity.TemplateContent content = 
jobTemplateEntity.templateContent();
     String executable =
         fetchFileFromUri(
-            replacePlaceholder(content.executable(), jobConf), stagingDir, 
TIMEOUT_IN_MS);
+            replacePlaceholder(content.executable(), jobConf),
+            stagingDir,
+            TIMEOUT_IN_MS,
+            allowLocalAddressForRemoteUri);
 
     List<String> args =
         content.arguments().stream()
@@ -702,7 +720,10 @@ public class JobManager implements JobOperationDispatcher {
               .map(
                   script ->
                       fetchFileFromUri(
-                          replacePlaceholder(script, jobConf), stagingDir, 
TIMEOUT_IN_MS))
+                          replacePlaceholder(script, jobConf),
+                          stagingDir,
+                          TIMEOUT_IN_MS,
+                          allowLocalAddressForRemoteUri))
               .collect(Collectors.toList());
 
       return ShellJobTemplate.builder()
@@ -723,7 +744,11 @@ public class JobManager implements JobOperationDispatcher {
           content.jars().stream()
               .map(
                   jar ->
-                      fetchFileFromUri(replacePlaceholder(jar, jobConf), 
stagingDir, TIMEOUT_IN_MS))
+                      fetchFileFromUri(
+                          replacePlaceholder(jar, jobConf),
+                          stagingDir,
+                          TIMEOUT_IN_MS,
+                          allowLocalAddressForRemoteUri))
               .collect(Collectors.toList());
 
       List<String> files =
@@ -731,7 +756,10 @@ public class JobManager implements JobOperationDispatcher {
               .map(
                   file ->
                       fetchFileFromUri(
-                          replacePlaceholder(file, jobConf), stagingDir, 
TIMEOUT_IN_MS))
+                          replacePlaceholder(file, jobConf),
+                          stagingDir,
+                          TIMEOUT_IN_MS,
+                          allowLocalAddressForRemoteUri))
               .collect(Collectors.toList());
 
       List<String> archives =
@@ -739,7 +767,10 @@ public class JobManager implements JobOperationDispatcher {
               .map(
                   archive ->
                       fetchFileFromUri(
-                          replacePlaceholder(archive, jobConf), stagingDir, 
TIMEOUT_IN_MS))
+                          replacePlaceholder(archive, jobConf),
+                          stagingDir,
+                          TIMEOUT_IN_MS,
+                          allowLocalAddressForRemoteUri))
               .collect(Collectors.toList());
 
       Map<String, String> configs =
@@ -793,13 +824,27 @@ public class JobManager implements JobOperationDispatcher 
{
 
   @VisibleForTesting
   static List<String> fetchFilesFromUri(List<String> uris, File stagingDir, 
int timeoutInMs) {
+    return fetchFilesFromUri(
+        uris, stagingDir, timeoutInMs, false /* allowLocalAddressForRemoteUri 
*/);
+  }
+
+  @VisibleForTesting
+  static List<String> fetchFilesFromUri(
+      List<String> uris, File stagingDir, int timeoutInMs, boolean 
allowLocalAddressForRemoteUri) {
     return uris.stream()
-        .map(uri -> fetchFileFromUri(uri, stagingDir, timeoutInMs))
+        .map(uri -> fetchFileFromUri(uri, stagingDir, timeoutInMs, 
allowLocalAddressForRemoteUri))
         .collect(Collectors.toList());
   }
 
   @VisibleForTesting
   static String fetchFileFromUri(String uri, File stagingDir, int timeoutInMs) 
{
+    return fetchFileFromUri(
+        uri, stagingDir, timeoutInMs, false /* allowLocalAddressForRemoteUri 
*/);
+  }
+
+  @VisibleForTesting
+  static String fetchFileFromUri(
+      String uri, File stagingDir, int timeoutInMs, boolean 
allowLocalAddressForRemoteUri) {
     try {
       URI fileUri = new URI(uri);
       String scheme = Optional.ofNullable(fileUri.getScheme()).orElse("file");
@@ -809,7 +854,10 @@ public class JobManager implements JobOperationDispatcher {
         case "http":
         case "https":
         case "ftp":
-          validateRemoteUri(fileUri);
+          RemoteUriValidator.validate(
+              fileUri,
+              allowLocalAddressForRemoteUri,
+              String.format("'%s' to true", 
Configs.JOB_REMOTE_URI_ALLOW_LOCAL_ADDRESS_KEY));
           FileUtils.copyURLToFile(fileUri.toURL(), destFile, timeoutInMs, 
timeoutInMs);
           break;
 
@@ -832,57 +880,6 @@ public class JobManager implements JobOperationDispatcher {
     }
   }
 
-  /**
-   * Resolves the host in the given URI and rejects addresses that should not 
be reachable from the
-   * server (loopback, link-local, RFC-1918 private ranges, IPv6 ULA, cloud 
metadata endpoints).
-   * This is a defence-in-depth measure against Server-Side Request Forgery 
(SSRF).
-   *
-   * <p><b>Note on DNS TOCTOU:</b> This method resolves the hostname once at 
validation time. {@code
-   * FileUtils.copyURLToFile()} will re-resolve it when opening the 
connection, so a precisely-timed
-   * DNS-rebinding attack could theoretically bypass this check. Complete 
protection against DNS
-   * rebinding requires network-level egress controls (e.g. firewall rules) in 
addition to this
-   * application-layer validation.
-   */
-  @VisibleForTesting
-  static void validateRemoteUri(URI uri) throws IOException {
-    String host = uri.getHost();
-    if (host == null) {
-      throw new IllegalArgumentException("URI has no host: " + uri);
-    }
-    InetAddress[] addresses = InetAddress.getAllByName(host);
-    for (InetAddress address : addresses) {
-      if (isBlockedAddress(address)) {
-        throw new IllegalArgumentException(
-            String.format(
-                "URI '%s' resolves to blocked address %s, access denied (SSRF 
prevention)",
-                uri, address.getHostAddress()));
-      }
-    }
-  }
-
-  private static boolean isBlockedAddress(InetAddress address) {
-    // Covers loopback (127.x.x.x / ::1), link-local (169.254.x.x / fe80::/10 
— includes AWS/GCP/
-    // Azure metadata), RFC-1918 private (10.x / 172.16-31.x / 192.168.x), 
multicast, unspecified.
-    if (address.isLoopbackAddress()
-        || address.isLinkLocalAddress()
-        || address.isSiteLocalAddress()
-        || address.isMulticastAddress()
-        || address.isAnyLocalAddress()) {
-      return true;
-    }
-    byte[] b = address.getAddress();
-    // Alibaba Cloud / Oracle Cloud metadata endpoint: 100.100.100.200
-    if (b.length == 4
-        && (b[0] & 0xFF) == 100
-        && (b[1] & 0xFF) == 100
-        && (b[2] & 0xFF) == 100
-        && (b[3] & 0xFF) == 200) {
-      return true;
-    }
-    // IPv6 Unique Local Addresses (RFC 4193): fc00::/7 — not covered by 
isSiteLocalAddress()
-    return b.length == 16 && ((b[0] & 0xFE) == 0xFC);
-  }
-
   @VisibleForTesting
   JobTemplateEntity updateJobTemplateEntity(
       NameIdentifier jobTemplateIdent,
diff --git a/core/src/test/java/org/apache/gravitino/job/TestJobManager.java 
b/core/src/test/java/org/apache/gravitino/job/TestJobManager.java
index 692d70d010..7e8d693f93 100644
--- a/core/src/test/java/org/apache/gravitino/job/TestJobManager.java
+++ b/core/src/test/java/org/apache/gravitino/job/TestJobManager.java
@@ -31,9 +31,13 @@ import static org.mockito.Mockito.when;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
+import com.sun.net.httpserver.HttpServer;
 import java.io.File;
 import java.io.IOException;
-import java.net.URI;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.time.Instant;
 import java.util.Collections;
@@ -902,6 +906,20 @@ public class TestJobManager {
         .build();
   }
 
+  private HttpServer createLoopbackHttpServer(String response) throws 
IOException {
+    HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 
0), 0);
+    server.createContext(
+        "/artifact.jar",
+        exchange -> {
+          byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
+          exchange.sendResponseHeaders(200, bytes.length);
+          try (OutputStream outputStream = exchange.getResponseBody()) {
+            outputStream.write(bytes);
+          }
+        });
+    return server;
+  }
+
   @Test
   public void testFetchFileFromUriWithMissingLocalFileShouldFail() throws 
IOException {
     File stagingDir = new File(testStagingDir);
@@ -925,7 +943,7 @@ public class TestJobManager {
         Assertions.assertThrows(
             RuntimeException.class,
             () -> JobManager.fetchFileFromUri("http://127.0.0.1:8090/configs";, 
stagingDir, 1000));
-    Assertions.assertTrue(e1.getCause().getMessage().contains("SSRF 
prevention"));
+    assertRemoteUriBlockedMessage(e1);
 
     // AWS / GCP / Azure cloud-metadata endpoint (link-local 169.254.x.x)
     RuntimeException e2 =
@@ -934,64 +952,47 @@ public class TestJobManager {
             () ->
                 JobManager.fetchFileFromUri(
                     "http://169.254.169.254/latest/meta-data/";, stagingDir, 
1000));
-    Assertions.assertTrue(e2.getCause().getMessage().contains("SSRF 
prevention"));
+    assertRemoteUriBlockedMessage(e2);
 
     // RFC-1918 private range
     RuntimeException e3 =
         Assertions.assertThrows(
             RuntimeException.class,
             () -> JobManager.fetchFileFromUri("http://192.168.1.1/";, 
stagingDir, 1000));
-    Assertions.assertTrue(e3.getCause().getMessage().contains("SSRF 
prevention"));
+    assertRemoteUriBlockedMessage(e3);
 
     // Alibaba Cloud / Oracle Cloud metadata endpoint
     RuntimeException e4 =
         Assertions.assertThrows(
             RuntimeException.class,
             () -> JobManager.fetchFileFromUri("http://100.100.100.200/";, 
stagingDir, 1000));
-    Assertions.assertTrue(e4.getCause().getMessage().contains("SSRF 
prevention"));
+    assertRemoteUriBlockedMessage(e4);
   }
 
   @Test
-  public void testValidateRemoteUri() throws Exception {
-    // Loopback
-    Assertions.assertThrows(
-        IllegalArgumentException.class,
-        () -> JobManager.validateRemoteUri(new URI("http://127.0.0.1/";)));
-
-    // Link-local (cloud metadata)
-    Assertions.assertThrows(
-        IllegalArgumentException.class,
-        () -> JobManager.validateRemoteUri(new 
URI("http://169.254.169.254/";)));
-
-    // RFC-1918 private 10.x.x.x
-    Assertions.assertThrows(
-        IllegalArgumentException.class,
-        () -> JobManager.validateRemoteUri(new URI("http://10.0.0.1/";)));
-
-    // RFC-1918 private 172.16.x.x
-    Assertions.assertThrows(
-        IllegalArgumentException.class,
-        () -> JobManager.validateRemoteUri(new URI("http://172.16.0.1/";)));
+  public void testFetchFileFromUriShouldAllowLocalhostWhenConfigured() throws 
Exception {
+    File stagingDir = new File(testStagingDir);
+    Assertions.assertTrue(stagingDir.mkdirs() || stagingDir.exists());
+    HttpServer server = createLoopbackHttpServer("job artifact");
 
-    // RFC-1918 private 192.168.x.x
-    Assertions.assertThrows(
-        IllegalArgumentException.class,
-        () -> JobManager.validateRemoteUri(new URI("http://192.168.0.1/";)));
+    try {
+      server.start();
+      int port = server.getAddress().getPort();
 
-    // Alibaba Cloud metadata
-    Assertions.assertThrows(
-        IllegalArgumentException.class,
-        () -> JobManager.validateRemoteUri(new 
URI("http://100.100.100.200/";)));
+      String fetchedFile =
+          JobManager.fetchFileFromUri(
+              String.format("http://127.0.0.1:%d/artifact.jar";, port), 
stagingDir, 1000, true);
 
-    // localhost resolves to loopback
-    Assertions.assertThrows(
-        IllegalArgumentException.class,
-        () -> JobManager.validateRemoteUri(new URI("http://localhost/";)));
+      Assertions.assertEquals("job artifact", 
Files.readString(Path.of(fetchedFile)));
+    } finally {
+      server.stop(0);
+    }
+  }
 
-    // IPv6 Unique Local Address (RFC 4193): fc00::/7
-    Assertions.assertThrows(
-        IllegalArgumentException.class,
-        () -> JobManager.validateRemoteUri(new URI("http://[fd00::1]/";)));
+  private static void assertRemoteUriBlockedMessage(RuntimeException 
exception) {
+    
Assertions.assertTrue(exception.getCause().getMessage().contains("Gravitino 
server side"));
+    Assertions.assertTrue(
+        
exception.getCause().getMessage().contains(Configs.JOB_REMOTE_URI_ALLOW_LOCAL_ADDRESS_KEY));
   }
 
   @Test
diff --git a/docs/apache-hive-catalog.md b/docs/apache-hive-catalog.md
index f27fec46ab..32f9a633b9 100644
--- a/docs/apache-hive-catalog.md
+++ b/docs/apache-hive-catalog.md
@@ -29,19 +29,20 @@ The Hive catalog supports creating, updating, and deleting 
databases and tables
 
 Besides the [common catalog 
properties](./gravitino-server-config.md#apache-gravitino-catalog-properties-configuration),
 the Hive catalog has the following properties:
 
-| Property Name                            | Description                       
                                                                                
                                                                                
                                                  | Default Value | Required    
                 | Since Version |
-|------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|------------------------------|---------------|
-| `metastore.uris`                         | The Hive metastore service URIs, 
separate multiple addresses with commas. Such as `thrift://127.0.0.1:9083`      
                                                                                
                                                   | (none)        | Yes        
                  | 0.2.0         |
-| `client.pool-size`                       | The maximum number of Hive 
metastore clients in the pool for Gravitino.                                    
                                                                                
                                                         | 1             | No   
                        | 0.2.0         |
-| `gravitino.bypass.`                      | Property name with this prefix 
passed down to the underlying HMS client for use. Such as 
`gravitino.bypass.hive.metastore.failure.retries = 3` indicate 3 times of 
retries upon failure of Thrift metastore calls                                  
 | (none)        | No                           | 0.2.0         |
-| `client.pool-cache.eviction-interval-ms` | The cache pool eviction interval. 
                                                                                
                                                                                
                                                  | 300000        | No          
                 | 0.4.0         |
-| `impersonation-enable`                   | Enable user impersonation for 
Hive catalog.                                                                   
                                                                                
                                                      | false         | No      
                     | 0.4.0         |
-| `kerberos.principal`                     | The Kerberos principal for the 
catalog. You should configure 
`gravitino.bypass.hadoop.security.authentication`, 
`gravitino.bypass.hive.metastore.kerberos.principal` and 
`gravitino.bypass.hive.metastore.sasl.enabled`if you want to use Kerberos. | 
(none)        | required if you use kerberos | 0.4.0         |
-| `kerberos.keytab-uri`                    | The uri of key tab for the 
catalog. Now supported protocols are `https`, `http`, `ftp`, `file`.            
                                                                                
                                                         | (none)        | 
required if you use kerberos | 0.4.0         |
-| `kerberos.check-interval-sec`            | The interval to check validness 
of the principal                                                                
                                                                                
                                                    | 60            | No        
                   | 0.4.0         |
-| `kerberos.keytab-fetch-timeout-sec`      | The timeout to fetch key tab      
                                                                                
                                                                                
                                                  | 60            | No          
                 | 0.4.0         |
-| `list-all-tables`                        | Whether to list all tables in a 
database, including non-Hive tables such as Iceberg, Paimon, and Hudi. When 
false, non-Hive tables are filtered out on a best-effort basis; see the note 
below for known limitations.                            | false         | No    
                       | 0.5.1         |
-| `default.catalog`                        | The default catalog name for the 
Hive3 metastore backend; this configuration is ignored when using a Hive2 
metastore.                                                                      
                                                         | hive          | No   
                        | 1.1.0         |
+| Property Name                               | Description                    
                                                                                
                                                                                
                                                     | Default Value | Required 
                    | Since Version |
+|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|------------------------------|---------------|
+| `metastore.uris`                            | The Hive metastore service 
URIs, separate multiple addresses with commas. Such as 
`thrift://127.0.0.1:9083`                                                       
                                                                                
  | (none)        | Yes                          | 0.2.0         |
+| `client.pool-size`                          | The maximum number of Hive 
metastore clients in the pool for Gravitino.                                    
                                                                                
                                                         | 1             | No   
                        | 0.2.0         |
+| `gravitino.bypass.`                         | Property name with this prefix 
passed down to the underlying HMS client for use. Such as 
`gravitino.bypass.hive.metastore.failure.retries = 3` indicate 3 times of 
retries upon failure of Thrift metastore calls                                  
 | (none)        | No                           | 0.2.0         |
+| `client.pool-cache.eviction-interval-ms`    | The cache pool eviction 
interval.                                                                       
                                                                                
                                                            | 300000        | 
No                           | 0.4.0         |
+| `impersonation-enable`                      | Enable user impersonation for 
Hive catalog.                                                                   
                                                                                
                                                      | false         | No      
                     | 0.4.0         |
+| `kerberos.principal`                        | The Kerberos principal for the 
catalog. You should configure 
`gravitino.bypass.hadoop.security.authentication`, 
`gravitino.bypass.hive.metastore.kerberos.principal` and 
`gravitino.bypass.hive.metastore.sasl.enabled`if you want to use Kerberos. | 
(none)        | required if you use kerberos | 0.4.0         |
+| `kerberos.keytab-uri`                       | The uri of key tab for the 
catalog. Now supported protocols are `https`, `http`, `ftp`, `file`.            
                                                                                
                                                         | (none)        | 
required if you use kerberos | 0.4.0         |
+| `kerberos.check-interval-sec`               | The interval to check 
validness of the principal                                                      
                                                                                
                                                              | 60            | 
No                           | 0.4.0         |
+| `kerberos.keytab-fetch-timeout-sec`         | The timeout to fetch key tab   
                                                                                
                                                                                
                                                     | 60            | No       
                    | 0.4.0         |
+| `kerberos.keytab-fetch-allow-local-address` | Whether to allow the Kerberos 
keytab URI to resolve to local, private, link-local, or cloud metadata 
addresses from the Gravitino server side. Enable this only for trusted URIs 
that must be fetched from local or private addresses.            | false        
 | No                           | 1.3.0         |
+| `list-all-tables`                           | Whether to list all tables in 
a database, including non-Hive tables such as Iceberg, Paimon, and Hudi. When 
false, non-Hive tables are filtered out on a best-effort basis; see the note 
below for known limitations.                            | false         | No    
                       | 0.5.1         |
+| `default.catalog`                           | The default catalog name for 
the Hive3 metastore backend; this configuration is ignored when using a Hive2 
metastore.                                                                      
                                                         | hive          | No   
                        | 1.1.0         |
 
 :::note
 When `list-all-tables=false`, the Hive catalog removes the following on a 
best-effort basis:
@@ -248,4 +249,3 @@ Refer to [Manage view metadata using 
Gravitino](./manage-view-metadata-using-gra
 
 To create a Hive catalog with S3 storage, you can refer to the [Hive catalog 
with S3](./hive-catalog-with-s3.md) documentation. No special configurations 
are required for the Hive catalog to work with S3 storage.
 The only difference is the storage location of the files, which is in S3. You 
can use `location` to specify the S3 path for the database or table.
-
diff --git a/docs/fileset-catalog.md b/docs/fileset-catalog.md
index fbc5113239..b63733602b 100644
--- a/docs/fileset-catalog.md
+++ b/docs/fileset-catalog.md
@@ -53,14 +53,15 @@ Please refer to [Credential 
vending](./security/credential-vending.md) for more
 Apart from the above properties, to access fileset like HDFS fileset, you need 
to configure the following extra
 properties.
 
-| Property Name                                      | Description             
                                                                        | 
Default Value | Required                                                    | 
Since Version |
-|----------------------------------------------------|-------------------------------------------------------------------------------------------------|---------------|-------------------------------------------------------------|---------------|
-| `authentication.impersonation-enable`              | Whether to enable 
impersonation for the Fileset catalog.                                        | 
`false`       | No                                                          | 
0.5.1         |
-| `authentication.type`                              | The type of 
authentication for Fileset catalog, currently we only support `kerberos`, 
`simple`. | `simple`      | No                                                  
        | 0.5.1         |
-| `authentication.kerberos.principal`                | The principal of the 
Kerberos authentication                                                    | 
(none)        | required if the value of `authentication.type` is Kerberos. | 
0.5.1         |
-| `authentication.kerberos.keytab-uri`               | The URI of The keytab 
for the Kerberos authentication.                                          | 
(none)        | required if the value of `authentication.type` is Kerberos. | 
0.5.1         |
-| `authentication.kerberos.check-interval-sec`       | The check interval of 
Kerberos credential for Fileset catalog.                                  | 60  
          | No                                                          | 0.5.1 
        |
-| `authentication.kerberos.keytab-fetch-timeout-sec` | The fetch timeout of 
retrieving Kerberos keytab from `authentication.kerberos.keytab-uri`.      | 60 
           | No                                                          | 
0.5.1         |
+| Property Name                                              | Description     
                                                                                
| Default Value | Required                                                    | 
Since Version |
+|------------------------------------------------------------|-------------------------------------------------------------------------------------------------|---------------|-------------------------------------------------------------|---------------|
+| `authentication.impersonation-enable`                      | Whether to 
enable impersonation for the Fileset catalog.                                   
     | `false`       | No                                                       
   | 0.5.1         |
+| `authentication.type`                                      | The type of 
authentication for Fileset catalog, currently we only support `kerberos`, 
`simple`. | `simple`      | No                                                  
        | 0.5.1         |
+| `authentication.kerberos.principal`                        | The principal 
of the Kerberos authentication                                                  
  | (none)        | required if the value of `authentication.type` is Kerberos. 
| 0.5.1         |
+| `authentication.kerberos.keytab-uri`                       | The URI of The 
keytab for the Kerberos authentication.                                         
 | (none)        | required if the value of `authentication.type` is Kerberos. 
| 0.5.1         |
+| `authentication.kerberos.check-interval-sec`               | The check 
interval of Kerberos credential for Fileset catalog.                            
      | 60            | No                                                      
    | 0.5.1         |
+| `authentication.kerberos.keytab-fetch-timeout-sec`         | The fetch 
timeout of retrieving Kerberos keytab from 
`authentication.kerberos.keytab-uri`.      | 60            | No                 
                                         | 0.5.1         |
+| `authentication.kerberos.keytab-fetch-allow-local-address` | Whether to 
allow the Kerberos keytab URI to resolve to local, private, link-local, or 
cloud metadata addresses from the Gravitino server side. Enable this only for 
trusted URIs that must be fetched from local or private addresses. | false | No 
| 1.3.0 |
 
 The `config.resources` property allows users to specify custom configuration 
files.
 
diff --git a/docs/gravitino-server-config.md b/docs/gravitino-server-config.md
index 2aec2d9183..37617d68d2 100644
--- a/docs/gravitino-server-config.md
+++ b/docs/gravitino-server-config.md
@@ -104,6 +104,18 @@ gravitino.cache.lockSegments=16
 - `gravitino.cache.expireTimeInMs`: Controls the cache TTL in milliseconds.
 - If `gravitino.cache.enableStats` is enabled, Gravitino will log cache 
statistics (hit count, miss count, load failures, etc.) every 5 minutes at the 
Info level.
 
+### Job configuration
+
+The following table lists the job configuration items:
+
+| Configuration item                          | Description                    
                                                                                
                                                                                
                                              | Default value                  
| Required | Since version |
+|---------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|----------|---------------|
+| `gravitino.job.stagingDir`                  | Directory for managing staging 
files when running jobs.                                                        
                                                                                
                                              | `/tmp/gravitino/jobs/staging`  
| No       | 1.0.0         |
+| `gravitino.job.executor`                    | The executor to run jobs. By 
default it is `local`; users can implement their own executor and set it here.  
                                                                                
                                                 | `local`                      
  | No       | 1.0.0         |
+| `gravitino.job.stagingDirKeepTimeInMs`      | The time in milliseconds to 
keep the staging files of the finished job in the job staging directory. The 
minimum recommended value is 10 minutes if you are not testing.                 
                                                     | `604800000` (7 days)     
      | No       | 1.0.0         |
+| `gravitino.job.statusPullIntervalInMs`      | The interval in milliseconds 
to pull the job status from the job executor. The minimum recommended value is 
1 minute if you are not testing.                                                
                                                  | `300000` (5 minutes)        
   | No       | 1.0.0         |
+| `gravitino.job.remoteUri.allowLocalAddress` | Whether to allow job file 
remote URIs to resolve to local, private, link-local, or cloud metadata 
addresses from the Gravitino server side. This is disabled by default to 
prevent server-side request forgery (SSRF). Enable it only for trusted URIs 
when existing jobs must fetch from local or private addresses. | `false`        
                | No       | 1.3.0         |
+
 #### Eviction strategies
 
 Gravitino supports multiple eviction strategies including capacity-based, 
weight-based, and time-based (TTL) eviction. The following describes how they 
work with Caffeine:
diff --git a/docs/iceberg-rest-service.md b/docs/iceberg-rest-service.md
index d25832d6cb..578297a791 100644
--- a/docs/iceberg-rest-service.md
+++ b/docs/iceberg-rest-service.md
@@ -372,15 +372,16 @@ Please refer to [HTTPS 
Configuration](./security/how-to-use-https/#apache-iceber
 For JDBC backend, you can use the `gravitino.iceberg-rest.jdbc-user` and 
`gravitino.iceberg-rest.jdbc-password` to authenticate the JDBC connection. For 
Hive backend, you can use the `gravitino.iceberg-rest.authentication.type` to 
specify the authentication type, and use the 
`gravitino.iceberg-rest.authentication.kerberos.principal` and 
`gravitino.iceberg-rest.authentication.kerberos.keytab-uri` to authenticate the 
Kerberos connection.
 The detailed configuration items are as follows:
 
-| Configuration item                                                        | 
Description                                                                     
                                                                                
                                                                                
       | Default value | Required                                               
                                                                                
               [...]
-|---------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------
 [...]
-| `gravitino.iceberg-rest.authentication.type`                              | 
The type of authentication for Iceberg rest catalog backend. This configuration 
only applicable for for Hive backend, and only supports `Kerberos`, `simple` 
currently. As for JDBC backend, only username/password authentication was 
supported now.  | `simple`      | No                                            
                                                                                
                        [...]
-| `gravitino.iceberg-rest.authentication.impersonation-enable`              | 
Whether to enable impersonation for the Iceberg catalog                         
                                                                                
                                                                                
       | `false`       | No                                                     
                                                                                
               [...]
-| `gravitino.iceberg-rest.hive.metastore.sasl.enabled`                      | 
Whether to enable SASL authentication protocol when connect to Kerberos Hive 
metastore.                                                                      
                                                                                
          | `false`       | No, This value should be true in most case(Some 
will use SSL protocol, but it rather rare) if the value of 
`gravitino.iceberg-rest.authentication.typ [...]
-| `gravitino.iceberg-rest.authentication.kerberos.principal`                | 
The principal of the Kerberos authentication                                    
                                                                                
                                                                                
       | (none)        | required if the value of 
`gravitino.iceberg-rest.authentication.type` is Kerberos.                       
                                             [...]
-| `gravitino.iceberg-rest.authentication.kerberos.keytab-uri`               | 
The URI of The keytab for the Kerberos authentication.                          
                                                                                
                                                                                
       | (none)        | required if the value of 
`gravitino.iceberg-rest.authentication.type` is Kerberos.                       
                                             [...]
-| `gravitino.iceberg-rest.authentication.kerberos.check-interval-sec`       | 
The check interval of Kerberos credential for Iceberg catalog.                  
                                                                                
                                                                                
       | 60            | No                                                     
                                                                                
               [...]
-| `gravitino.iceberg-rest.authentication.kerberos.keytab-fetch-timeout-sec` | 
The fetch timeout of retrieving Kerberos keytab from 
`authentication.kerberos.keytab-uri`.                                           
                                                                                
                                  | 60            | No                          
                                                                                
                                          [...]
+| Configuration item                                                           
     | Description                                                              
                                                                                
                                                                                
              | Default value | Required                                        
                                                                                
              [...]
+|-----------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------------
 [...]
+| `gravitino.iceberg-rest.authentication.type`                                 
     | The type of authentication for Iceberg rest catalog backend. This 
configuration only applicable for for Hive backend, and only supports 
`Kerberos`, `simple` currently. As for JDBC backend, only username/password 
authentication was supported now.  | `simple`      | No                         
                                                                                
                                   [...]
+| `gravitino.iceberg-rest.authentication.impersonation-enable`                 
     | Whether to enable impersonation for the Iceberg catalog                  
                                                                                
                                                                                
              | `false`       | No                                              
                                                                                
              [...]
+| `gravitino.iceberg-rest.hive.metastore.sasl.enabled`                         
     | Whether to enable SASL authentication protocol when connect to Kerberos 
Hive metastore.                                                                 
                                                                                
               | `false`       | No, This value should be true in most 
case(Some will use SSL protocol, but it rather rare) if the value of 
`gravitino.iceberg-rest.authentica [...]
+| `gravitino.iceberg-rest.authentication.kerberos.principal`                   
     | The principal of the Kerberos authentication                             
                                                                                
                                                                                
              | (none)        | required if the value of 
`gravitino.iceberg-rest.authentication.type` is Kerberos.                       
                                     [...]
+| `gravitino.iceberg-rest.authentication.kerberos.keytab-uri`                  
     | The URI of The keytab for the Kerberos authentication.                   
                                                                                
                                                                                
              | (none)        | required if the value of 
`gravitino.iceberg-rest.authentication.type` is Kerberos.                       
                                     [...]
+| `gravitino.iceberg-rest.authentication.kerberos.check-interval-sec`          
     | The check interval of Kerberos credential for Iceberg catalog.           
                                                                                
                                                                                
              | 60            | No                                              
                                                                                
              [...]
+| `gravitino.iceberg-rest.authentication.kerberos.keytab-fetch-timeout-sec`    
     | The fetch timeout of retrieving Kerberos keytab from 
`authentication.kerberos.keytab-uri`.                                           
                                                                                
                                  | 60            | No                          
                                                                                
                                  [...]
+| 
`gravitino.iceberg-rest.authentication.kerberos.keytab-fetch-allow-local-address`
 | Whether to allow the Kerberos keytab URI to resolve to local, private, 
link-local, or cloud metadata addresses from the Gravitino Iceberg REST server 
side. Enable this only for trusted URIs that must be fetched from local or 
private addresses. | false         | No                                         
                                                                                
                      [...]
 
 ### Credential vending
 
diff --git a/docs/lakehouse-hudi-catalog.md b/docs/lakehouse-hudi-catalog.md
index 1bf6c59116..a1c3b79c2a 100644
--- a/docs/lakehouse-hudi-catalog.md
+++ b/docs/lakehouse-hudi-catalog.md
@@ -44,14 +44,15 @@ Tested and verified with Apache Hudi `0.15.0`.
 
 Users can use the following properties to configure the security of the 
catalog backend if needed. For example, if you are using a Kerberos Hive 
catalog backend, you must set `authentication.type` to `Kerberos` and provide 
`authentication.kerberos.principal` and `authentication.kerberos.keytab-uri`.
 
-| Property name                                      | Description             
                                                                                
                                                       | Default value | 
Required                                                    | Since Version     
|
-|----------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|-------------------------------------------------------------|-------------------|
-| `authentication.type`                              | The type of 
authentication for hudi catalog backend. This configuration only applicable for 
for hms backend, and only supports `kerberos`, `simple` currently. | `simple`   
   | No                                                          | 1.0.0 |
-| `authentication.impersonation-enable`              | Whether to enable 
impersonation for the hudi catalog                                              
                                                             | `false`       | 
No                                                          | 1.0.0 |
-| `authentication.kerberos.principal`                | The principal of the 
Kerberos authentication                                                         
                                                          | (none)        | 
required if the value of `authentication.type` is kerberos. | 1.0.0 |
-| `authentication.kerberos.keytab-uri`               | The URI of The keytab 
for the Kerberos authentication.                                                
                                                         | (none)        | 
required if the value of `authentication.type` is kerberos. | 1.0.0 |
-| `authentication.kerberos.check-interval-sec`       | The check interval of 
Kerberos credential for hudi catalog.                                           
                                                         | 60            | No   
                                                       | 1.0.0 |
-| `authentication.kerberos.keytab-fetch-timeout-sec` | The fetch timeout of 
retrieving Kerberos keytab from `authentication.kerberos.keytab-uri`.           
                                                          | 60            | No  
                                                        | 1.0.0 |
+| Property name                                              | Description     
                                                                                
                                                               | Default value 
| Required                                                    | Since Version   
  |
+|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|-------------------------------------------------------------|-------------------|
+| `authentication.type`                                      | The type of 
authentication for hudi catalog backend. This configuration only applicable for 
for hms backend, and only supports `kerberos`, `simple` currently. | `simple`   
   | No                                                          | 1.0.0 |
+| `authentication.impersonation-enable`                      | Whether to 
enable impersonation for the hudi catalog                                       
                                                                    | `false`   
    | No                                                          | 1.0.0 |
+| `authentication.kerberos.principal`                        | The principal 
of the Kerberos authentication                                                  
                                                                 | (none)       
 | required if the value of `authentication.type` is kerberos. | 1.0.0 |
+| `authentication.kerberos.keytab-uri`                       | The URI of The 
keytab for the Kerberos authentication.                                         
                                                                | (none)        
| required if the value of `authentication.type` is kerberos. | 1.0.0 |
+| `authentication.kerberos.check-interval-sec`               | The check 
interval of Kerberos credential for hudi catalog.                               
                                                                     | 60       
     | No                                                          | 1.0.0 |
+| `authentication.kerberos.keytab-fetch-timeout-sec`         | The fetch 
timeout of retrieving Kerberos keytab from 
`authentication.kerberos.keytab-uri`.                                           
                          | 60            | No                                  
                        | 1.0.0 |
+| `authentication.kerberos.keytab-fetch-allow-local-address` | Whether to 
allow the Kerberos keytab URI to resolve to local, private, link-local, or 
cloud metadata addresses from the Gravitino server side. Enable this only for 
trusted URIs that must be fetched from local or private addresses. | false | No 
| 1.3.0 |
 
 Property name with this prefix passed down to the underlying backend client 
for use. Such as 
`gravitino.bypass.hive.metastore.kerberos.principal=XXXX`、`gravitino.bypass.hadoop.security.authentication=kerberos`、`gravitino.bypass.hive.metastore.sasl.enabled=ture`
 And so on.
 
diff --git a/docs/lakehouse-iceberg-catalog.md 
b/docs/lakehouse-iceberg-catalog.md
index 203b055048..0cfe6f9122 100644
--- a/docs/lakehouse-iceberg-catalog.md
+++ b/docs/lakehouse-iceberg-catalog.md
@@ -204,15 +204,16 @@ Please set the `warehouse` parameter to 
`{storage_prefix}://{bucket_name}/${pref
 
 Users can use the following properties to configure the security of the 
catalog backend if needed. For example, if you are using a Kerberos Hive 
catalog backend, you must set `authentication.type` to `Kerberos` and provide 
`authentication.kerberos.principal` and `authentication.kerberos.keytab-uri`.
 
-| Property name                                      | Description             
                                                                                
                                                                                
                                                         | Default value | 
Required                                                                        
                                                                                
     | Since Version    |
-|----------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------|
-| `authentication.type`                              | The type of 
authentication for Iceberg catalog backend. This configuration only applicable 
for for Hive backend, and only supports `Kerberos`, `simple` currently. As for 
JDBC backend, only username/password authentication was supported now. | 
`simple`      | No                                                              
                                                                                
                     | 0.6.0-incubating |
-| `authentication.impersonation-enable`              | Whether to enable 
impersonation for the Iceberg catalog                                           
                                                                                
                                                               | `false`       
| No                                                                            
                                                                                
       | 0.6.0-incubating |
-| `hive.metastore.sasl.enabled`                      | Whether to enable SASL 
authentication protocol when connect to Kerberos Hive metastore. This is a raw 
Hive configuration                                                              
                                                           | `false`       | 
No, This value should be true in most case(Some will use SSL protocol, but it 
rather rare) if the value of `gravitino.iceberg-rest.authentication.type` is 
Kerberos. | 0.6.0-incubating |
-| `authentication.kerberos.principal`                | The principal of the 
Kerberos authentication                                                         
                                                                                
                                                            | (none)        | 
required if the value of `authentication.type` is Kerberos.                     
                                                                                
     | 0.6.0-incubating |
-| `authentication.kerberos.keytab-uri`               | The URI of The keytab 
for the Kerberos authentication.                                                
                                                                                
                                                           | (none)        | 
required if the value of `authentication.type` is Kerberos.                     
                                                                                
     | 0.6.0-incubating |
-| `authentication.kerberos.check-interval-sec`       | The check interval of 
Kerberos credential for Iceberg catalog.                                        
                                                                                
                                                           | 60            | No 
                                                                                
                                                                                
  | 0.6.0-incubating |
-| `authentication.kerberos.keytab-fetch-timeout-sec` | The fetch timeout of 
retrieving Kerberos keytab from `authentication.kerberos.keytab-uri`.           
                                                                                
                                                            | 60            | 
No                                                                              
                                                                                
     | 0.6.0-incubating |
+| Property name                                              | Description     
                                                                                
                                                                                
                                                                 | Default 
value | Required                                                                
                                                                                
             | Sin [...]
+|------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|----
 [...]
+| `authentication.type`                                      | The type of 
authentication for Iceberg catalog backend. This configuration only applicable 
for for Hive backend, and only supports `Kerberos`, `simple` currently. As for 
JDBC backend, only username/password authentication was supported now. | 
`simple`      | No                                                              
                                                                                
                     | 0.6 [...]
+| `authentication.impersonation-enable`                      | Whether to 
enable impersonation for the Iceberg catalog                                    
                                                                                
                                                                      | `false` 
      | No                                                                      
                                                                                
             | 0.6 [...]
+| `hive.metastore.sasl.enabled`                              | Whether to 
enable SASL authentication protocol when connect to Kerberos Hive metastore. 
This is a raw Hive configuration                                                
                                                                         | 
`false`       | No, This value should be true in most case(Some will use SSL 
protocol, but it rather rare) if the value of 
`gravitino.iceberg-rest.authentication.type` is Kerberos. | 0.6 [...]
+| `authentication.kerberos.principal`                        | The principal 
of the Kerberos authentication                                                  
                                                                                
                                                                   | (none)     
   | required if the value of `authentication.type` is Kerberos.                
                                                                                
          | 0.6 [...]
+| `authentication.kerberos.keytab-uri`                       | The URI of The 
keytab for the Kerberos authentication.                                         
                                                                                
                                                                  | (none)      
  | required if the value of `authentication.type` is Kerberos.                 
                                                                                
         | 0.6 [...]
+| `authentication.kerberos.check-interval-sec`               | The check 
interval of Kerberos credential for Iceberg catalog.                            
                                                                                
                                                                       | 60     
       | No                                                                     
                                                                                
              | 0.6 [...]
+| `authentication.kerberos.keytab-fetch-timeout-sec`         | The fetch 
timeout of retrieving Kerberos keytab from 
`authentication.kerberos.keytab-uri`.                                           
                                                                                
                            | 60            | No                                
                                                                                
                                                   | 0.6 [...]
+| `authentication.kerberos.keytab-fetch-allow-local-address` | Whether to 
allow the Kerberos keytab URI to resolve to local, private, link-local, or 
cloud metadata addresses from the Gravitino server side. Enable this only for 
trusted URIs that must be fetched from local or private addresses.              
                                                 | false         | No           
                                                                                
                          [...]
 
 #### Table metadata cache
 
diff --git a/docs/lakehouse-paimon-catalog.md b/docs/lakehouse-paimon-catalog.md
index 3de913e2c5..6000fe0ae7 100644
--- a/docs/lakehouse-paimon-catalog.md
+++ b/docs/lakehouse-paimon-catalog.md
@@ -29,31 +29,32 @@ Builds with Apache Paimon `1.2`.
 
 ### Catalog properties
 
-| Property name                                      | Description             
                                                                                
                                                                                
                    | Default value                                             
                     | Required                                                 
                                                                                
              [...]
-|----------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------
 [...]
-| `catalog-backend`                                  | Catalog backend of 
Gravitino Paimon catalog. Supports `filesystem`, `jdbc`, `hive` and `rest`.     
                                                                                
                         | (none)                                               
                          | Yes                                                 
                                                                                
                   [...]
-| `uri`                                              | The URI configuration 
of the Paimon catalog. `thrift://127.0.0.1:9083` or 
`jdbc:postgresql://127.0.0.1:5432/db_name` or 
`jdbc:mysql://127.0.0.1:3306/metastore_db`. It is optional for 
`FilesystemCatalog`. | (none)                                                   
                      | required if the value of `catalog-backend` is not 
`filesystem`.                                                                   
                     [...]
-| `warehouse`                                        | Warehouse directory of 
catalog. `file:///user/hive/warehouse-paimon/` for local fs, 
`hdfs://namespace/hdfs/path` for HDFS , `s3://{bucket-name}/path/` for S3 or 
`oss://{bucket-name}/path` for Aliyun OSS  | (none)                             
                                            | Yes                               
                                                                                
                                     [...]
-| `catalog-backend-name`                             | The catalog name passed 
to underlying Paimon catalog backend.                                           
                                                                                
                    | The property value of `catalog-backend`, like `jdbc` for 
JDBC catalog backend. | No                                                      
                                                                                
               [...]
-| `authentication.type`                              | The type of 
authentication for Paimon catalog backend, currently Gravitino only supports 
`Kerberos` and `simple`.                                                        
                                   | `simple`                                   
                                    | No                                        
                                                                                
                             [...]
-| `hive.metastore.sasl.enabled`                      | Whether to enable SASL 
authentication protocol when connect to Kerberos Hive metastore. This is a raw 
Hive configuration                                                              
                      | `false`                                                 
                       | No, This value should be true in most case(Some will 
use SSL protocol, but it rather rare) if the value of 
`gravitino.iceberg-rest.authentication.type [...]
-| `authentication.kerberos.principal`                | The principal of the 
Kerberos authentication.                                                        
                                                                                
                       | (none)                                                 
                        | required if the value of `authentication.type` is 
Kerberos.                                                                       
                     [...]
-| `authentication.kerberos.keytab-uri`               | The URI of The keytab 
for the Kerberos authentication.                                                
                                                                                
                      | (none)                                                  
                       | required if the value of `authentication.type` is 
Kerberos.                                                                       
                     [...]
-| `authentication.kerberos.check-interval-sec`       | The check interval of 
Kerberos credential for Paimon catalog.                                         
                                                                                
                      | 60                                                      
                       | No                                                     
                                                                                
                [...]
-| `authentication.kerberos.keytab-fetch-timeout-sec` | The fetch timeout of 
retrieving Kerberos keytab from `authentication.kerberos.keytab-uri`.           
                                                                                
                       | 60                                                     
                        | No                                                    
                                                                                
                 [...]
-| `oss-endpoint`                                     | The endpoint of the 
Aliyun OSS.                                                                     
                                                                                
                        | (none)                                                
                         | required if the value of `warehouse` is a OSS path   
                                                                                
                  [...]
-| `oss-access-key-id`                                | The access key of the 
Aliyun OSS.                                                                     
                                                                                
                      | (none)                                                  
                       | required if the value of `warehouse` is a OSS path     
                                                                                
                [...]
-| `oss-secret-access-key`                            | The secret key the 
Aliyun OSS.                                                                     
                                                                                
                         | (none)                                               
                          | required if the value of `warehouse` is a OSS path  
                                                                                
                   [...]
-| `s3-endpoint`                                      | The endpoint of the AWS 
S3.                                                                             
                                                                                
                    | (none)                                                    
                     | required if the value of `warehouse` is a S3 path        
                                                                                
              [...]
-| `s3-access-key-id`                                 | The access key of the 
AWS S3.                                                                         
                                                                                
                      | (none)                                                  
                       | required if the value of `warehouse` is a S3 path      
                                                                                
                [...]
-| `s3-secret-access-key`                             | The secret key of the 
AWS S3.                                                                         
                                                                                
                      | (none)                                                  
                       | required if the value of `warehouse` is a S3 path      
                                                                                
                [...]
-| `token-provider`                                   | The token provider type 
for Paimon catalog backend.                                                     
                                                                                
                    | Token provider could be `bear` or `dlf`.                  
                     | required if the value of `catalog-backend` is `rest`.    
                                                                                
              [...]
-| `token`                                            | The bear token for 
Paimon REST catalog authentication.                                             
                                                                                
                         | (none)                                               
                          | required if the value of `token-provider` is 
`bear`.                                                                         
                          [...]
-| `dlf-access-key-id`                                | The access key ID for 
Aliyun DLF (Data Lake Formation).                                               
                                                                                
                      | (none)                                                  
                       | required if the value of `catalog-backend` is `rest` 
and accessing Aliyun DLF Paimon REST server.                                    
                  [...]
-| `dlf-access-key-secret`                            | The access key secret 
for Aliyun DLF.                                                                 
                                                                                
                      | (none)                                                  
                       | required if the value of `catalog-backend` is `rest` 
and accessing Aliyun DLF Paimon REST server.                                    
                  [...]
-| `dlf-security-token`                               | The security token for 
Aliyun DLF.                                                                     
                                                                                
                     | (none)                                                   
                      | No                                                      
                                                                                
               [...]
-| `dlf-token-path`                                   | The token path for 
Aliyun DLF.                                                                     
                                                                                
                         | (none)                                               
                          | No                                                  
                                                                                
                   [...]
-| `dlf-token-loader`                                 | The token loader for 
Aliyun DLF.                                                                     
                                                                                
                       | (none)                                                 
                        | No                                                    
                                                                                
                 [...]
+| Property name                                              | Description     
                                                                                
                                                                                
                            | Default value                                     
                             | Required                                         
                                                                                
              [...]
+|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------
 [...]
+| `catalog-backend`                                          | Catalog backend 
of Gravitino Paimon catalog. Supports `filesystem`, `jdbc`, `hive` and `rest`.  
                                                                                
                            | (none)                                            
                             | Yes                                              
                                                                                
              [...]
+| `uri`                                                      | The URI 
configuration of the Paimon catalog. `thrift://127.0.0.1:9083` or 
`jdbc:postgresql://127.0.0.1:5432/db_name` or 
`jdbc:mysql://127.0.0.1:3306/metastore_db`. It is optional for 
`FilesystemCatalog`. | (none)                                                   
                      | required if the value of `catalog-backend` is not 
`filesystem`.                                                                   
             [...]
+| `warehouse`                                                | Warehouse 
directory of catalog. `file:///user/hive/warehouse-paimon/` for local fs, 
`hdfs://namespace/hdfs/path` for HDFS , `s3://{bucket-name}/path/` for S3 or 
`oss://{bucket-name}/path` for Aliyun OSS  | (none)                             
                                            | Yes                               
                                                                                
                             [...]
+| `catalog-backend-name`                                     | The catalog 
name passed to underlying Paimon catalog backend.                               
                                                                                
                                | The property value of `catalog-backend`, like 
`jdbc` for JDBC catalog backend. | No                                           
                                                                                
                  [...]
+| `authentication.type`                                      | The type of 
authentication for Paimon catalog backend, currently Gravitino only supports 
`Kerberos` and `simple`.                                                        
                                   | `simple`                                   
                                    | No                                        
                                                                                
                     [...]
+| `hive.metastore.sasl.enabled`                              | Whether to 
enable SASL authentication protocol when connect to Kerberos Hive metastore. 
This is a raw Hive configuration                                                
                                    | `false`                                   
                                     | No, This value should be true in most 
case(Some will use SSL protocol, but it rather rare) if the value of 
`gravitino.iceberg-rest.authenticat [...]
+| `authentication.kerberos.principal`                        | The principal 
of the Kerberos authentication.                                                 
                                                                                
                              | (none)                                          
                               | required if the value of `authentication.type` 
is Kerberos.                                                                    
                [...]
+| `authentication.kerberos.keytab-uri`                       | The URI of The 
keytab for the Kerberos authentication.                                         
                                                                                
                             | (none)                                           
                              | required if the value of `authentication.type` 
is Kerberos.                                                                    
                [...]
+| `authentication.kerberos.check-interval-sec`               | The check 
interval of Kerberos credential for Paimon catalog.                             
                                                                                
                                  | 60                                          
                                   | No                                         
                                                                                
                    [...]
+| `authentication.kerberos.keytab-fetch-timeout-sec`         | The fetch 
timeout of retrieving Kerberos keytab from 
`authentication.kerberos.keytab-uri`.                                           
                                                                       | 60     
                                                                        | No    
                                                                                
                                                         [...]
+| `authentication.kerberos.keytab-fetch-allow-local-address` | Whether to 
allow the Kerberos keytab URI to resolve to local, private, link-local, or 
cloud metadata addresses from the Gravitino server side. Enable this only for 
trusted URIs that must be fetched from local or private addresses.              
           | false                                                              
            | No                                                                
                          [...]
+| `oss-endpoint`                                             | The endpoint of 
the Aliyun OSS.                                                                 
                                                                                
                            | (none)                                            
                             | required if the value of `warehouse` is a OSS 
path                                                                            
                 [...]
+| `oss-access-key-id`                                        | The access key 
of the Aliyun OSS.                                                              
                                                                                
                             | (none)                                           
                              | required if the value of `warehouse` is a OSS 
path                                                                            
                 [...]
+| `oss-secret-access-key`                                    | The secret key 
the Aliyun OSS.                                                                 
                                                                                
                             | (none)                                           
                              | required if the value of `warehouse` is a OSS 
path                                                                            
                 [...]
+| `s3-endpoint`                                              | The endpoint of 
the AWS S3.                                                                     
                                                                                
                            | (none)                                            
                             | required if the value of `warehouse` is a S3 
path                                                                            
                  [...]
+| `s3-access-key-id`                                         | The access key 
of the AWS S3.                                                                  
                                                                                
                             | (none)                                           
                              | required if the value of `warehouse` is a S3 
path                                                                            
                  [...]
+| `s3-secret-access-key`                                     | The secret key 
of the AWS S3.                                                                  
                                                                                
                             | (none)                                           
                              | required if the value of `warehouse` is a S3 
path                                                                            
                  [...]
+| `token-provider`                                           | The token 
provider type for Paimon catalog backend.                                       
                                                                                
                                  | Token provider could be `bear` or `dlf`.    
                                   | required if the value of `catalog-backend` 
is `rest`.                                                                      
                    [...]
+| `token`                                                    | The bear token 
for Paimon REST catalog authentication.                                         
                                                                                
                             | (none)                                           
                              | required if the value of `token-provider` is 
`bear`.                                                                         
                  [...]
+| `dlf-access-key-id`                                        | The access key 
ID for Aliyun DLF (Data Lake Formation).                                        
                                                                                
                             | (none)                                           
                              | required if the value of `catalog-backend` is 
`rest` and accessing Aliyun DLF Paimon REST server.                             
                 [...]
+| `dlf-access-key-secret`                                    | The access key 
secret for Aliyun DLF.                                                          
                                                                                
                             | (none)                                           
                              | required if the value of `catalog-backend` is 
`rest` and accessing Aliyun DLF Paimon REST server.                             
                 [...]
+| `dlf-security-token`                                       | The security 
token for Aliyun DLF.                                                           
                                                                                
                               | (none)                                         
                                | No                                            
                                                                                
                 [...]
+| `dlf-token-path`                                           | The token path 
for Aliyun DLF.                                                                 
                                                                                
                             | (none)                                           
                              | No                                              
                                                                                
               [...]
+| `dlf-token-loader`                                         | The token 
loader for Aliyun DLF.                                                          
                                                                                
                                  | (none)                                      
                                   | No                                         
                                                                                
                    [...]
 
 :::note
 - If you want to use the `oss` or `s3` warehouse, you need to place related 
jars in the `catalogs/lakehouse-paimon/lib` directory, more information can be 
found in the [Paimon 
S3](https://paimon.apache.org/docs/1.2/maintenance/filesystems/#s3).
diff --git 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/authentication/kerberos/FetchFileUtils.java
 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/authentication/kerberos/FetchFileUtils.java
index 96d91765a2..bba3dabd51 100644
--- 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/authentication/kerberos/FetchFileUtils.java
+++ 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/authentication/kerberos/FetchFileUtils.java
@@ -25,6 +25,7 @@ import java.net.URISyntaxException;
 import java.nio.file.Files;
 import java.util.Optional;
 import org.apache.commons.io.FileUtils;
+import org.apache.gravitino.utils.RemoteUriValidator;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
@@ -35,6 +36,16 @@ public class FetchFileUtils {
 
   public static void fetchFileFromUri(
       String fileUri, File destFile, int timeout, Configuration conf) throws 
IOException {
+    fetchFileFromUri(fileUri, destFile, timeout, conf, false /* 
allowLocalAddressForRemoteUri */);
+  }
+
+  public static void fetchFileFromUri(
+      String fileUri,
+      File destFile,
+      int timeout,
+      Configuration conf,
+      boolean allowLocalAddressForRemoteUri)
+      throws IOException {
     try {
       URI uri = new URI(fileUri);
       String scheme = Optional.ofNullable(uri.getScheme()).orElse("file");
@@ -43,6 +54,14 @@ public class FetchFileUtils {
         case "http":
         case "https":
         case "ftp":
+          RemoteUriValidator.validate(
+              uri,
+              allowLocalAddressForRemoteUri,
+              String.format(
+                  "'%s' to true, or set 'gravitino.iceberg-rest.%s' to true 
for the Iceberg REST "
+                      + "service",
+                  KerberosConfig.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY,
+                  KerberosConfig.KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY));
           FileUtils.copyURLToFile(uri.toURL(), destFile, timeout * 1000, 
timeout * 1000);
           break;
 
diff --git 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/authentication/kerberos/KerberosClient.java
 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/authentication/kerberos/KerberosClient.java
index 12b612fa82..5cfbc9936c 100644
--- 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/authentication/kerberos/KerberosClient.java
+++ 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/authentication/kerberos/KerberosClient.java
@@ -120,7 +120,12 @@ public class KerberosClient implements Closeable {
 
     // TODO: Make the configuration
     int fetchKeytabFileTimeout = kerberosConfig.getFetchTimeoutSec();
-    FetchFileUtils.fetchFileFromUri(keyTabUri, keytabFile, 
fetchKeytabFileTimeout, hadoopConf);
+    FetchFileUtils.fetchFileFromUri(
+        keyTabUri,
+        keytabFile,
+        fetchKeytabFileTimeout,
+        hadoopConf,
+        kerberosConfig.allowKeytabFetchLocalAddress());
 
     return keytabFile;
   }
diff --git 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/authentication/kerberos/KerberosConfig.java
 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/authentication/kerberos/KerberosConfig.java
index 78959c9932..d5bb4c2049 100644
--- 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/authentication/kerberos/KerberosConfig.java
+++ 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/authentication/kerberos/KerberosConfig.java
@@ -38,6 +38,9 @@ public class KerberosConfig extends AuthenticationConfig {
   public static final String FETCH_TIMEOUT_SEC_KEY =
       "authentication.kerberos.keytab-fetch-timeout-sec";
 
+  public static final String KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY =
+      "authentication.kerberos.keytab-fetch-allow-local-address";
+
   public static final String GRAVITINO_KEYTAB_FORMAT =
       "keytabs/gravitino-lakehouse-iceberg-%s-keytab";
 
@@ -75,6 +78,17 @@ public class KerberosConfig extends AuthenticationConfig {
           .checkValue(value -> value > 0, 
ConfigConstants.POSITIVE_NUMBER_ERROR_MSG)
           .createWithDefault(2);
 
+  public static final ConfigEntry<Boolean> 
KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_ENTRY =
+      new ConfigBuilder(KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY)
+          .doc(
+              "Whether to allow the Kerberos keytab URI to resolve to local, 
private, link-local, "
+                  + "or cloud metadata addresses from the Gravitino server 
side. This is disabled "
+                  + "by default to prevent SSRF. Set it to true only when the 
URI is trusted and "
+                  + "must be fetched from local or private addresses.")
+          .version(ConfigConstants.VERSION_1_3_0)
+          .booleanConf()
+          .createWithDefault(false);
+
   public KerberosConfig(Map<String, String> properties) {
     super(properties);
     loadFromMap(properties, k -> true);
@@ -101,6 +115,10 @@ public class KerberosConfig extends AuthenticationConfig {
     return get(FETCH_TIMEOUT_SEC_ENTRY);
   }
 
+  public boolean allowKeytabFetchLocalAddress() {
+    return get(KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_ENTRY);
+  }
+
   public static final Map<String, PropertyEntry<?>> KERBEROS_PROPERTY_ENTRIES =
       new ImmutableMap.Builder<String, PropertyEntry<?>>()
           .put(
@@ -135,5 +153,16 @@ public class KerberosConfig extends AuthenticationConfig {
                   false /* immutable */,
                   60 /* defaultValue */,
                   false /* hidden */))
+          .put(
+              KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY,
+              PropertyEntry.booleanPropertyEntry(
+                  KEYTAB_FETCH_ALLOW_LOCAL_ADDRESS_KEY,
+                  "Whether to allow Kerberos keytab fetch from local or 
private addresses from "
+                      + "the Gravitino server side. This is disabled by 
default to prevent SSRF.",
+                  false /* required */,
+                  false /* immutable */,
+                  false /* defaultValue */,
+                  false /* hidden */,
+                  false /* reserved */))
           .build();
 }


Reply via email to