Managing Secrets

Create and manage vault secrets, secret tags, and secret rules.

With the importance of using OCI cloud services, it's important to store, retrieve, and manage secrets in its digital vault. Secrets can be a password, certificates, SSH keys, or authentication tokens that you use for connecting to OCI services and systems. Storing secrets in OCI Vault has greater security than storing them in a code or configuration files. The application communicates with the OCI Secret Management to retrieve the secret and connect to the target service.

Oracle Cloud Infrastructure Secret Management lets you to effortlessly protect sensitive data such as API keys, passwords, and encryption keys by using secrets. It offers a robust solution to create, store, manage, and access these secrets securely. The centralized storage it provides leverages the hardware security modules (HSMs) and granular access control to safeguard the security and integrity of critical information. Use OCI Secret Management to eliminate embedding secrets directly in applications and reduce the attack surface and strengthen an application's overall security.

Automatic Secret Generation and Rotation

When a secret is generated, it's updated periodically. You can update a secret manually or set automatically. The automatic generation and rotation of secrets takes off the burden in setting secret manually and rotating it using scripts but instead, provides efficient way to manage secrets from creation, rotation and deletion.

The automatic secret generation feature supports the use of templates for secret generation. With automatic secret rotation, you can set secret interval from 1 to 12 months. The feature integrates with Autonomous Database and Function services, letting you update a secret used in Autonomous Database or Functions code. In OCI Functions, the automatic rotation of secrets lets you easily rotate a credential and run code as part of the rotation process. The automation secret rotation feature is also available for manually created secrets.

Cross Region Replication of Secrets

You can replicate a secret in up to three destination regions. The replicas are read-only, and inherit changes made to the source secret. See Replicating Secrets for more information.

Benefits of using automatic secret rotation

Following are the benefits of automatic secret rotation:
  • Enhanced security: Regularly updating your secrets minimizes the impact of compromised credentials leading to a data breach.
  • Operational efficiency: Automating manual tasks such as creating, rotating a secret saves time and efficiency.
  • Regulated compliance: Adhere to many standards that regulate compliance for secret rotation and automation.
  • Reduced human error: Automating repetitive tasks reduces the possibility of human errors to bolster security.

Secret Generation

You can generate a secret for passphrases, SSH keys, and bytes. All secrets that OCI Vault generates are FIPS and security compliant. You can generate a secret using the OCI Console, API, or CLI. When you generate a secret, you must provide the secret context and define the secret template. The secret context defines the secret type and structure of the secret. Based on the secret type that you select; Vault supports different secret generation templates.

You can generate the following secret types:
  • PASSPHRASE: Generate passwords up to 32 characters in length. For OCI Database service default passwords, the maximum character length is 30.
  • SSH_KEY: Generate RSA key pairs of length 2048, 3072 and 4096. The private key is stored in the PKCS#8 PEM format and the public key is stored in the X.509 PEM format.
  • BYTES: Generate 512 and 1024 bytes that are FIPS complaint binary secret. Bytes are base64 code.

Secret Types and Default Templates

  • PASSPHRASE
    • Supported templates: SECRETS_DEFAULT_PASSWORD and DBAAS_DEFAULT_PASSWORD
    • Placeholder in secret template: %GENERATED_PASSPHRASE%
    • Example: {"user": "abc", "pwd": "%GENERATED_PASSPHRASE%"}
  • SSH_KEY
    • Supported templates: RSA_2048, RSA_3072, RSA_4096
    • Placeholder in secret template: %GENERATED_PUBLIC_KEY%, %GENERATED_PRIVATE_KEY%
    • Example: {"publicKey": "%GENERATED_PRIVATE_KEY%", "privateKey": "%GENERATED_PRIVATE_KEY%"} → {"publicKey": "-----BEGIN PUBLIC KEY-----\nBase64 encoded public key\n-----END PUBLIC KEY-----", "privateKey":"-----BEGIN PRIVATE KEY-----\nBase64 encoded private key\n-----END PRIVATE KEY-----"}
  • BYTES
    • Supported templates: BYTES_512, BYTES_1024
    • Placeholder in secret template: %GENERATED_BYTES%
    • Example: {"host": "abc", "hostLuksKey": "%GENERATED_BYTES%"} → {"host": "abc", "hostLuksKey": "generatedbyteshere=="}

Secret Versions and Rotation States

Learn about vault secret versions, rotation states, and the impact of secret version limitation.

Understanding vault secret versions and rotation states helps you track and manage secret contents to stay in compliance with any limits, rotation or other rules, or regulations.

