Retrieve environment wise database configurations without revealing it
Why Database configuration should not be stored in the application?
In most of today's nodejs applications, we have seen that database configuration is stored in the json file inside the application. We have also seen that environment-wise database configuration is maintained to access a particular database as per the environment e.g. development.json will have database configuration which will be run on a development environment.
Even these configurations are git ignored, it can be easily vulnerable by the users who have the right code access and also it’s a big headache to maintain it in different environments. So it’s really necessary to avoid this vulnerability by maintaining configurations in an encrypted way.
Let’s check it out, how can we access this environment wise configurations in a secure way.
Maintain a single master database
We need to maintain a single master database for storing these configurations in an encrypted format and can be accessed using a key. In my case, I am using PostgreSQL which provides the `pgcrypto` extension for cryptography.
Note: Even if master database is accessible inside the code, no one will be able to decrypt your configurations without encryption key.
So let’s create a database:
-- create database
CREATE DATABASE master_db_config;
Now, let’s have a table to store configurations:
-- create table
CREATE TABLE app_config(
id SERIAL PRIMARY KEY, -- auto increment id
config bytea, -- encrypted config
env varchar(30), -- environment e.g. dev
project_key varchar(30), -- unique project key
is_active bool default true,
createdon timestamp,
updatedon timestamp
);
Here, id represents the auto increment id, config is the encrypted data column, env can be development, test, production, etc. project_key is a unique project identifier.
Now, let’s activate the `pgcrypto` extension using:
-- this is required for running encrypt and decrypt functions
CREATE EXTENSION pgcrypto;
Let’s insert our first config for the development environment:
INSERT INTO app_config(config, env, project_key, createdon, updatedon) VALUES ( encrypt( '{"host":"localhost","port":"5432","database":"myDb","user":"postgres","password":"root","max":"10","idleTimeoutMillis":"30000"}', 'ml9gi2r5ce275y3i8sxq', 'aes'), 'development', 'MY_WORST_PROJECT', '2018-09-29 10:01:00', '2018-09-29 10:01:00' );
So if you check this insert query, encrypt function is passed as value and JSON config is given as an argument to it. `ml9gi2r5ce275y3i8sxq` is the key to encrypt the config, you can pass any random string to encrypt it. aes is the encryption algorithm.
Note: You need to store this key somewhere safely as you won’t be decrypt your config without key.
Here we finish our master DB setting. So let’s move on to the code.
Let’s code it
I have created the library for easily accessing your configurations form Postgres, so, first of all, add it to your node modules using the following command:
npm install --save https://github.com/pravindot17/configprovider
Now you have this as a module, so let’s use it in your application
let configProvider = require('configprovider');
let masterDbConfig = {
"host":"localhost",
"port":"5432",
"database":"master_db_config",
"user":"postgres",
"password":"root",
"max":"10",
"idleTimeoutMillis":"30000"
}
// init the connection in your bootstrap file using following code
configProvider.init(masterDbConfig, 'MY_SECRET_KEY', 'development', 'MY_WORST_PROJECT').then((appConfig) => {
console.log('received the db config here in appConfig.dbConfig');
}).catch(console.error);
MY_SECRET_KEY: This is the secret key that was used while inserting the database configuration in master DB. We can provide this key in node ENV during deployment or running your application. e.g. If you are running your application in development we can pass DB_KEY=’MY_SECRET_KEY’ in npm start command as below:
NODE_ENV="development" DB_KEY="MY_SECRET_KEY" node app.js
And access it in your code using process.env.NODE_ENV
and process.env.DB_KEY
In this way, we will get our database configuration in our bootstrap file mostly app.js. To use it anywhere in the application, just use:
// to use it inside your application use following
let dbConfig = configProvider.getConfig().dbConfig;
Note: This library is using module caching so every time we require this library in application, it’s not going to hit master database except in bootstrap file where init function is called.
I have also added some functions so that existing application configuration can be added to it, so that the entire application and database configuration will be easily accessible across the application.
// You can also add more config to the cache
configProvider.addConfig('appConfig', {"title": "My worst application", "isEmailRequired": true});// You can also merge config into existing config
configProvider.mergeConfig({"smsConfig": { "isEnabled": true, "content": "Have a good day!" }});
Conclusion
In this way, we can retrieve our database configurations easily without compromising our database connections.