Sequelize authenticate using Managed Identity to Azure postgres - node.js

Facing a connection error when trying to connect to Azure postgres instance using azure Managed identity. My app is developed on Typescript and I am using Sequelize as the ORM (sequelize npm v6.6.2). I am using the following config to establish connection to the database but this fails to authenticate.
config: Options = {
dialect: 'postgres',
logging: false,
dialectOptions : {
authentication: {
type: 'azure-active-directory-msi-vm',
options: {
clientId: <AZURE_CLIENT_ID>,
},
},
}
}
.
.
.
sequelize = new Sequelize(<DB_URL>, config)
I was able to successfully connect if I use password, by just replacing the dialectOptions with 'password'.
I have verified that I am able to connect to a Azure SQL Server instance using the below configuration without an issue.
config: Options = {
database: <AZURE_SQL_DB>,
host: <AZURE_SQL_SERVER>,
dialect: 'mssql',
logging: false,
dialectOptions: {
authentication: {
type: 'azure-active-directory-msi-vm',
options: {
clientId: <AZURE_CLIENT_ID>,
},
},
},
}
.
.
sequelize = new Sequelize(config)
NOTE: For this to work for MSSQL, I had to set the user-assigned managed identity as the 'Azure Active Directory Admin' in the server settings. So I have set it for the postgres instance as well.
Can someone tell me what I am doing wrong here, or is this capability not available yet on Azure Postgres instances?

Related

How do I use Azure AD Authentication with node-mssql?

So I got a connection working using tedious but the options available in node-mssql for handling JSON are something really useful that I would like to have access to.
The documentation for node-mssql says you can pass an object with authentication settings that tedious would use and it will override the user/password properties but it's definitely not doing that. I can confirm because the error message comes back with the value for the user property.
Is there something wrong with the config object?
const sqlConfig = {
server: process.env.SQL_SERVER,
database: process.env.DATABASE_NAME,
user: "root.user",
password: "password",
options:
{
encrypt: true,
authentication: {
type: "azure-active-directory-password",
options: {
userName: "root.options.authentication.options.userName",
password: "password"
}
},
}
};
Here's the example using node-mssql and azure-active-directory-password(supports Azure AD from tedious#4.1.0):
const config = {
server: 'yoursqlserver.database.windows.net',
database: 'yourdb',
authentication: {
type: "azure-active-directory-password",
options: {
userName: "bob#contoso.com",
password: "password",
}
}
}
Ref here:
https://stackoverflow.com/a/58386825/10549281
https://stackoverflow.com/a/56145320/10549281.
https://github.com/tediousjs/tedious/issues/416#issuecomment-441364272
So there are packages for mssql and node-mssql. I installed node-mssql but was working with the documentation for mssql. I am an idiot.
Thanks to everyone who looked at this and especially Leon.

Nodejs mssql server connection throws an error

I want to connect my mssql database from nodejs.
I am using sequelize and tedious module for this.
My connection configs like this
const sequelize = new Sequelize(
"mydb",
"username", "password", {
host:config_data.host,
"dialect":"mssql",
"port":1433
dialectOptions: {
instanceName: "MSSQLSERVER"
},
},
);
When I tried to run script, it throws an error.
(node:14584) UnhandledPromiseRejectionWarning: SequelizeConnectionError: Failed to connect to 192.168.10.220:1433 - Could not connect (sequence)
I apply all steps in configuration manager.
Enabled TCP/IP
Started Server SQL browser
Added 1433 port to Firewall
There is also additional error when host is "localhost".(Currently writed IP address)
(node:13552) UnhandledPromiseRejectionWarning: SequelizeConnectionError: Failed to connect to localhost:1433
- 4292:error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocol:c:\ws\deps\openssl\openssl\ssl\statem\statem_lib.c:1947:
I need to help, is there someone have any idea ?
You need to set encrypt to false in your Sequelize inside options.
const sequelize = new Sequelize(
"mydb",
"username", "password", {
host:config_data.host,
"dialect":"mssql",
"port":1433,
"options": {
encrypt: false,
enableArithAbort: false
},
dialectOptions: {
instanceName: "MSSQLSERVER"
},
},
);
The issue is because of TLS protocol mismatch between Source & Destination server. In my case my App server was Ubuntu 20.04 & my SQL Server(Express 2012) was on Windows server. I also tried to downgrade TLS protocols on Ubuntu but nothing worked. So finally I disabled tls encryption in Sequelize.
Make sure options are given as expected by Sequelize
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
host: dbConfig.HOST,
port: dbConfig.PORT,
dialect: dbConfig.dialect,
pool: {
max: dbConfig.pool.max,
min: dbConfig.pool.min,
acquire: dbConfig.pool.acquire,
idle: dbConfig.pool.idle,
},
// The below options are important to supress ssl issue on
// AWS EC2 ubuntu when db server is on windows. There is TLS protocol issue
// Which by using these options we disable tls encryption
dialectOptions: {
// Observe the need for this nested `options` field for MSSQL
options: {
encrypt: false,
enableArithAbort: false
}
}
});

