Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(pg-connection-string): get closer to libpq semantics for sslmode #2709

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/pg-connection-string/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,10 @@ Query parameters follow a `?` character, including the following special query p
* `ssl=1`, `ssl=true`, `ssl=0`, `ssl=false` - sets `ssl` to true or false, accordingly
* `sslmode=<sslmode>`
* `sslmode=disable` - sets `ssl` to false
* `sslmode=no-verify` - sets `ssl` to `{ rejectUnauthorized: false }`
* `sslmode=prefer`, `sslmode=require`, `sslmode=verify-ca`, `sslmode=verify-full` - sets `ssl` to true
* `sslmode=no-verify`, `sslmode=prefer` - sets `ssl` to `{ rejectUnauthorized: false }`
* `sslmode=require` - sets `ssl` to `{ rejectUnauthorized: false }` unless `sslrootcert` is specified, in which case it behaves like `verify-ca`
* `sslmode=verify-ca` - sets `ssl` to `{ checkServerIdentity: no-op }` (verify CA, but not server identity)
* `sslmode=verify-full` - sets `ssl` to `{}` (verify CA and server identity)
* `sslcert=<filename>` - reads data from the given file and includes the result as `ssl.cert`
* `sslkey=<filename>` - reads data from the given file and includes the result as `ssl.key`
* `sslrootcert=<filename>` - reads data from the given file and includes the result as `ssl.ca`
Expand Down
21 changes: 16 additions & 5 deletions packages/pg-connection-string/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,26 @@ function parse(str) {
break
}
case 'prefer':
case 'require':
case 'verify-ca':
case 'verify-full': {
break
}
case 'no-verify': {
config.ssl.rejectUnauthorized = false
break
}
case 'require': {
if (config.sslrootcert) {
// If a root CA is specified, behavior of `sslmode=require` will be the same as that of `verify-ca`
config.ssl.checkServerIdentity = function () {}
} else {
config.ssl.rejectUnauthorized = false
}
break
}
case 'verify-ca': {
config.ssl.checkServerIdentity = function () {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second thought, even though libpq has and has documented this same problem, maybe we should enforce that config.ssl.ca is truthy here.

Suggested change
config.ssl.checkServerIdentity = function () {}
if (!config.ssl.ca) {
throw new Error('sslmode=verify-ca requires specifying a CA with sslrootcert')
}
config.ssl.checkServerIdentity = function () {}

The main issue with this would be for anyone who wants to add a ca key to the returned config.ssl after parsing. I don’t think a warning is the way to go (too easy for those who need it to miss or ignore, and annoying for those who don’t need it to suppress). Would adding a whole options parameter to support this (parse(str, {deferSslCa: true}) – probably a better name option) be too much? (Related issue, merging configuration with a connection string is hard in general: #3327)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, the check could go in pg, e.g. by pg-connection-string exporting const NO_VERIFY = () => {} and pg throwing if config.ssl.checkServerIdentity === NO_VERIFY && !config.ssl.ca.

break
}
case 'verify-full': {
break
}
}

return config
Expand Down
17 changes: 11 additions & 6 deletions packages/pg-connection-string/test/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,19 +258,24 @@ describe('parse', function () {
it('configuration parameter sslmode=prefer', function () {
var connectionString = 'pg:///?sslmode=prefer'
var subject = parse(connectionString)
subject.ssl.should.eql({})
subject.ssl.should.eql({
rejectUnauthorized: false,
})
})

it('configuration parameter sslmode=require', function () {
var connectionString = 'pg:///?sslmode=require'
var subject = parse(connectionString)
subject.ssl.should.eql({})
subject.ssl.should.eql({
rejectUnauthorized: false,
})
})

it('configuration parameter sslmode=verify-ca', function () {
var connectionString = 'pg:///?sslmode=verify-ca'
var subject = parse(connectionString)
subject.ssl.should.eql({})
subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
expect(subject.ssl.checkServerIdentity()).to.be.undefined
})

it('configuration parameter sslmode=verify-full', function () {
Expand All @@ -282,9 +287,9 @@ describe('parse', function () {
it('configuration parameter ssl=true and sslmode=require still work with sslrootcert=/path/to/ca', function () {
var connectionString = 'pg:///?ssl=true&sslrootcert=' + __dirname + '/example.ca&sslmode=require'
var subject = parse(connectionString)
subject.ssl.should.eql({
ca: 'example ca\n',
})
subject.ssl.should.have.property('ca', 'example ca\n')
subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
expect(subject.ssl.checkServerIdentity()).to.be.undefined
})

it('allow other params like max, ...', function () {
Expand Down