recursive.codes

recursive.codes

recursive.codes


The Personal Blog of Todd Sharp

Oracle Functions - Connecting To An ATP Database With A Wallet Stored As Secrets

Posted By: Todd Sharp on 4/2/2020 11:00 GMT
Tagged: Cloud, Cloud, Containers, Microservices, APIs, Containers, Microservices, APIs, Database, Database, Java, Java

In my last post, we talked about how to create and use your first secret on the Oracle Cloud. It was a simple, but certainly common use case where we stored a DB password in the secret and retrieve that for use in a serverless function. You’ll often need to store simple text strings like passwords and API keys in secrets, but what about binary file contents like the kind that you would find in an Autonomous DB wallet?  Well, it turns out it is just as easy to use the secrets service to store these files as well, it just takes a minor extra step to write them to disk after they’ve been decrypted and before they can be used with your connections.

You may remember one of the previous entries on this blog that addressed this issue:

Note: This post represents the current best practice as it relates to connecting to an ATP Database and supersedes all previous guidance. 

Let me show you how to encode, encrypt, retrieve and decrypt your Autonomous DB wallet contents and use them in a serverless function to run a query against your instance. If you haven’t yet done so, make sure you are working in the console with a user in a group with the following permissions applied:

You’ll also need to apply the following service level policies:

Create Wallet Secrets

Since we’re dealing with a group of files - some of which contain text content and some of which contain binary content - the first thing we’re going to need to do is base64 encode all of them. I came up with the following bash script to make that task a bit easier and write them all out to a temporary directory so that I can grab them when I need to create the secret. The script just loops over the original wallet folder and calls base64 to encode the contents and write out to the temp file. 

Now it’s just a matter of heading to the Oracle Cloud console and creating a secret that represents each file. First I copy the encoded content like so:

Then I head to the console and create the secret, choosing ‘Base64’ as the ’Secret Type Template’ and pasting the encoded content. 

Make sure there are no extra line breaks or whitespace in the secret contents!

Create a secret for each of the wallet files as well as for your ATP DB password and copy the OCID of each secret for use later on.

If you prefer working with the CLI, you can also create your secrets via the OCI CLI. You’ll need the OCID of your vault, key and compartment to do so. Here’s an example of creating a secret in a vault encrypted by my demo-key containing the base64 encoded string “hunter2”:

Create Serverless Application & Function

Let’s create a serverless application to work with:

Next, create the function. If you’re new to serverless functions on the Oracle Cloud, check out the following videos:

Create your function:

Add the OCIDs of your secrets and your DB URL and username to the function configuration:

You should also set some environment variables to match these values for use locally when running tests.

We’ll need to use the OCI SDK, so add the following dependencies:

Don’t forget the OJDBC driver dependency:

If you’re using anything above Java 8, don’t forget the following dependency.

We’re going to use Resource Principal authentication in our function to work with the secret service, so make sure that you have a dynamic group and the proper policies in place. To do this, first create a dynamic group. I like to include all resources within a specific compartment in my dynamic group, so the definition would look like so:

Next, give the proper polices to the dynamic group (this can be applied at the tenancy or the compartment level):

Now let’s move on to the function handler. The full source code for this blog post is available on GitHub for reference. I’ve renamed my package and class here to be more appropriate. The first thing that we’ll need to do is declare some variables for use in our class. 

We need to declare a path to store our wallet files on the function’s Docker image. The only place we have write access to is the /tmp directory, so we’ll use that. We also need to store the DB username and URL so we’ll grab the values that we set into the function’s config. These are stored as environment variables and are accessible to our function at runtime.

Now declare some variables that we’ll use to store the decoded password and the secrets client:

Next, create a Map to store the OCIDs of all of our wallet files:

Let’s create our constructor where we’ll create our auth provider and construct an instance of the secrets client. We’ll also decrypt our DB password and set that into our variable for use later on. We’ll look in-depth at the getSecret()method shortly.

Now let’s start our handleRequest() function. The first thing we’ll do in this handler is check to see if our wallet exists on the local machine (it will persist across hot invocations of our function so we don’t have to recreate it every time):

The createWallet()function is simple:

Note that we’re looping over the Map of wallet files and writing the wallet file out to our temp directory. Our writeWalletFile() method will use the wallet file OCID to retrieve the wallet secret by calling that same getSecret()method that we used in the constructor above and writing that out to a file:

Finally, the getSecret()method uses the OCI SDK to retrieve the decrypted and base64 encoded secret, then decodes that base64 secret returning it as a byte array:

We’re now ready to modify our handleRequest() method to run our query (note that our DB URL pointed the TNS_ADMIN variable at our /tmp/wallet directory so we’re good to create a connection and run a query).  Here’s the rest of the handleRequest()method:

Which now will return a list of maps representing the rows returned by our query. We can add a test:

And deploy:

And invoke:

Summary

In this post, we looked at the most secure and reliable way to store Autonomous DB wallet contents in the Oracle Cloud. We also saw how easy it is to retrieve those secrets and use them in our applications using the OCI Java SDK. Remember, you’re not limited to serverless functions or using the Java SDK. Any application using any OCI SDK version can work just as easily with the secrets service in the Oracle Cloud.

For further reference, the code for this blog post can be viewed on GitHub.

Photo by Andrea Natali on Unsplash



Related Posts

Querying Autonomous Database from an Oracle Function (The Quick, Easy & Completely Secure Way)

Querying Autonomous Database from an Oracle Function (The Quick, Easy & Completely Secure Way)

I've written many blog posts about connecting to an Autonomous DB instance in the past. Best practices evolve as tools, services, and frameworks become...

Sending Email With OCI Email Delivery From Micronaut

Sending Email With OCI Email Delivery From Micronaut

Email delivery is a critical function of most web applications in the world today. I've managed an email server in the past - and trust me - it's not fun...

Brain to the Cloud - Part III - Examining the Relationship Between Brain Activity and Video Game Performance

Brain to the Cloud - Part III - Examining the Relationship Between Brain Activity and Video Game Performance

In my last post, we looked at the technical aspects of my Brain to the Cloud project including much of the code that was used to collect and analyze the...

Note: Comments are currently closed on this blog. Disqus is simply too bloated to justify its use with the low volume of comments on this blog. Please visit my contact page if you have something to say!