Setup and use OCI Centralized Configuration Providers for Oracle Database applications

Sharad Chandran
Oracle Developers
Published in
11 min readAug 28, 2024
Photo by Ferenc Almasi on Unsplash

Centralized Configuration Providers aka “Config Stores” allow configuration details (including Database Configuration) to be stored in a centralized location (usually on the cloud), effectively removing the burden of configuration management out of the application. Config Stores make the same configuration accessible to multiple applications or components.

Centralized Configuration offers benefits of :

  • Improved manageability: Configuration changes can be made in a single location, that can be quickly picked up by dependent applications.
  • Enhanced security: Sensitive information can be encrypted or secured with access controls.
  • Dynamic Configuration: The configuration details can be updated, without redeploying or recoding the applications.

Oracle Cloud Infrastructure (OCI) now also provides Centralized Configuration Provider capabilities, which can be leveraged by most of our Oracle Database drivers (e.g., Node.js, Java, C/C++).

In this blog, I will walk through the process of setting up an Oracle Database configuration on OCI and then using a Node.js app to connect to the Oracle Database via the Centralized Configuration Provider setting.

The blog is split into two sections:

Prerequisites

  • Create a new Oracle Cloud Account or use an existing one. You can quickly create an ‘Always Free’ account for no cost. There are other posts and documentation that show this, such as this blog.
  • Create an IAM User (say sharadraju) in your Oracle Cloud account for accessing the Oracle Database configuration details from the Centralized Configuration Provider.

Create OCI Storage JSON File

Once the prerequisites are met, the next step is to create an OCI Storage JSON file which stores all the database configuration details. Here is a sample OCI Storage JSON file:

{
"connect_descriptor": "(description=(address=(protocol=tcp)(host=hostname.domain.com)(port=2345))(CONNECT_DATA=(SERVICE_NAME=svc_name.domain.com)))",
"user": "user1",
"password": "password1",
"sales_app1":
{
"connect_descriptor": "tcp://hostname.domain.com:2345/svc_name.domain.com",
"user": "user2",
"password": "password2"
}
}

Save this file as basicConfig on your machine.

The connect_descriptor sub-object contains the Oracle Database connect string and is mandatory. The user and password sub-objects can be used specify the database user name and database password. Note that the password is stored as plain text here. It is advisable to store the password in a password vault (which is described in a later section).

The sub-object sales_app1 holds another set of database credentials, which can be used for a different configuration.

Store OCI Storage JSON File in an OCI Bucket

Then, the OCI Storage JSON File should be stored in specialized object containers called buckets.

Login to Oracle Cloud and go to the OCI Console. Navigate to the Storage>>Object Storage & Archive Storage>>Buckets page.

‘Buckets’ Page

Note the OCI compartment where the bucket is created (in this case, it is the ‘root’ compartment). You can create your own child compartments under the root compartment and create the buckets and password vaults in the child compartment for more organized and secure access.

Click on the Create Bucket button, if a bucket does not exist. The ‘Create Bucket’ form pops up.

‘Create Bucket’ Form

Fill in the bucket name and other details (as shown in the screenshot above) and click on the Create button.

Make sure that new bucket is created.

Updated ‘Bucket’ page

Click on the newly created bucket link highlighted above. The ‘Bucket Details’ page opens up.

‘Bucket Details’ page

Click the Upload button in the Objects section. The ‘Upload Objects’ form comes up.

‘Upload Objects’ Form

Select the OCI Storage JSON file created earlier (basicConfig) from your local machine. Once the file is uploaded, click on the Upload button at the bottom of the form.

Ensure that the OCI Storage JSON file is uploaded in the bucket.

OCI Storage JSON File uploaded
‘View Object Details’ Menu

Click on the three dots at the end of the row containing the ‘basicConfig’ information in the Objects section. Select View Object Details from the menu that comes up.

Object URL

Now save the URL details of the stored OCI Storage JSON file as highlighted in the screenshot above.

Add Policies for IAM Users to Access the Buckets

In the OCI Console, Navigate to the Identity>>Policies page.

Create policies for IAM Users group (IAMUsers). I had created a earlier policy for my existing IAM User (sharadraju) as detailed here.

In the same policy, you can add new policy statements to allow the IAM User to access the bucket created in the earlier section.

‘Policy Details’ page

Click on the Edit Policy Statements button.

Policy Statements

Add the following policy statements:

Allow group IAMUsers to manage objects in tenancy
Allow group IAMUsers to manage buckets in tenancy

This will allow the IAM Users group to manage objects and buckets in all the compartments in your cloud tenancy (including your ‘root’ compartment).

If you want to restrict the IAM Users to manage buckets and objects in a specific child compartment (say testCompartment), then add the following policy statements:

Allow group IAMUsers to manage objects in compartment testCompartment
Allow group IAMUsers to manage buckets in compartment testCompartment

Click on the Save Changes button.

Create Application to Access Config Store details

