57

I am trying to connect to my Heroku PostgreSQL DB and I keep getting an SSL error. Does anyone have an idea on how to enable SSL in the connection string?

postgres://user:pass@host:port/database;

Been looking for it everywhere but it does not seem to be a very popular topic. By the way, I am running Nodejs and the node-pg module with its connection-pooled method:

pg.connect(connString, function(err, client, done) {
  // Should work.
});

Comments are much appreciated.

Soviut
  • 88,194
  • 49
  • 192
  • 260
Stefan
  • 1,214
  • 1
  • 9
  • 17

9 Answers9

83

You can achieve this like this:

postgres://user:pass@host:port/database?ssl=true
Jérôme Verstrynge
  • 57,710
  • 92
  • 283
  • 453
  • 3
    I get following error when I do this - ```The server does not support SSL connections```. But ```psql "sslmode=require"``` works. And thus, I am inclined to think there is something wrong with the ```"pg": "^4.3.0"``` npm package am using. Any thoughts? – Anshu Kumar Feb 26 '18 at 17:49
  • If I am correct the `pass` stands for password. So what is the point of password if we are writing it here in plain text? How logical it is to write passwords in plain text in these connection string anyway? – Suraj Oct 05 '18 at 13:43
  • A connection string like this is generally stored in the same secure way as any other secret, since, as you point out, it contains the password. In Heroku the connection string like this comes through as an environment variable. At some point passwords have to be in plaintext to be used by a machine, but you definitely wouldn't want to check this into source control or anything like that. – juanpaco Jan 29 '19 at 18:28
26

You also can use this code below when create a new Client from node-postgres:

var pg = require("pg");

var client = new pg.Client({
  user: "yourUser",
  password: "yourPass",
  database: "yourDatabase",
  port: 5432,
  host: "host.com",
  ssl: true
});

client.connect();

var query = client.query('CREATE TABLE people(id SERIAL PRIMARY KEY, name VARCHAR(100) not null)');

query.on('row', function(row) {
  console.log(row.name);
});

query.on('end', client.end.bind(client));

Hope this helps!

felipekm
  • 2,820
  • 5
  • 32
  • 42
  • 3
    I took your suggestion and added the `ssl: true` property. Was it that simple? How can I make sure that my connection to the DB is really secure now? – tom Jul 27 '18 at 23:01
  • 1
    Thanks for the help. I was trying to map my username to postgres usernames using pd_ident.conf maps but all I had to do was add the user explicitly in the client config object. – Shmuel Kamensky Dec 27 '18 at 20:27
  • ssl true worked for me – Guilherme Lucas Feb 23 '23 at 19:38
22

With Google Cloud PG and pg-promise I had a similar need. The error I got (using ?ssl=true) was connection requires a valid client certificate.

SSL connection is not documented for pg-promise but it is built on node-postgres. As explained in the link, the ssl config parameter can be more than just true:

const pgp = require('pg-promise')();
const fs = require('fs');

const connectionConf = {
    host: 'myhost.com',
    port: 5432,
    database: 'specific_db_name',
    user: 'my_App_user',
    password: 'aSecretePass',
    ssl: {
        rejectUnauthorized : false,
        ca   : fs.readFileSync("server-ca.pem").toString(),
        key  : fs.readFileSync("client-key.pem").toString(),
        cert : fs.readFileSync("client-cert.pem").toString(),
  }

};
const new_db = pgp(connectionConf);
new_db.any('SELECT * FROM interesting_table_a LIMIT 10')
    .then(res => {console.log(res);})
    .catch(err => {console.error(err);})
    .then(() => {new_db.$pool.end()});