Strapi giving me DB errors in production, even though I'm using correct credentials

EDIT: I found a file at /config/database.js which is used to connect to sqlite in development. When I change the client name from sqlite to postgres, that's when the trouble starts.
Isn't strapi supposed to ignore files like this in production? How can I get strapi to ignore this file, and just use my postgres db?
module.exports = ({ env }) => ({
defaultConnection: 'default',
connections: {
default: {
connector: 'bookshelf',
settings: {
client: 'sqlite',
filename: env('DATABASE_FILENAME', '.tmp/data.db'),
},
options: {
useNullAsDefault: true,
},
},
},
});
End edit.
I'm trying to get my strapi app to start up in production, but it keeps erroring out saying
[2020-07-22T01:15:40.246Z] debug ⛔️ Server wasn't able to start properly.
[2020-07-22T01:15:40.247Z] error error: password authentication failed for user "<redacted>"
The rest of the output is related to pg, which leads me to think that this is a DB connection error.
I can log into my db from the command line using psql -U postgres -W, which confirms that I know my password.
In addition, I'm using pm2 to run things, and instead of using process.env in that file, I just added the db variables directly, but that made no difference.
The application has been built in production mode. I have 3 dbs in pg, one called postgres, one with my apps name, and another called strapi.
Thanks
my /config/enviroronments/production.database.json looks like this
{
"defaultConnection": "default",
"connections": {
"default": {
"connector": "bookshelf",
"settings": {
"client": "postgres",
"host": "${process.env.DATABASE_HOST || '127.0.0.1'}",
"port": "${process.env.DATABASE_PORT || 27017}",
"database": "${process.env.DATABASE_NAME || 'strapi'}",
"username": "${process.env.DATABASE_USERNAME || ''}",
"password": "${process.env.DATABASE_PASSWORD || ''}"
},
"options": {
"ssl": false
}
}
}
}
and I have a .env file at the root of the backend app that looks like this
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_NAME="<redacted - all letters>"
DATABASE_USERNAME="<redacted - all letters>"
DATABASE_PASSWORD="<redacted - all letters>"
Found the issue. When I created the app, I used sqlite as my db. As a result, the default database.js file wasn't set up in a way that could be overwritten with env variables.
I created a new local Strapi app with pgsql as my db, and copied the contents of the database.js file to my server. All working now.
New file for reference
module.exports = ({ env }) => ({
defaultConnection: 'default',
connections: {
default: {
connector: 'bookshelf',
settings: {
client: 'postgres',
host: env('DATABASE_HOST', '127.0.0.1'),
port: env.int('DATABASE_PORT', 5432),
database: env('DATABASE_NAME', 'my-strapi-project'),
username: env('DATABASE_USERNAME', 'testing'),
password: env('DATABASE_PASSWORD', 'testing'),
ssl: env.bool('DATABASE_SSL', false),
},
options: {}
},
},
});
I had the same situation in development. I created a strapi app with SQLite and decided to use PostgreSQL. That's where the trouble came in. So the fix was as follows:
app_name/config/database.js
module.exports = ({ env }) => ({
connection: {
client: 'postgres',
connection: {
host: env('DATABASE_HOST', '127.0.0.1'),
port: env.int('DATABASE_PORT', 5432),
database: env('DATABASE_NAME', 'db_name'),
user: env('DATABASE_USERNAME', 'postgres'),
password: env('DATABASE_PASSWORD', 'postgres'),
ssl: env.bool('DATABASE_SSL', false),
},
},
});
Your dependencies under app_name/package.json should be like
"dependencies": {
"#strapi/strapi": "4.1.8",
"#strapi/plugin-users-permissions": "4.1.8",
"#strapi/plugin-i18n": "4.1.8",
"pg": "8.6.0"
}
[2023-02-19 11:27:27.197] debug: ⛔️ Server wasn't able to start properly.
[2023-02-19 11:27:27.199] error: password authentication failed for user "root"
FIX==>
su - postgres
psql postgres
CREATE ROLE root SUPERUSER LOGIN PASSWORD 'password';
The point of interest here is the module used with strapi .
configuration file database.js
module.exports = ({ env }) => ({
defaultConnection: "default",
connection: {
client: "postgres",
connection: {
host: "127.0.0.1",
port: 5432,
database: "dbname",
username: "postgres",
password: "password",
ssl: false
},
debug: true,
useNullAsDefault: true
}
});
version package.json
"#_sh/strapi-plugin-ckeditor": "^2.0.3",
"#strapi/plugin-i18n": "4.6.1,",
"#strapi/plugin-users-permissions": "4.6.1,",
"#strapi/strapi": "4.6.1,",
"better-sqlite3": "8.0.1",
"pg": "8.6.0"
check version
/etc/postgresql/{{version-postsql}}/main/pg_hba.conf
local replication all peer
host replication all 127.0.0.1/32 md5
host replication all ::1/128 md5
host all postgres 127.0.0.1/32 trust
host all all ::1/128 trust
restart postgresql
sudo systemctl restart postgresql.service
su - postgres
psql
DROP root;
CREATE ROLE root WITH SUPERUSER CREATEDB CREATEROLE LOGIN ENCRYPTED PASSWOR 'password......';
CREATEDB dbname;
I don't know why it took the initial role of root but the above simple solution worked for me

