OCI Secrets Management: Rotation, Versioning, and Application Integration

Every application needs credentials: database passwords, API keys, TLS private keys, third-party service tokens. The wrong place to store them is source code, environment variables in CI/CD pipelines, or configuration files committed to Git. OCI Vault Secrets provides a managed secrets store where applications retrieve credentials at runtime using IAM Resource Principal authentication, rotation is automated through Functions, and every access is logged in the audit trail.

Step 1: Vault and Secrets

resource "oci_kms_vault" "production_vault" {
  compartment_id = var.compartment_id
  display_name   = "production-vault"
  vault_type     = "DEFAULT"
  defined_tags   = { "Operations.Environment" = "production", "Operations.ManagedBy" = "terraform" }
}

resource "oci_kms_key" "vault_master_key" {
  compartment_id      = var.compartment_id
  management_endpoint = oci_kms_vault.production_vault.management_endpoint
  display_name        = "vault-master-encryption-key"

  key_shape {
    algorithm = "AES"
    length    = 32
  }
}

resource "oci_vault_secret" "db_password" {
  compartment_id = var.compartment_id
  vault_id       = oci_kms_vault.production_vault.id
  key_id         = oci_kms_key.vault_master_key.id
  secret_name    = "orders-db-password"
  description    = "Oracle Database password for orders application user"

  secret_content {
    content_type = "BASE64"
    content      = base64encode(var.db_password_initial_value)
    name         = "initial"
    stage        = "CURRENT"
  }

  secret_rules {
    rule_type                         = "SECRET_EXPIRY_RULE"
    secret_version_expiry_interval    = "P90D"
    is_secret_content_retrieval_blocked_on_expiry = true
    time_of_absolute_expiry           = null
  }

  defined_tags = { "Operations.Environment" = "production" }
}

resource "oci_vault_secret" "api_key" {
  compartment_id = var.compartment_id
  vault_id       = oci_kms_vault.production_vault.id
  key_id         = oci_kms_key.vault_master_key.id
  secret_name    = "payment-gateway-api-key"

  secret_content {
    content_type = "BASE64"
    content      = base64encode(var.payment_api_key)
    name         = "v1"
    stage        = "CURRENT"
  }

  secret_rules {
    rule_type                         = "SECRET_EXPIRY_RULE"
    secret_version_expiry_interval    = "P365D"
    is_secret_content_retrieval_blocked_on_expiry = false
  }
}

output "vault_id"          { value = oci_kms_vault.production_vault.id }
output "db_secret_id"      { value = oci_vault_secret.db_password.id }
output "api_key_secret_id" { value = oci_vault_secret.api_key.id }

Step 2: IAM Policy for Application Access

resource "oci_identity_dynamic_group" "orders_app_instances" {
  compartment_id = var.tenancy_ocid
  name           = "orders-application-instances"
  description    = "Compute instances running the orders application"
  matching_rule  = "All {instance.compartment.id = 'COMPARTMENT_OCID', tag.Operations.Application.value = 'orders'}"
}

resource "oci_identity_policy" "orders_vault_policy" {
  compartment_id = var.compartment_id
  name           = "orders-app-vault-access"
  statements = [
    "Allow dynamic-group orders-application-instances to read secret-bundle in compartment id COMPARTMENT_OCID where target.secret.name = 'orders-db-password'",
    "Allow dynamic-group orders-application-instances to read secret-bundle in compartment id COMPARTMENT_OCID where target.secret.name = 'payment-gateway-api-key'"
  ]
}

Step 3: Expiry Alarm

resource "oci_monitoring_alarm" "secret_expiry_warning" {
  compartment_id        = var.compartment_id
  display_name          = "vault-secret-expiry-warning"
  is_enabled            = true
  metric_compartment_id = var.compartment_id
  namespace             = "oci_vault"
  query                 = "SecretExpiryDays[1d]{vaultId = 'VAULT_OCID'}.min() < 30"
  severity              = "WARNING"
  pending_duration      = "PT24H"
  destinations          = [var.ops_notification_topic_id]
  body                  = "One or more Vault secrets expire in less than 30 days. Rotate the secret before expiry to avoid application authentication failures."
}

Operational Notes

Applications retrieve secrets at runtime using the OCI SDK with Resource Principal authentication. No credentials are needed to call the Vault API when the application runs on a compute instance in the matching dynamic group. The SDK authenticates using the instance’s IMDS-issued token, which the IAM policy grants access to read specific secrets. The secret value is retrieved at startup and cached in memory for the lifetime of the process.

Secret rotation with OCI Vault creates a new secret version rather than overwriting the current value. The application must call the Vault API to get the latest version on each restart or on a periodic refresh timer. If your application caches the secret value on startup only, rotation requires a rolling restart of all instances to pick up the new value. Implement a background refresh that re-fetches the secret every hour to avoid requiring restarts for rotation.

Regards,
Osama

#OCI #OracleCloud #Vault #SecretsManagement #Terraform #IaC #TechBlog #Oracle #DevSecOps #PlatformEngineering #CloudSecurity #OracleCloudInfrastructure #ResourcePrincipal #KMS #Encryption #SecretRotation #ZeroTrust #CredentialManagement #IAM #ApplicationSecurity

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.