amotzg
  • 1,142
  • 12
  • 28
  • SSL is documented in `pg-promise` through declarations, if you are using TypeScript ;) See type [TConnectionParameters](https://github.com/vitaly-t/pg-promise/blob/master/typescript/pg-subset.d.ts#L74), which uses [TSSLConfig](https://github.com/vitaly-t/pg-promise/blob/master/typescript/pg-subset.d.ts#L53) for the SSL configuration. The beauty of using TypeScript, huh, you get to discover supported things much easier ;) – vitaly-t Aug 22 '18 at 07:55
  • 1
    In case anyone wonders the ca connection works for aws rds postgres db and knexjs, the readFileSync is essential, other solutions say that one can just use the url as url: postgres://[db-username]:[your db password]@[endpoint]:[your db port number]/[db name]?sslca=config/amazon-rds-ca-cert.pem&sslmode=required but the path of your certificate seems to not be read somehow. – VericalId Dec 28 '20 at 20:46
  • Why would you specify a `ca` to validate authorized servers and then contradict that by specifying `rejectUnauthorized : false`? – Amit Naidu Jul 11 '23 at 01:56
7

For anybody looking for a TypeORM solution, it's also {ssl: true}.

Full example:

const connectionOptions: PostgresConnectionOptions = {
    name: `default`,
    type: `postgres`,
    url: process.env.DATABASE_URL,
    ssl: process.env.DATABASE_SSL === `true`
}
Karim Varela
  • 7,562
  • 10
  • 53
  • 78
4

I have the same problem. As for today, there is a problem with pg >= 8.0.0. So if you have this problem use pg version 7 and below.

yarn add pg@7
  • 1
    Deprecation message with pg7 - (node:42432) DeprecationWarning: Implicit disabling of certificate verification is deprecated and will be removed in pg 8. Specify `rejectUnauthorized: true` to require a valid CA or `rejectUnauthorized: false` to explicitly opt out of MITM protection. – ns15 Jul 09 '20 at 13:41
2

You can also use environment variables to set up the connection. Here is an example.

(Assuming you have a Postgres DB running on port 5432@localhost and the DB supports SSL connection)

.env

PGHOST=localhost
PGPORT=5432
PGDATABASE=mydb
PGUSER=pguser1
PGPASSWORD=mypassword
PGSSLMODE=require

(Ensure you set PGSSLMODE to require as shown above.)

db.js

require('dotenv').config()
const { Pool } = require('pg')

// pools will use environment variables for connection information
const pool = new Pool()
// const pool = new Pool({ ssl: true }); This works too in the absence of PGSSLMODE
pool.on('error', function (err) {
    console.log('idle client error', err.message, err.stack)
})

module.exports = {
    pool,
    query: (text, params, callback) => {
        return pool.query(text, params, callback)
    }
}

server.js

const express = require('express')
const { pool } = require('./db')

const app = express()
const port = 3000

app.get('/', async (req, res) => {
  console.log('Request received...')
  const result = await pool.query(`SELECT * FROM organization`);
  res.send(result)
})

app.listen(port, () => console.log(`Example app listening on port ${port}!`))

Note: in case your Postgres database does not support SSL connections, you will have the following error when your application tries to make a query:

Error: The server does not support SSL connections
at Socket.<anonymous> (node_modules/pg/lib/connection.js:87:35)

References:

Yuci
  • 27,235
  • 10
  • 114
  • 113
2

If none of the above work.

Consider my case, was working before and recently switched work stations - suddenly not working anymore and giving the abovementioned error.

The reason was that I cloned a GitHub repo (my own) that had calls to private variables after switching computers. The repo did not have a .env file (obviously) which contained the database connection string along with many other variables. Thus, my call to it in the code no longer worked until I created the .env file locally.

TLDR

// database_config.js
const { Client } = require("pg");
const client = new Client({
  connectionString: process.env.DATABASE_CONNECTION, // <--- not defined without .env file definition
  ssl: {
    rejectUnauthorized: false,
  },
});

client
.connect()
.then(() => console.log("connected to database"))
.catch((err) => console.error(err));

module.exports = client;

SOLUTION

// .env
DATABASE_CONNECTION = your_connection_string
lbragile
  • 7,549
  • 3
  • 27
  • 64
2
const sequelize = new Sequelize(
    configuration.get("postgresConnectionString"),
    {
        logging: false,
        dialectOptions: {
            ssl: {
                ca: fs.readFileSync(path.join(__dirname, "/ssl/DigiCertGlobalRootG2.crt.pem")),
                rejectUnauthorized: true,
                require: true,
            },
        },
    },
);

DigiCertGlobalRootG2.crt.pem: Is a combination of 2 key for constitency(BaltimoreCyberTrustRoot.crt.pem gonna expiry soon).One file with multiple keys as ----- BEGIN CERTIFICATE ----- BaltimoreCyberTrustRoot.crt.pem ----- END CERTIFICATE ----- ----- BEGIN CERTIFICATE ----- DigiCertGlobalRootG2.crt.pem -----END CERTIFICATE----- Connecting to Azure postgress from node.js application using Sequelize ORM with support of SSL.
DigiCertGlobalRootG2.crt.pem: You will get it from the Azure portal.

Note: If not using any key then ssl configuration is like this:

const sequelize = new Sequelize(
    configuration.get("postgresConnectionString"),
    {
        logging: false,
        dialectOptions: {
            ssl: true,
        },
    },
);

Note: Please check the connectionString ends with sslmode=require|| ssl_ca=required: https://learn.microsoft.com/en-us/azure/mysql/concepts-certificate-rotation, based this, please add SSL configuration accordingly Note: The content you feel need a change , please let me know

Nelad
  • 31
  • 3
1

If you are using AWS RDS Proxy for Postgresql then ssl: true is sufficient. complete example is here

let getConnection = async () => {
    let connParam = {
        host: {RDS_PROXY_URL},
        port: {PORT},
        user: {USERNAME},
        database : {DB_ANME},
        password: {PASSWORD},
        ssl: true
    };
    
    let pgClient = new pg.Client(connParam);

    await pgClient.connect();

    return pgClient;
}
Girish Kumar
  • 450
  • 5
  • 6