LiJie20190102 opened a new issue, #13314:
URL: https://github.com/apache/gravitino/issues/13314
### Describe the feature
Support encrypting sensitive configuration values (e.g.,
`gravitino.entity.store.relational.jdbcPassword`) in `gravitino.conf` so that
plaintext passwords are not exposed in configuration files.
Encrypted values are wrapped in `ENC(...)` format. The server transparently
decrypts them at startup when a master encryption key is provided via
environment variable or system property. Plain text values continue to work
unchanged (backward compatible).
### Motivation
Currently, `gravitino.conf` stores the JDBC password in plaintext:
```properties
gravitino.entity.store.relational.jdbcPassword = gravitino
```
This is a security risk in production deployments:
- Anyone with read access to the config file can see the database password.
- Config files are often committed to version control, copied to shared
storage, or included in container images — all of which widen the exposure
surface.
- Auditing and compliance requirements (e.g., SOC2, GDPR) typically mandate
that credentials at rest be encrypted.
The password is read via
`config.get(Configs.ENTITY_RELATIONAL_JDBC_BACKEND_PASSWORD)` in 3 locations:
- `H2Database.java:54` — embedded H2 initialization
- `SqlSessionFactoryHelper.java:91` — MyBatis data source (MySQL/PostgreSQL)
- `StatisticManager.java:116` — partition statistics storage
None of these call sites perform any decryption today.
### Describe the solution
**Approach: Java-native AES-256-GCM (no external dependencies)**
Rather than introducing Jasypt (last updated 2014, requires Apache LICENSE
review) or another third-party library, use the JDK built-in `javax.crypto`
package. This keeps the dependency footprint at zero — important for an Apache
project.
### Encryption design
| Parameter | Value
|
| --------------------- |
---------------------------------------------------------------- |
| Cipher algorithm | `AES/GCM/NoPadding` (256-bit key)
|
| Key derivation | `PBKDF2WithHmacSHA256` (65536 iterations)
|
| Salt | 16 random bytes (per-encryption)
|
| IV | 12 random bytes (per-encryption)
|
| Authentication tag | 128 bits
|
| Encrypted format | `ENC(base64(salt[16] + iv[12] + ciphertext +
tag))` |
Each encryption produces a different ciphertext (random salt + IV), and GCM
provides integrity protection against tampering.
### Master key resolution
The master encryption key is **never** stored in `gravitino.conf`. It is
resolved at startup from (in priority order):
1. Environment variable: `GRAVITINO_PASSWORD_ENCRYPTION_KEY`
2. System property: `gravitino.password.encryption.key`
### User workflow
```bash
# 1. Encrypt the password (choose either tool)
python3 scripts/encrypt_password.py encrypt "myDbPassword" --key
"mySecretMasterKey"
# or
java -cp gravitino-common-*.jar
org.apache.gravitino.utils.PasswordEncryptorCLI \
encrypt "myDbPassword" --key "mySecretMasterKey"
# Output: ENC(xxxbase64xxx)
# 2. Put the encrypted value in gravitino.conf
gravitino.entity.store.relational.jdbcPassword = ENC(xxxbase64xxx)
# 3. Set the master key as an environment variable and start the server
export GRAVITINO_PASSWORD_ENCRYPTION_KEY="mySecretMasterKey"
./bin/gravitino.sh start
```
### Code changes
| File | Change | Description
|
| ------------------------------------------------- | -------- |
-------------------------------------------------------------------------- |
| `common/.../utils/PasswordEncryptor.java` | **New** | AES-256-GCM
encrypt/decrypt utility, `isEncrypted()`, `decryptIfNeeded()` |
| `common/.../utils/PasswordEncryptorCLI.java` | **New** | Java CLI
tool (`encrypt` / `decrypt` subcommands, interactive mode) |
| `common/.../utils/TestPasswordEncryptor.java` | **New** | 15 unit
tests (roundtrip, wrong key, tampered ciphertext, backward compat) |
| `core/.../database/H2Database.java` | **Modified** | Wrap
password read with `decryptIfNeeded()` |
| `core/.../session/SqlSessionFactoryHelper.java` | **Modified** | Wrap
password read with `decryptIfNeeded()` |
| `core/.../stats/StatisticManager.java` | **Modified** | Wrap
password read with `decryptIfNeeded()` |
| `scripts/encrypt_password.py` | **New** | Python
encryption script (requires `cryptography` package), interoperable |
| `conf/gravitino.conf.template` | **Modified** | Add
usage instructions for encrypted passwords |
### Backward compatibility
- Plain text passwords work unchanged — `decryptIfNeeded()` returns the
original value if it does not start with `ENC(`.
- No configuration changes required for existing deployments.
- If an encrypted value is detected but no master key is configured, the
server fails fast with a clear error message.
### Cross-language interoperability
The Python script and Java CLI produce/consume the same `ENC(base64(...))`
format. Verified:
- Java encrypt → Python decrypt ✅
- Python encrypt → Java decrypt ✅
### Additional context
**Why not Jasypt?**
- Jasypt's last release was 1.9.3 in 2014; the project is inactive.
- Default algorithm `PBEWithMD5AndDES` is weak (can be changed, but defaults
matter).
- Introducing a third-party dependency in an Apache project requires
LICENSE/NOTICE review.
- Java 17's built-in `javax.crypto` provides everything needed with zero
overhead.
**Future extensibility**
- The same `PasswordEncryptor.decryptIfNeeded()` pattern can be applied to
other sensitive config values (e.g., authenticator secrets, auxiliary service
tokens) with minimal changes.
- A `gravitino.password.encryption.algorithm` config key could be added
later to allow pluggable algorithms if needed.
**Scope of this issue**
- The PR implements encryption for
`gravitino.entity.store.relational.jdbcPassword` only.
- Other password-like config keys can be added incrementally in follow-up
PRs.
--
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]