roryqi commented on code in PR #10732:
URL: https://github.com/apache/gravitino/pull/10732#discussion_r3077626222


##########
design-docs/external_secret_management_design.md:
##########
@@ -0,0 +1,1402 @@
+# External Secret Management Integration for Apache Gravitino
+
+---
+
+
+## Background
+
+Apache Gravitino currently supports credential vending for cloud storage 
systems (S3, GCS, ADLS, OSS) through various credential providers. However, 
sensitive credentials (access keys, secret keys, passwords, API tokens) are 
currently configured directly in configuration files or catalog properties as 
plain text, which poses significant security risks in production environments.
+
+Modern cloud-native applications follow the principle of **never storing 
secrets in database as plain-text or in  configuration files** and instead rely 
on external secret management systems that provide:
+- Centralized secret storage with encryption at rest
+- Fine-grained access control and audit logging
+- Automatic secret rotation
+- Secret versioning and rollback capabilities
+- Integration with cloud IAM systems
+
+### Security Risks  Today
+
+1. **Plain Text Catalog Credentials in Database:**
+   When users create catalogs via UI/API, passwords are stored as plain text:
+   ```properties
+   # PostgreSQL Catalog
+   jdbc-password = MyDatabasePassword123
+   
+   # MySQL Catalog  
+   jdbc-password = SuperSecret456
+   
+   # Hive Catalog
+   authentication.kerberos.keytab-uri = /path/to/keytab
+   ```
+   These credentials are stored in Gravitino's backend database in plain text 
and accessible to anyone with database access.
+
+2. **Lack of Secret Rotation:** When catalog passwords are rotated, users must 
manually update the catalog, which exposes the new password again as plain text.
+
+3. **Compliance Requirements:** Many industries (finance, healthcare, 
government) mandate external secret management for SOC2, HIPAA, PCI-DSS 
compliance.
+
+
+### Industry Best Practices
+
+Many organizations running applications in production already use an external 
secret management solution, such as:
+- AWS Secrets Manager
+- HashiCorp Vault
+- Azure Key Vault
+- Google Secret Manager
+- Kubernetes Secrets with external secret operators
+
+Gravitino should integrate seamlessly with these existing secret management 
infrastructures and provide an extensible framework to support any other vault 
providers.
+
+## Current State
+
+### Understanding CredentialProvider vs SecretProvider
+
+It's important to clarify the relationship between two similar-sounding 
concepts:
+
+| Component | Purpose | Layer | Changes Needed |
+|-----------|---------|-------|----------------|
+| **CredentialProvider** (existing) | Vends credentials to clients (Iceberg, 
Spark, etc.) for accessing cloud storage | Application layer |  None - works 
transparently |
+| **SecretProvider** (proposed) | Fetches secrets from external systems (AWS 
Secrets Manager, Vault) | Infrastructure layer |  New abstraction |
+
+**Key Point:** `SecretProvider` is a **prerequisite layer** that runs 
**before** `CredentialProvider`. It resolves secret references 
(`${secret:...}`) to actual values, then passes those resolved values to 
`CredentialProvider` implementations.
+
+**Example Flow[Postgresql catalog]:**
+```
+User creates PostgreSQL catalog via UI with:
+  jdbc-password = ${secret:prod-postgres-catalog/password}
+              ↓
+Gravitino stores the reference "${secret:prod-postgres-catalog/password}" in 
backend DB
+              ↓
+When catalog needs to connect to PostgreSQL:
+  SecretProvider: Calls AWS Secrets Manager API → returns "mySecureP@ssw0rd"
+              ↓
+  Catalog/CredentialProvider: Receives resolved password "mySecureP@ssw0rd" 
and connects to PostgreSQL
+              ↓ (no code changes needed!)
+Client: Uses the PostgreSQL catalog to query tables
+```
+
+They work together at different layers, and existing `CredentialProvider` 
implementations require **zero code changes**.
+
+### Catalog Credential Flow Today
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│  User Creates Catalog via UI/API                             │
+│  ┌───────────────────────────────────────────────────────┐ │
+│  │ Catalog: "prod-postgres"                             │ │
+│  │ Provider: "jdbc-postgresql"                          │ │
+│  │ Properties:                                          │ │
+│  │   jdbc-url = jdbc:postgresql://db.example.com/prod   │ │
+│  │   jdbc-user = app_user                               │ │
+│  │   jdbc-password = PlainTextPassword123               │ │
+│  └───────────────────────────────────────────────────────┘ │
+└─────────────────────────────────────────────────────────────┘
+                            │
+                            ▼
+┌─────────────────────────────────────────────────────────────┐
+│  Gravitino Backend Database                                  │
+│  - Stores catalog properties including plain text password  │
+│  - Password visible in database and logs                     │
+└─────────────────────────────────────────────────────────────┘
+                            │
+                            ▼
+┌─────────────────────────────────────────────────────────────┐
+│  Catalog.initialize(properties)                              │
+│  - Reads password from properties Map                        │
+│  - Connects to external database with plain text password   │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Proposed Solution
+
+### High-Level Architecture
+
+Introduce a **Secret Provider** abstraction layer that resolves secret 
references :
+
+```
+┌──────────────────────────────────────────────────────────────────┐
+│  Gravitino Server Configuration (gravitino.conf)                  │
+│  ┌────────────────────────────────────────────────────────────┐  │
+│  │ # Global secret provider configuration                     │  │
+│  │ secret-provider = aws-secrets-manager                      │  │
+│  │ secret-provider.region = us-east-1                         │  │
+│  │ secret-provider.secret-path-prefix = gravitino/prod/       │  │
+│  └────────────────────────────────────────────────────────────┘  │
+└──────────────────────────────────────────────────────────────────┘
+                            │
+                            ▼
+┌──────────────────────────────────────────────────────────────────┐
+│  SecretProviderFactory                                            │
+│  - Creates appropriate SecretProvider based on configuration      │
+└──────────────────────────────────────────────────────────────────┘
+                            │
+                            ▼
+┌──────────────────────────────────────────────────────────────────┐
+│  SecretProvider Interface                                         │
+│  ┌────────────────────────────────────────────────────────────┐  │
+│  │ + resolveSecret(secretReference): String                   │  │
+│  │ + resolveSecrets(secretReferences): Map<String, String>    │  │
+│  │ + refreshSecrets(): void                                   │  │
+│  └────────────────────────────────────────────────────────────┘  │
+└──────────────────────────────────────────────────────────────────┘
+         │                    │                    │
+         ▼                    ▼                    ▼
+┌──────────────┐  ┌──────────────────┐  ┌─────────────────┐
+│ AWS Secrets  │  │  HashiCorp       │  │  Plain Text     │
+│ Manager      │  │  Vault           │  │  (Backward      │
+│ Provider     │  │  Provider        │  │  Compatibility) │
+└──────────────┘  └──────────────────┘  └─────────────────┘
+                            │
+                            ▼
+┌──────────────────────────────────────────────────────────────────┐
+│  SecretCache                                                     │
+│  - TTL-based caching                                             │
+│  - Thread-safe access                                            │
+└──────────────────────────────────────────────────────────────────┘
+                            │
+                            ▼
+┌──────────────────────────────────────────────────────────────────┐
+│  CredentialProviders                                             │
+│  - Receives resolved catalog credentials transparently           │
+│  - No code changes required                                      │
+└──────────────────────────────────────────────────────────────────┘
+```
+
+### Hybrid Approach: Global vs Explicit Provider (Catalog Properties)
+
+The design supports **two complementary syntax patterns for catalog 
properties**:
+
+1. **Global Provider (Recommended for single-tenant):**
+   - Configure one secret provider in `gravitino.conf`
+   - Use simple syntax in catalog properties: `${secret:path/to/secret}`
+   - Simpler to manage, easier to migrate providers
+
+2. **Explicit Provider (Multi-tenant scenarios):**
+   - Configure multiple secret providers in `gravitino.conf`
+   - Use provider-specific syntax in catalog properties: 
`${secret:provider-type:path/to/secret}`
+   - Each catalog can use different secret management systems
+   - Enables team-specific or compliance-driven provider selection
+
+**Example (Catalog Property Values):**
+```properties
+# Catalog property using global provider
+jdbc-password = ${secret:postgres/password}
+
+# Catalog property with explicit provider override
+jdbc-password = ${secret:vault:postgres/password}
+```
+
+This hybrid approach provides **simplicity by default** while enabling 
**flexibility when needed**.
+
+## Goals (V1 Scope: Catalog Properties Only)
+
+**Primary Focus:** Secret resolution for **catalog create/update/read 
operations only**.
+
+1. **Provide pluggable external secret management** through a well-defined 
abstraction
+2. **Implement AWS Secrets Manager integration** as the first reference 
implementation
+3. **Support secret references in catalog properties** (`jdbc-password`, 
`jdbc-user`, Kerberos keytabs, etc.)
+4. **Maintain backward compatibility** with existing plain-text catalog 
properties
+5. **Implement API redaction rules** to prevent secret exposure in responses
+6. **Minimize performance overhead** through intelligent caching
+
+## Non-Goals (Deferred to Future Work)
+
+1.  **Server configuration secret interpolation** (e.g., `gravitino.conf`, 
`gravitino-iceberg-rest-server.conf`) — **V2 feature**
+2.  Encrypting Gravitino's internal metadata store (separate concern) 
+
+## Design Details
+
+### Core Components
+
+#### 1. SecretProvider Interface