How to set up postgresql config with heroku and nodejs?

This is my first time try to host nodeJS application - built with hapi.js, typeorm and postgresql - on heroku. I've create two apps on heroku - for "staging" (server-staging) and "production" (server-prod) - that using same code but will use different configuration. Why different configuration? because each application on heroku will use different postgres credential, as it's attached as an add-ons.
objective
My objective/main question is How and where I have to set the database config for my application?
I use .env file (which I ignore in .gitignore - I don't want to put the credential in my repo) to connect the application to my local database. Here is how the .env looks like:
HOST=localhost
PORT=3001
TYPEORM_CONNECTION=postgres
TYPEORM_HOST=localhost
TYPEORM_USERNAME=postgres
TYPEORM_PASSWORD=password
TYPEORM_DATABASE=database
TYPEORM_PORT=5432
TYPEORM_SYNCHRONIZE=true
TYPEORM_LOGGING=false
In the application, I never do/write code such process.env.TYPEORM_USERNAME since its done by the typeorm node_modules. What I do to start the connection is by doing this:
const server = new Hapi.Server({
port: process.env.PORT,
host: process.env.HOST,
routes: {
cors: Cors,
},
});
await server.register(Plugins);
server.route(Router);
await createConnection();
await server.start();
And my application automatically connected to the specified database as defined in the .env. Now, in heroku, the credential is lies here:
All information lies there, but, [Q1] I don't know how to tell my application (of course, without store the credential in my code/repo) that I have to use the config as defined in above picture? Also, as stated in above image, "Heroku rotates credentials periodically and updates applications where this database is attached.". Does it means the credentials will changed periodically? [Q2] If yes, is there any way to make my application auto recognise the new credential?
Sorry if my explanation make confused. If you did not understand what I am trying to achieve, please ask things that you don't understand, so I can fix/update my question to make it understandable.
Anyway, I found this example first-example and second-example. But, they are using process.env.DATABASE_URL, which contain credential. I think, it means that they not ignore their .env file in their repo?
*) Note: Q1 means Question 1, and so for the rest
Write a ormconfig.js file in the root of your repo. This way you can access the environment variables like the url provided from heroku and you don't have credentials in your repo.
require('dotenv').config();
module.exports = [
{
name: 'default',
type: 'postgres',
url: process.env.DATABASE_URL,
synchronize: false,
logging: false,
entities: ['dist/entities/*.*'],
migrations: ['dist/database/migrations/**/*.js'],
subscribers: ['dist/database/subscribers/**/*.js'],
cli: {
entitiesDir: 'dist/entities',
migrationsDir: 'dist/database/migrations',
subscribersDir: 'dist/database/subscribers',
},
},
{
name: 'development',
type: 'postgres',
host: process.env.POSTGRES_HOST,
port: process.env.POSTGRES_PORT,
username: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DB,
synchronize: true,
logging: true,
entities: ['src/entities/*.*'],
migrations: ['src/database/migrations/**/*.ts'],
subscribers: ['src/database/subscribers/**/*.ts'],
cli: {
entitiesDir: 'src/entities',
migrationsDir: 'src/database/migrations',
subscribersDir: 'src/database/subscribers',
},
},
];
With this configuration you can then get a specific configuration in javascript/typescript:
let connectionOptions: ConnectionOptions;
if(process.env.NODE_ENV ==='development') {
connectionOptions = await getConnectionOptions("development");
} else {
connectionOptions = await getConnectionOptions("default");
}
await createConnection(connectionOptions);

How to set the Application Name for a Sequelize application

I've got a nodejs application that uses Sequelize as it's ORM. I've successfully got Sequelize connected to the database, but I haven't found anything in the documentation that explains how to set the application name. To clarify I'm looking to set a unique Application Name attribute for my app's connection string. That way when a DBA is looking at traffic they can pick out my application's queries from the rest.
Is this something that Sequelize can even do? Or does this need to be done at the tedious level? Failing that, is there a way in nodejs to specify connection string attributes?
Tedious allows setting the app name with the appName config param. You should be able to set this via the dialectOptions object when creating your Sequelize connection:
var conn = new Sequelize('my_db', 'my_user', 'my_pass', {
host: 'my_server',
dialect: 'mssql',
dialectOptions: {
appName: 'my_app_name'
}
});
For those finding this when they're looking for how to set the name for Postgres, you use application_name in the dialectOptions, eg
{
username: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
port: process.env.DB_PORT,
host: DB_HOST,
dialect: 'postgresql',
dialectOptions: {
application_name: 'My Node App',
},
},

Resources