← Oracle ACE articles

ACE-U field notes

Vault + DB wallets: secure distribution patterns (illustrative)

Generating an Autonomous Database wallet, keeping it in Vault, and rebuilding it only where it is needed.

Shubham Anandani··3 min read

Problem

Mutual TLS to Autonomous Database needs a wallet. In practice that wallet ends up in a chat thread, in a repo, or baked into a container image, and rotating it means finding every copy.

I wanted the wallet to have one home, a controlled way out, and a short life on disk.

Options and trade-offs

  • Copying the wallet files around: fast, hard to rotate and hard to audit.
  • Wallet files as Vault secrets: access is governed by IAM and logged. The files need encoding and a rebuild step.
  • Server-authenticated TLS without a wallet: fewer artifacts. It depends on the database allowing TLS connections without mTLS.

My recommendation

Generate the wallet from the database service, store its files as Vault secrets, and let the runtime principal read them at startup. The application recreates the files in a protected directory, sets TNS_ADMIN, and connects through the TNS alias.

Worked example

Generate the wallet and push the files into Vault. The CLI needs base64 content, while the Console encodes plain text for you.

oci db autonomous-database generate-wallet \
  --autonomous-database-id <db_ocid> \
  --file wallet.zip \
  --password "<wallet_password>"
 
unzip wallet.zip -d wallet
base64 -w0 wallet/cwallet.sso > cwallet.sso.b64
 
oci vault secret create-base64 \
  --compartment-id <compartment_ocid> \
  --vault-id <vault_ocid> \
  --key-id <symmetric_key_ocid> \
  --secret-name db_wallet_cwallet_sso \
  --secret-content-content "$(cat cwallet.sso.b64)" \
  --secret-content-stage CURRENT

Repeat for the other files the connection needs, usually tnsnames.ora, sqlnet.ora and ojdbc.properties. At startup, the app reads each secret, decodes it and writes it into a directory only its own user can access. The sketch below shows the permission handling.

Path dir = Files.createTempDirectory("wallet",
    PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------")));
for (var e : walletSecrets.entrySet()) {          // name -> base64 content
    Files.write(dir.resolve(e.getKey()), Base64.getDecoder().decode(e.getValue()));
}
// start the JVM or pool with -Doracle.net.tns_admin=<dir> or TNS_ADMIN=<dir>

The ojdbc.properties file that ships in the wallet refers to the TNS_ADMIN directory for its wallet location, so it works unchanged when you rebuild the directory.

Checklist

  1. Generate the wallet from the database service and track when it expires.
  2. Store each file as a secret, using base64 for CLI and API paths.
  3. Give the runtime principal read access to those secrets only.
  4. Rebuild the wallet directory at runtime with owner-only permissions, and never bake it into an image.
  5. Confirm the server certificate name check is on, and log wallet reads.
  6. Keep the previous secret version for a short time in case rotation goes wrong.

Where this doesn't apply

If your policy allows server-authenticated TLS, you may not need a wallet at all. That removes the distribution problem entirely.

One honest limitation

Wallets are multi-file, and the cleanup of temporary files is easy to forget. Test the rebuild path and the deletion path as carefully as the connection itself.

Sources

  • OCI Documentation: Downloading Client Credentials (Wallets) for Autonomous Database
  • OCI CLI Reference: oci vault secret create-base64
  • OCI Documentation: Connecting with JDBC and a Wallet