Setup and use OCI Centralized Configuration Providers for Oracle Database applications
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:
- Accessing Oracle Database through a basic configuration (with plain text password) stored in the OCI Config Store file
- Accessing Oracle Database through a password vault-based configuration stored in the OCI Config Store file
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.
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.
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.
Click on the newly created bucket link highlighted above. The ‘Bucket Details’ page opens up.
Click the Upload
button in the Objects section. The ‘Upload Objects’ form comes up.
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.
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.
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.
Click on the Edit Policy Statements
button.
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
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.
Fill in the vault name (say KeyVault1) and click on the Create Vault
button at the bottom of the page.
Ensure that KeyVault1 is created as shown above. Click on KeyVault1.
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.
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.
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.
To start with, select Secrets, under Resources sub-section on the bottom left.
Then click on the Create Secret
button in the Secrets section.
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.
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, thetype
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 thetype
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.
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!