To learn basic secret concepts, including secret versions and rotation states, see Key and Secret Management Concepts. For information about working with secret versions, see Managing Secrets.

Rotation Functions

Rotation functions are responsible for rotating the secrets in Secret Service. Expand the following section to learn about the four steps in a rotation function, and to see sample function handler code

Rotation function steps and details

Rotation functions included the following four steps:

  1. VERIFY_CONNECTION: Verifies the connection to the target system using the existing credentials stored in the secret.
  2. CREATE_PENDING_VERSION: Creates a new pending secret version in the secret.
  3. UPDATE_TARGET_SYSTEM: Updates the target system with the credentials stored in the pending secret version created in the second step.
  4. PROMOTE_PENDING_VERSION: Updates the state of the secret from pending to current.

Function handler:

The function handler must contain the logic to call the appropriate rotation step based on the step parameter. This can be achieved either by using IF ELSE conditional statements or switch case. The function handler generates an error if an invalid step parameter is provided.

Input:

The function handler expects single parameter: SecretRotationInput. The secretRotationInput is a POJO (Plain-old java object) containing the parameters required for rotation, including the secretId(String), step(String) and currentVersionNo(Long).

  • secretId: The secret identifier or OCID
  • step: The rotation step (Must be one of VERIFY_CONNECTION, CREATE_PENDING_VERSION, UPDATE_TARGET_SYSTEM or PROMOTE_PENDING_VERSION)
  • currentVersionNo: The number of the CURRENT secret version

SecretRotationInput POJO


