X

Best Practices from Oracle Development's A‑Team

Secure storage of confidential configuration data in Oracle Functions using Oracle OCI Key Management Services

Angelo Santagata
Architect
table td {     border-width: 1px;     border-color: black;     border-style: solid;     border-spacing: 1px 1px;     padding: 10px;     background-color: white; }

Introduction

Whilst building a demo recently I had to store an oAuth client secret somewhere for Oracle Function to work properly. I immediately decided to store it in the Oracle Functions configurations section and thought all was well.

The below image shows the configuration section for my application and as you can see both the IDCS client id and client secret are stored.

However after a security review with my good friend (Olaf) we realised that whilst the data is relatively secure (i.e. Only someone who has access to the compartment could see it), its not really that secure as the oAuth secret is displayed in cleartext without any obfuscation. For stuff which is security related, e.g. oAuth Client Secrets,  it would be nice to be able to store it "securely" in a way which is encrypted.

Oracle Key Management Service is the Solution

The answer to my problem is to use the Oracle Key Management service to encrypt the data (the oAuth Client ID), store this encrypted data in Oracle Functions and then at runtime reverse the process and decrypt the data on the fly.

Setting this up is relatively easy, first step to do is to navigate to the Oracle Key Management Service (Menu/Security/Key Management) and create a new KMS Vault.

Once the vault is created you can create a "key" in the vault. This "key" is not the encrypted data but the key to which is used to encrypt, and subsequently decrypt, the data.

Once the vault is created you need to note down the following data : 

- The Key's OCID

- The Management Endpoint URL

Now we can encrypt our data using either code (Python/Java etc) or we can do this from the OCI CLI.

The below script encrypts the data can encrypt the data..

echo "Please enter the KMS Endpoint URL"
read ENDPOINT
echo "Please enter the KMS Key OCID"
read KEY
echo "Please enter the text you wish to encrypt"
read PLAIN_TEXT
echo "Encrypted Text"
oci kms crypto encrypt --key-id "$KEY" --endpoint "$ENDPOINT" --plaintext "$( echo $PLAIN_TEXT | base64 -w0 )"

You must have a installed the OCI CLI for the above shell script to work

 

If you run this with your data you will be greeted with a JSON response containing the encrypted text.

Please enter the KMS Endpoint URL
https://mycryptokms-crypto.kms.us-phoenix-1.oraclecloud.com
Please enter the KMS Key OCID
ocid1.key.oc1.phx.11111222.aaaaaabbbbbbbbbbdddddddddeeeeeeefffffff
Please enter the text you wish to encrypt
helo
Encrypted Text
{
  "data": {
    "ciphertext": "ITNi/++xxxxxx/gT/ssssss/Febc+zQ/wwwwwwwwww="
  }
}

You can now cut-n-paste the encrypted text (JSON attribute ciphertet) in the Function configuration using either the UI or using the command line.

e.g. Setting the configuration value via the command line

fn config app cloudnativesaas myEncryptedIDCSClientSecret ITNi/++xxxxxx/gT/ssssss/Febc+zQ/wwwwwwwwww

 

Getting the Decrypted Data and using it in Functions

OK, now from within your function you need to query the KMS Service and get it to decrypt your encrypted key so you can use it in your code.

For this to work you will need to ensure that your application has the OCI library available, in my case I was using Java and the library was available using the maven repository. I used the following maven coordinates to allow my Java app to use the crypto services.

        <dependency>
            <groupId>com.oracle.oci.sdk</groupId>
            <artifactId>oci-java-sdk-keymanagement</artifactId>
            <version>1.12.2</version>
        </dependency>
        <!-- Need the activation library if using Java 11 -->
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>javax.activation-api</artifactId>
            <version>1.2.0</version>
        </dependency>

Here is a sample snippet of Java code which gets the encrypted value from functions configuration section and then calls the KMS services to decrypt it.


    @FnConfiguration
    public ResourceServerConfig(RuntimeContext ctx)   {
        
        // Config data for IDCS oAuth App
        IDCS_URL = ctx.getConfigurationByKey("idcs_app_url").orElse("NOTSET");
        SCOPE_AUD = ctx.getConfigurationByKey("idcs_app_scopeid").orElse("NOTSET");
        CLIENT_ID = ctx.getConfigurationByKey("idcs_app_clientid").orElse("NOTSET");

        // KMS Key for IDCS Client Secret
        KMSEndpoint   = ctx.getConfigurationByKey("kms_endpoint").orElse("NOTSET");
        KMSKeyOCID    = ctx.getConfigurationByKey("kms_idcs_secret_key").orElse("NOTSET");
        EncryptedText = ctx.getConfigurationByKey("idcs_app_secret").orElse("NOTSET");
         
        LOGGER.info("Decrypting key");
        AbstractAuthenticationDetailsProvider provider = null;
        provider = ResourcePrincipalAuthenticationDetailsProvider.builder().build();
        KmsCryptoClient cryptoClient = KmsCryptoClient.builder().endpoint(KMSEndpoint).build(provider);
        DecryptDataDetails decryptDataDetails = DecryptDataDetails.builder().keyId(KMSKeyOCID).ciphertext(EncryptedText).build();
        DecryptRequest decryptRequest = DecryptRequest.builder().decryptDataDetails(decryptDataDetails).build();
        DecryptResponse decryptResponse = cryptoClient.decrypt(decryptRequest);
        String decryptedDEK = decryptResponse.getDecryptedData().getPlaintext();
        CLIENT_SECRET =  new String (Base64.getDecoder().decode(decryptedDEK.getBytes()));
        LOGGER.info("oAuth Client Secret Decrypted");
    }

The above snippet of code,queries all the parameters, including the client secret key out of the Functions configurations , calls the KMS Services to decrypt the keys and then sets the variables appropriately.

Setting IAM Permissions

Finally for the above to work you need to ensure that the right permissions are granted at the OCI IAM level.

The Following IAM Security Policies need to be setup

Policy Type    Policy    Usage
Policy  ALL {resource.type='fnfunc', resource.compartment.id='YOUR_COMPARTMENT_OCID'} Dynamically defines all Function instances within the given compartment
Policy  Allow dynamic-group FN_DYN_GRP to manage vaults in compartment Allow access the KMS Vaults
Policy  Allow dynamic-group FN_DYN_GRP to manage keys in compartment Allow access the keys in KMS
Policy  Allow dynamic-group FN_DYN_GRP to manage key-delegate in compartment | Allow access the key delegates in KMS |

 

Conclusion

As per always when developing code think about security and always store your secret stuff "securely"!

 

 

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha