// SECURE - Node.js with secure password management
const { Sequelize } = require('sequelize');
const { createClient } = require('redis');
const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager");
class SecureDatabaseConfig {
constructor() {
this.connections = {};
}
async initializeFromSecretsManager() {
const client = new SecretsManagerClient({ region: process.env.AWS_REGION });
try {
const command = new GetSecretValueCommand({
SecretId: "prod/database/credentials",
});
const response = await client.send(command);
const secrets = JSON.parse(response.SecretString);
return {
username: secrets.username,
password: secrets.password,
engine: secrets.engine,
host: secrets.host,
port: secrets.port,
dbname: secrets.dbname
};
} catch (error) {
console.error("Failed to retrieve database credentials:", error);
throw error;
}
}
async createPostgresConnection() {
const config = await this.initializeFromSecretsManager();
const sequelize = new Sequelize({
dialect: 'postgres',
host: config.host,
port: config.port,
database: config.dbname,
username: config.username,
password: config.password,
logging: process.env.NODE_ENV === 'development',
pool: {
max: 20,
min: 5,
acquire: 30000,
idle: 10000
},
dialectOptions: {
ssl: {
require: true,
rejectUnauthorized: process.env.NODE_ENV === 'production'
},
keepAlive: true,
statement_timeout: 30000,
idle_in_transaction_session_timeout: 60000
},
retry: {
max: 3,
match: [
Sequelize.ConnectionError,
Sequelize.ConnectionTimedOutError,
Sequelize.TimeoutError
]
}
});
// Test connection
await sequelize.authenticate();
console.log('Database connection established successfully.');
this.connections.postgres = sequelize;
return sequelize;
}
async createRedisConnection() {
// Use environment variable for AUTH token
const redis = createClient({
socket: {
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT) || 6379,
tls: process.env.NODE_ENV === 'production'
},
password: process.env.REDIS_AUTH_TOKEN, // Use AUTH token
database: parseInt(process.env.REDIS_DB) || 0,
reconnectStrategy: (options) => { // Use 'reconnectStrategy' for node-redis v4
if (options.error && options.error.code === 'ECONNREFUSED') {
return new Error('Redis connection refused');
}
if (options.total_retry_time > 1000 * 60 * 60) {
return new Error('Redis retry time exhausted');
}
if (options.attempt > 10) {
return undefined;
}
return Math.min(options.attempt * 100, 3000);
}
});
redis.on('error', (err) => console.error('Redis Client Error', err));
await redis.connect();
this.connections.redis = redis;
return redis;
}
// Password rotation handler
async rotatePasswords() {
console.log('Starting password rotation...');
// Close existing connections
for (const [name, conn] of Object.entries(this.connections)) {
if (conn.close) await conn.close();
if (conn.quit) await conn.quit();
}
// Reinitialize with new credentials
await this.createPostgresConnection();
await this.createRedisConnection();
console.log('Password rotation completed successfully');
}
}