@Getter
@Setter
@ToString(callSuper = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
@AllArgsConstructor
@Builder(toBuilder = true)
public static class SecretRotationInput {
    private String step;
    private Long versionNo;
    private String secretId;
}                

Sample function handler code:

public static class SecretRotationOutput {
    int responseCode;
    Long currentVersionNo;
    String returnMessage;
}
 
 
public SecretRotationOutput handleRequest(SecretRotationInput input) {
    switch(input.getStep()) {
        case "VERIFY_CONNECTION":
            return verifyConnection(input.getSecretId());
        case "CREATE_PENDING_VERSION":
            return createNewPendingVersion(input.getSecretId());
        case "UPDATE_TARGET_SYSTEM":
            return updateTargetSystem(input.getSecretId(), input.getVersionNo());
        case "PROMOTE_PENDING_VERSION":
            return promotePendingVersion(input.getSecretId(), input.getVersionNo());
        default:
            log.error("Secret Rotation: invalid rotation step");
            return SecretRotationOutput.builder()
                    .responseCode(400)
                    .currentVersionNo(null)
                    .returnMessage("INVALID STEP")
                    .build();
    }
}

Step 1: VERIFY_CONNECTION

This step verifies connection to the target system.

  • Input: This method expects only the parameter SecretId.
  • Output: This method outputs the SecretRotationOutput.
  • Logic: The function first tries to fetch the PENDING version and verify the connection with it. This handles the failure scenario where previous retry failed to promote the PENDING version, but the version has been successfully applied to the system. If it succeeds, return the success response. If it fails, fetch the current version and check the connection. If that succeeds, return the success response code along with the current version number. If it doesn't succeed, return the error.

Pseudo code for VERIFY_CONNECTION

pseudo code for verifyConnection:
    Check if "PENDING" secret version exists
        If it exist, verify connection
            If succeeds, then return success.
    Fetch the "CURRENT" secret version
    Verify connection with "CURRENT" secret version
       If succeeds, return success and the current version number in the response.
        If not, return SecretRotationOutput with appropriate error code and message.

Step 2: CREATE_PENDING_VERSION

Generates a new pending secret version.

  • Input: This method also accepts the secretId parameter.
  • Output: This method outputs the SecretRotationOutput.
  • Logic: This method first checks for the existence of a secret version in PENDING state. If one doesn't exist, it generates a new PENDING secret version. However, if one already exists, the step returns the success response. You can also use the secret metadata field to store extra details about the secret structure and can use the same structure when creating a new secret version. You can also use the Secret service's auto-generation feature to auto-generate the secret content. Auto-generation lets you to write more generic functions.

Pseudo code for CREATE_PENDING_VERSION

pseudo code for createPendingVersion:
    Try to get the pending version
        If it succeeds, return success
        Else create a new secret version in "PENDING" state and then return success

Sample code:

// Constructor
public void constructorClass() {
     secretsDpClient = SecretsClient.builder()
                    .region(region)
                    .configuration(configuration)
                    .build(auth_provider);
     secretsCpClient = VaultsClient.builder()
                    .region(region)
                    .configuration(configuration)
                    .build(auth_provider);
}
 
private String createNewPassword() {
    // This method must contains the logic to generate the new secret content
}
  
private SecretRotationOutput createNewPendingVersion(SecretRotationInput input) {
    // Make sure the given secret exist
    Secret secret = secretsCpClient.getSecret(GetSecretRequest.builder()
                    .secretId(input.getSecretId())
            .build()).getSecret();
  
    // Now try to get the pending secret version, if that fails, create a new one
    try {
        GetSecretBundleRequest getSecretBundleRequest = GetSecretBundleRequest.builder()
                .secretId(input.getSecretId())
                .stage(GetSecretBundleRequest.Stage.Pending)
                .build();
        secretsDpClient.getSecretBundle(getSecretBundleRequest);
        return SecretRotationOutput.builder()
                .responseCode(200)
                .returnMessage("Successfully retrieved the pending secret version")
                .build();
    } catch (BmcException bmc) {
        // Create a new pending version
        UpdateSecretDetails updateSecretDetails = secret.getIsAutoGenerationEnabled() ? UpdateSecretDetails.builder()
                .secretContent(Base64SecretContentDetails.builder()
                        .stage(SecretContentDetails.Stage.Pending)
                        .build())
                .build() : UpdateSecretDetails.builder()
                .secretContent(Base64SecretContentDetails.builder()
                        .content(createNewPassword())
                        .stage(SecretContentDetails.Stage.Pending)
                        .build())
                .build();
        secretsCpClient.updateSecret(UpdateSecretRequest.builder()
                .secretId(input.getSecretId())
                .updateSecretDetails(updateSecretDetails)
                .build()).getSecret();
        log.info("Successfully added a new version with 'PENDING' stage for secretId {}", input.getSecretId());
        return SecretRotationOutput.builder()
                .responseCode(200)
                .returnMessage("Pending version created successfully")
                .currentVersionNo(input.getVersionNo())
                .build();
    }
}

Step 3: UPDATE_TARGET_SYSTEM

Sets the pending secret in the target system.

  • Input: This method accepts the currentVersionNo and secretId.
  • Output: This method also outputs the SecretRotationOutput.
  • Logic: This method first tries to login to the target system with the PENDING secret version and returns on success. This also handles the failure scenario where previous retry failed to promote the pending version but the version has been successfully applied to the system. If that fails, it tries to login with the CURRENT secret version. If the current secret version succeeds, it sets the PENDING password as the user password in the target system.

Pseudo code for UPDATE_TARGET_SYSTEM

Pseudo code for updateTargetSystem:
    Fetch the "PENDING" secret version
    Check the connection with Pending version
        It it works return success and the pending version number in the response
    Fetch the "CURRENT" secret version
    Check the connection with it
        If it works
            Update the target system with "PENDING" version
            Verify that target system is updated.
               if yes then return success and the pending version number in the response 
            else return the appropriate error code and the error message
        If not, return SecretRotationOutput with appropriate error code and message.

Sample code:

private SecretRotationOutput updateTargetSystem(String secretId, Long currentVersionNo) {
    // Get the pending secret version
    SecretBundle pendingSecretBundle = secretsDpClient.getSecretBundle(GetSecretBundleRequest.builder()
                    .secretId(secretId)
                    .stage(GetSecretBundleRequest.Stage.Pending)
            .build()).getSecretBundle();
  
    // First try to login with the pending secret, if it succeeds, return
    Connection conn = getConnection(pendingSecretBundle);
    if(conn) {
        log.info("Updated the target system with pending version");
        return SecretRotationOutput.builder()
                    .responseCode(200)
                    .returnMessage("Successfully update target service")
                    .currentVersionNo(pendingSecretBundle.getVersionNumber())
                    .build();
    }
  
    SecretBundle currentSecretBundle = secretsDpClient.getSecretBundle(GetSecretBundleRequest.builder()
                    .versionNumber(currentVersionNo)
                    .secretId(secretId)
            .build()).getSecretBundle();
  
    conn = getConnection(currentSecretBundle);
    if(conn) {
        // Write logic to update the target system and check the connection with the updated password
    } else {
        log.error("Unable to log into system using the current version");
        return SecretRotationOutput.builder()
                .returnMessage("Unable to log into system using the current version")
                .responseCode(400)
                .build();
    }
}

Step 4: PROMOTE_PENDING_VERSION

Finish the rotation by marking the pending secret as current.

  • Input: This method accepts the currentVersionNo and secretId.
  • Output: This method also outputs the SecretRotationOutput.
  • Logic: Call the Secret service to promote the PENDING version to CURRENT. UpdateRequest must specify which version need be promoted to CURRENT.

Pseudo code for PROMOTE_PENDING_VERSION

pseudo code for promotePendingVersion:
    Make updateSecret Api call to promote the specified version to "CURRENT"

Sample code:

private SecretRotationOutput promotePendingVersion(String secretId, Long versionNo) {
    try {
        Secret secret = secretsCpClient.updateSecret(UpdateSecretRequest.builder()
                .secretId(secretId)
                .updateSecretDetails(UpdateSecretDetails.builder()
                        .currentVersionNumber(versionNo)
                        .build())
                .opcRequestId(UUID.randomUUID().toString())
                .build()).getSecret();
        log.info("Successfully promoted the version number {} to 'CURRENT'", versionNo);
        return SecretRotationOutput.builder()
                .responseCode(200)
                .currentVersionNo(secret.getCurrentVersionNumber())
                .returnMessage("Successfully promoted the pending version")
                .build();
    } catch (BmcException bmc) {
        log.error("Fail to promote the pending version. SecretId: {}, pendingVersionNo: {}. {}", secretId, versionNo, bmc.getMessage());
        return SecretRotationOutput.builder()
                .returnMessage(bmc.getMessage())
                .responseCode(bmc.getStatusCode())
                .build();
    }
}

Rotation States

Secret versions can have more than one rotation state at a time. When only one secret version exists, such as when you first create a secret, the secret version is automatically marked as both current and latest. The latest version of a secret contains the secret contents that were last uploaded to the vault. If you need to find which secret version has secret material most recently uploaded, you can use the "latest" status to do this.

When upload new secret contents for secret rotation, you can mark the secret version as pending. This lets you upload the secret material to the vault without immediately putting it into active use. You can continue using the current secret version until you're ready to promote a pending secret version to current status. This typically happens after you have rotated credentials on the target resource or service first. Note that you need to consider the effects of the secret rotation on applications and resources that rely on the secret. Changing which secret version is current could prevent an application or resource that needs it from retrieving the expected secret version from the vault.

Secret versions can also be marked as previous. This lets you easily roll back a secret to a previous version. You might need to do this when a secret is a mistakenly rotated, or when restoring a backup of a resource that needs to resume using an older secret version. A secret version marked as previous is the version that was marked as current directly before the most recent rotation. To roll back to a previous version, you update the secret to specify the secret version number you want.

As long as a secret version hasn't been deleted, you can update the secret to use that past secret version. When you update the secret, the secret version number you select gets marked as current. This has the same effect as promoting a secret version to current.

You can only delete secret versions that have been marked as deprecated. A deprecated secret version is one that's not marked as current, pending, or previous. This helps to prevent circumstances where you might delete a secret version that you need later (for example, when restoring a database you backed up before). A secret version that's marked as anything other than deprecated can be marked as current to return it to active use.

Version Limitation

The limits on secret versions applies to both a secret's versions that are in use and versions that are deprecated, including those that have been scheduled for deletion. For information about limits on the number of versions for a particular secret and for secret versions in a tenancy, see Service Limits.

Before You Begin

Before you begin, we recommend that you first read Secret Rules and Secret Versions and Rotation States to better understand the implications of working with rules, secret versions, and secret version rotation states.

Required IAM Policy

To use Oracle Cloud Infrastructure, an administrator must be a member of a group granted security access in a policy  by a tenancy administrator. This access is required whether you're using the Console or the REST API with an SDK, CLI, or other tool. If you get a message that you don't have permission or are unauthorized, verify with the tenancy administrator what type of access you have and which compartment  your access works in.

For administrators:

If you're new to policies, see Managing Identity Domains and Common Policies.

Tagging Secrets

Apply tags to resources to help organize them according to your business needs. You can apply tags when you create a resource, and you can update a resource later to add, revise, or remove tags. For general information about applying tags, see Resource Tags.

Monitoring Resources

You can monitor the health, capacity, and performance of Oracle Cloud Infrastructure resources by using metrics, alarms, and notifications. For more information, see Monitoring and Notifications.

Moving Resources to a Different Compartment

You can move secrets from one compartment to another. After you move a secret to a new compartment, policies configured for the compartment apply immediately and affect access to the secret and secret versions. Moving a secret doesn't affect access to the vault that a secret is associated with. Similarly, you can move a vault from one compartment to another independently of moving any of its secrets. For more information, see Managing Compartments.