Review Comment:
   Maybe we can refer to the Iceberg similar design, you can see
   ```
   /** A minimum client interface to connect to a key management service (KMS). 
*/
   public interface KeyManagementClient extends Serializable, Closeable {
   
     /**
      * Wrap a secret key, using a wrapping/master key which is stored in KMS 
and referenced by an ID.
      * Wrapping means encryption of the secret key with the master key, and 
adding optional
      * KMS-specific metadata that allows the KMS to decrypt the secret key in 
an unwrapping call.
      *
      * @param key a secret key being wrapped
      * @param wrappingKeyId a key ID that represents a wrapping key stored in 
KMS
      * @return wrapped key material
      */
     ByteBuffer wrapKey(ByteBuffer key, String wrappingKeyId);
   
     /**
      * Some KMS systems support generation of secret keys inside the KMS 
server.
      *
      * @return true if KMS server supports key generation and 
KeyManagementClient implementation is
      *     interested to leverage this capability. Otherwise, return false - 
Iceberg will then
      *     generate secret keys locally (using the SecureRandom mechanism) and 
call {@link
      *     #wrapKey(ByteBuffer, String)} to wrap them in KMS.
      */
     default boolean supportsKeyGeneration() {
       return false;
     }
   
     /**
      * Generate a new secret key in the KMS server, and wrap it using a 
wrapping/master key which is
      * stored in KMS and referenced by an ID. This method will be called only 
if supportsKeyGeneration
      * returns true.
      *
      * @param wrappingKeyId a key ID that represents a wrapping key stored in 
KMS
      * @return key in two forms: raw, and wrapped with the given wrappingKeyId
      */
     default KeyGenerationResult generateKey(String wrappingKeyId) {
       throw new UnsupportedOperationException("Key generation is not supported 
in this KmsClient");
     }
   
     /**
      * Unwrap a secret key, using a wrapping/master key which is stored in KMS 
and referenced by an
      * ID.
      *
      * @param wrappedKey wrapped key material (encrypted key and optional KMS 
metadata, returned by
      *     the wrapKey method)
      * @param wrappingKeyId a key ID that represents a wrapping key stored in 
KMS
      * @return raw key bytes
      */
     ByteBuffer unwrapKey(ByteBuffer wrappedKey, String wrappingKeyId);
   
     /**
      * Initialize the KMS client with given properties.
      *
      * @param properties kms client properties
      */
     void initialize(Map<String, String> properties);
   
     /**
      * Close KMS Client to release underlying resources, this could be 
triggered in different threads
      * when KmsClient is shared by multiple encryption managers.
      */
     @Override
     default void close() {}
   
     /**
      * For KMS systems that support key generation, this class keeps the key 
generation result - the
      * raw secret key, and its wrap.
      */
     class KeyGenerationResult {
       private final ByteBuffer key;
       private final ByteBuffer wrappedKey;
   
       public KeyGenerationResult(ByteBuffer key, ByteBuffer wrappedKey) {
         this.key = key;
         this.wrappedKey = wrappedKey;
       }
   
       public ByteBuffer key() {
         return key;
       }
   
       public ByteBuffer wrappedKey() {
         return wrappedKey;
       }
     }
   }
   ```
   You can survey about this and realize its design, background and 
consideration by finding the discussion and design documents.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to