Ensure that your IAM User is configured in your machine (via OCI-CLI) and the IAM User configuration details such as tenancy, ocid and fingerprint are available in the OCI config file.

To connect to the database specified by the basicConfig OCI JSON Storage file configuration, you can create the following Node.js application:

'use strict';
Error.stackTraceLimit = 50;
const oracledb = require('oracledb');

async function run() {
let connection;
const options = {
// Replace the connect string here with URL of the 'basicConfig' file
// config-ociobject://<object_store_server_name>/n/{namespace}/b/{bucketname}/o/{filename}[/c/{network_service_name_or_alias}][?option1=value1&option2=value2...]
connectString: 'config-ociobject://objectstorage.region.oraclecloud.com/n/testNamespace/b/orantdev/o/basicConfig',
};
try {
// Get a non-pooled connection
connection = await oracledb.getConnection(options);

console.log('Connection was successful!');

} catch (err) {
console.error(err);
} finally {
if (connection) {
await connection.close();
}
}
}

run();

The URL of the basicConfig file to be provided as a value to connectString can be obtained here.

Save the application as a JavaScript file (say ociConfigProviderSample.js).

Ensure that npm and Node.js are installed on your machine and then install the node-oracledb npm package (version 6.6) and the required OCI SDK components as follows:

$ npm install oracledb
$ npm install oci-common
$ npm install oci-objectstorage

Then run the application:

$ node ociConfigProviderSample.js

You should be able to connect to the default database configuration provided in the basicConfig file.

If you want to connect to the ‘sales_app1’ database configuration within the basicConfig file, you have to change the connectString value in the application as follows:

const options = {
// Replace the connect string here with URL of the 'basicConfig' file
// config-ociobject://<object_store_server_name>/n/{namespace}/b/{bucketname}/o/{filename}[/c/{network_service_name_or_alias}][?option1=value1&option2=value2...]
connectString: 'config-ociobject://objectstorage.region.oraclecloud.com/n/testNamespace/b/orantdev/o/basicConfig/c/sales_app1',
};

You should get the following output on a successful database connection:

Connection was successful!

For more details on all the parameters in the OCI Centralized Configuration Providers supported by node-oracledb, please check the node-oracledb documentation.

Use OCI Password Vaults for Secure Password Storage and Access (optional)

The example above used the password directly from the OCI Storage JSON File.

It is recommended to store the password in password vaults for more secure storage and access. Oracle allows you to access password from both OCI and Microsoft Azure password vaults using Config Stores.

Here, I will use an OCI Vault for storing the database password.

Create Password Vaults

Oracle Cloud Infrastructure (OCI) Vault is a managed cloud service, that provides a secure store for secrets. You can store passwords, certificates, SSH keys, or authentication token, that you use in configuration files, as secrets in OCI Vault. This blog gives a good introduction on the usage and creation of OCI Vaults.

Login back to your OCI Console and navigate to Identity & Security >> Vault

Create Vault

Click on the Create Vault button. You can create the vault in any compartment. I have chosen my root compartment for convenience.

The ‘Create Vault’ Form pops up.

‘Create Vault’ Form

Fill in the vault name (say KeyVault1) and click on the Create Vault button at the bottom of the page.

Vault Home Page with the newly created key vault

Ensure that KeyVault1 is created as shown above. Click on KeyVault1.

Vault Details Page

In the Vault Details page that opens up, note down the vault OCID from the General Information section.

Create Encryption Keys

You need to create an Encryption Key to encrypt your password.

To create an encryption key, select Master Encryption Keys, under Resources sub-section on the bottom left. Then click on Create Key button in the Master Encryption Keys section as shown in the screenshot above.

The ‘Create Key’ pop-up is displayed on the OCI Console.

‘Create Key’ page

Set a Name to identify the key, say EncryptionKey.
Select Protection Mode (HSM or Software) that decides where the encryption key persists and cryptographic operations processed. I chose ‘HSM’ (Hardware Secure Module) for extra security.

Leave the other fields as is. Click the Create Key button at the bottom of the page.

Encryption Key Created

Ensure that ‘EncryptionKey’ is listed in the Master Encryption Keys section.

Add password to the OCI vault

The final step is to add the password as a secret in the OCI Vault.

Secrets section

To start with, select Secrets, under Resources sub-section on the bottom left.

Then click on the Create Secret button in the Secrets section.

‘Create Secret’ Form

In the ‘Create Secret’ form that pops up, fill in the following details:

  • Name: Unique name for the password
  • Description: Description about the password
  • Encryption Key: Select the Encryption Key created in the previous step (i.e. EncryptionKey). This key will be used to encrypt the secret.
  • Manual secret generation: Select this option to manually provide the password.
  • Secret Type Template: Plain-Text or Base64. You can provide secret contents in plain-text when you use the Console to create a secret or secret version, but secret contents do need to be base64-encoded before they’re sent to the service. The Console automatically encodes plain-text secret contents for you if you choose this format. Select Base64 to avoid confusion.
  • Secret Content: Provide the database password here. Please ensure that the password is strong and secure. The password shown in the screenshot is for demo purposes only and not a recommended password.

Leave all the other fields as-is and click the Create Secret button.

Ensure that the password secret is created successfully in the OCI Vault.

Password Secret page

Note down the password OCID (highlighted above) as it will be used in the OCI Storage JSON File later.

Add IAM Policies to access the OCI Vault

Next, we need to add policies that will allow the IAM Users to access the OCI Vaults.

In the OCI Console, Navigate to the Identity>>Policies page.

I am going to re-use the existing policy I had for my IAM User group that I described earlier(IAMUsers).

In the same policy, you can add new policy statements to allow the IAM User to access the OCI Vaults.

Add the following policy statements:

Allow group IAMUsers to manage keys in tenancy where target.vault.id='ocid1.vault.oc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Allow group IAMUsers to manage vaults in tenancy where target.vault.id='ocid1.vault.oc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Allow group IAMUsers to manage secrets in tenancy where target.vault.id='ocid1.vault.oc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Allow group IAMUsers to manage secret-family in tenancy where target.vault.id='ocid1.vault.oc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Allow group IAMUsers to manage secret-bundles in tenancy where target.vault.id='ocid1.vault.oc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

Use the ocid for the OCI Vault created in the earlier section (KeyVault1) for the target.vault.id parameter here.

This will allow the IAM Users group to manage the vault, irrespective of the compartment it is located in(including your ‘root’ compartment).

If you want to restrict the IAM Users to manage vaults only from a specific child compartment (say testCompartment), then add the following policy statements:

Allow group IAMUsers to manage keys in compartment TestCompartment where target.vault.id='ocid1.vault.oc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Allow group IAMUsers to manage vaults in compartment TestCompartment where target.vault.id='ocid1.vault.oc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Allow group IAMUsers to manage secrets in compartment TestCompartment where target.vault.id='ocid1.vault.oc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Allow group IAMUsers to manage secret-family in compartment TestCompartment where target.vault.id='ocid1.vault.oc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Allow group IAMUsers to manage secret-bundles in compartment TestCompartment where target.vault.id='ocid1.vault.oc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

Click on the Save Changes button.

Create OCI Storage JSON File with OCI Vault Details

Create an OCI Storage JSON file with OCI vault details and add it to the OCI Bucket as discussed earlier. Here is a sample OCI Storage JSON file with OCI vault details:

{"connect_descriptor": "localhost/orclpdb",
"user": "scott",
"password": { "type": "vault-oci",
"value": "ocid1.vaultsecret.oc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}

Under the password sub-object, the type is set to ‘vault-oci’ as this is the type recognized by the node-oracledb driver. Other drivers like JDBC and ODP.NET use ‘ocivault’ as the type value. So this value may change in future.

For the value key under the password sub-object, use the Password Secret OCID that was noted in the earlier section.

Save it as passwordVaultConfig in the bucket.

Password Vault Config JSON File

Note the URL for the passwordVaultConfig file.

Create and Run an Application

Install the node-oracledb npm package (version 6.6) and the required OCI SDK components as follows:

$ npm install oracledb
$ npm install oci-common
$ npm install oci-objectstorage
$ npm install oci-secrets

As before, create and run the Node.js application to access the database configuration from the passwordVaultConfig file.

'use strict';
Error.stackTraceLimit = 50;
const oracledb = require('oracledb');

async function run() {
let connection;
const options = {
// Replace the connect string here with URL of the 'passwordVaultConfig' file
// config-ociobject://<object_store_server_name>/n/{namespace}/b/{bucketname}/o/{filename}[/c/{network_service_name_or_alias}][?option1=value1&option2=value2...]
connectString: 'config-ociobject://objectstorage.region.oraclecloud.com/n/testNamespace/b/orantdev/o/passwordVaultConfig',
};
try {
// Get a non-pooled connection
connection = await oracledb.getConnection(options);

console.log('Connection is successful!');

} catch (err) {
console.error(err);
} finally {
if (connection) {
await connection.close();
}
}
}

run();

You should get the following output on a successful database connection:

Connection is successful!

Conclusion

That’s it!

I hope this blog was quite comprehensive and useful in helping you get started with using Oracle’s Centralized Configuration Providers. The steps followed in this blog can be generalized and used with any other supported programming languages like Java (JDBC) , C/C++ (Oracle Call Interface C/C++ libraries) etc.

The Centralized Configuration Providers also allow you to create and store application-specific parameters (e.g., application pool parameters), that can be used by Oracle Database drivers like node-oracledb. I will cover that in a short and separate blog.

Give the Centralized Configuration Providers a try and let me know how it goes!

--

--

Sharad Chandran
Oracle Developers

Sharad Chandran is a Principal Product Manager at Oracle driving development of client interfaces & APIs for Oracle Database in C/C++, Python, Node.js, Go etc.