How ssl works in psql? - postgresql

In AWS RDS postgresql server side ssl has been forced using below config values.
rds.force_ssl 1
ssl 1
When I am trying to connect to postgres RDS host without specifying the sslmode and sslrootcert, it is allowing the ssl connection.
psql -h hostname.us-east-1.rds.amazonaws.com -p 5432 --user=username
psql (10.10 (Ubuntu 10.10-0ubuntu0.18.04.1), server 10.6)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
username=>
Since I did not specify the sslmode, it has taken default sslmode which is prefer. I would like to know:
How is it encrypting the data in transit to the server?
How is it selecting ssl protocol, ssl cipher and compression before sending the data packets to server?
Since I am not specifying any sslrootcert, is it taking any default cert for ssl handshake with server?
Please explain.

Isn't "ECDHE-RSA-AES256-GCM-SHA384" the answer to this? If not, can you expand your question?
As far as I know, this is just outsourced to the ssl library. PostgreSQL doesn't do anything special, other than pass along the configuration options.
It will take the sslrootcert from the default location if it finds one there (in which case, the validation would probably fail, if you didn't go out of way to put something appropriate there). But if it does not find one, then you basically only get Diffie-Hellman protection. It doesn't verify that the server's cert was actually signed by the claimed CA, nor that the cert "belongs" to the server's hostname. You only get protection from eavesdroppers, not from MITM. If the client wants MITM protection, it must set sslmode to a level higher than 'require'.
There is no mechanism implemented by which the server can force the client to validate the server's cert.

Related

could not initiate GSSAPI security context on Postgres 14 logical replication

I am following this post to enable ssl on Postgres 14 for logical replication. Then try to make connection on client:
CREATE SUBSCRIPTION my-sub
CONNECTION 'host=my-domain.com dbname=my-db user=my-username password=xxxxxx'
PUBLICATION my-pub;
It throws error:
2022-05-12 13:51:36.047 PDT [37340] ERROR: could not connect to the publisher: connection to server at "my_domain.com" (xxx.xxx.xxx.141), port 5432 failed: could not initiate GSSAPI security context: The operation or option is not available: Credential for asked mech-type mech not found in the credential handle
connection to server at "my_domain.com" (xxx.xxx.xxx.141), port 5432 failed: FATAL: connection requires a valid client certificate
connection to server at "my-domain.com" (xxx.xxx.xxx.141), port 5432 failed: FATAL: no pg_hba.conf entry for host "xxx.xxx.xxx.199", user "my-username", database "my-db", no encryption
On my-pub server, one line was added to pg_hba.conf:
hostssl all all 0.0.0.0/0 scram-sha-256 clientcert=verify-full
On sub client, the ca file is setup as below:
ssl_ca_file = '/usr/local/var/postgres/root.crt'. //<<==client cert copied from pub server.
Mostly people just use serve certs. Using client certs is unusual, I would say especially in the case of a logical replication subscriber. But if you do actually want the publisher to demand client certs, it is not configured incorrectly to that purpose (or at least, not that we can tell from the current data). The publisher is demanding a client cert, but the subscriber is not offering one. The configuration problem is on the subscriber.
Note that in this case the subscriber will be acting as the client to connect to the publisher, not acting in the role of a server. It uses the libpq client library to do that, and so the configuration of it is not based on the contents of postgresql.conf. In particular, ssl_ca_file is a server configuration option, not client configuration.
So the way to do this would be for the CONNECTION to look something like
'host=my-domain.com dbname=my-db user=my-username password=xxxxxx sslcert=/foobar/my-username.crt sslkey=/foobar/my-username.key'
But for this to work, the cert and key would need to be on the subscriber computer, readable to whomever owns the postgres process. Which already renders any security benefit dubious.

PostgreSQL SSL doesn't work in cmd and java

Please help with the following problem ...
OS - Windows.
I want to configure SSL on Postgresql 12.
Then my Java application will add entries to the database, delete, etc.
I created certificates: CA, server, client.
CA and server are located in the directory C:\Program Files\PostgreSQL\12\data
The client is located in C:\Users\User\AppData\postgresql
Then I added CA certificate to trusted in Windows.
Configs:
pg_hba:
hostnossl all all 0.0.0.0/0 reject
hostssl all all 0.0.0.0/0 cert clientcert=1
postgresql.conf:
ssl = on
ssl_ca_file = 'root.crt'
I can connect server throw pgAdmin with my certificates, but there are some errors in cmd (and java)
Thant's what I tried to do in cmd
psql.exe -U postgres -h 127.0.0.1
Result:
SSL: certificate verify failed
FATAL: pg_hba.conf rejects connection for host "127.0.0.1", user "postgres", database "prod", SSL off
Logs:
tlsv1 alert unknown ca
FATAL: pg_hba.conf rejects connection for host "127.0.0.1", user "postgres", database "prod", SSL off
Please, tell me what's can be wrong and how can I fix it...
Also I did not find information, how to transfer my certificates to the database from Java application. Maybe, anybody can help me with that problem))
Thanks!
use this in your connection string and it will work:
ssl=true&sslfactory=org.postgresql.ssl.NonValidatingFactory
Then I added CA certificate to trusted in Windows.
psql does not integrate with the Windows certificate manager. The CA to be used by the client needs to go in %APPDATA%\postgresql\root.crt, or if elsewhere its location must be specified by a connection parameter (sslrootcert) or environment variable (PGSSLROOTCERT). These must be files, I don't think there is way to say "go get it from the cert manager".
I think jdbc is the same way, it does not integrate with the Windows cert manager either, for how to specify the locations see https://jdbc.postgresql.org/documentation/head/ssl-client.html
SSL: certificate verify failed FATAL: pg_hba.conf rejects connection for host "127.0.0.1", user "postgres", database "prod", SSL off
Maybe you already know this, but first it tried using SSL and failed to verify the cert (it does not make it clear which side failed, the client cert or the server cert), then it tried to fall back to no SSL and got rejected by the pg_hba. If you had set the client's sslmode to "require" or higher, it would not have tried to fall back to the no SSL, it would have stopped at the first error.

Certificate failure using tsl with pgbouncer and Postgres

Hi we are running into a problem with tls between PostgreSQL 11 and pgbouncer 1.9, both on the same host in different containers.
tls enabled and is working between client and pgbouncer and between pgbouncer and PostgreSQL.
The problem is that client connet to pgbouncer with client key and certificate, and after auth pgbouncer pass the client with his/her username and password to postgreSQL and use its own pgbouncer certificate. Immediatly we got error because PostgreSQL checks the username and username in cert and they doesn't match.
What is the right way of solving this issue?
Is there a way to pass client certificate to pgbouncer and then to PostgreSQL?
Thank you
For now there is a mapping on postgres side like this
mymap pgbouncer clientuser
but this is not a viable solution
The best you can do in this case is not to use certificate authentication with PostgreSQL.
In a setup like yours, it is pgBouncer that verifies the authenticity of the incoming client connection using certificates. One this is done, you don't need any further verification.
So use the trust authentication for connections between pgBouncer and PostgreSQL, if that is a trusted network for you.

How to Configure postgresql User with SSL connection only?

My steps I did (psql v. 9.3):
create psql user with password
give him access to relevant databases
allow connection from the outside (psql conf)
and put "hostssl all all 0.0.0.0/0 trust" in pg_hba.conf
Do I need to create a key now to be able to connect?
How can I test that key if it works?
Thanks for help.
Read the documentation.
If you didn't do that already, you'll have to configure the PostgreSQL server for SSL: create server.crt and server.key in the PostgreSQL data directory, set ssl = on in postgresql.conf and restart the server.
You don't need a client certificate or key to connect to the server.
To test if your setup works, just connect with psql.
If SSL is used, it will look as follows:
$ psql -d test -h 127.0.0.1
psql (9.3.13)
SSL connection (cipher: DHE-RSA-AES256-GCM-SHA384, bits: 256)
Type "help" for help.
test=#
Change the trust to md5 (EXTREMELY IMPORTANT)
try to connect over psql from another system using different ssl settings (see the psql man page for this).
If you want to require a cert, then there are additional steps to do, but that's not specified in your question. And cert authentication is possible but an advanced topic.

SSL error when connection pgAdmin3 to Heroku postgreSQL DB

When trying to connect to Heroku PostgresSQL DB using pgAdmin3 I'm getting the following error:
Error connecting to the server: SSL error: certificate verify failed
The connection is based on pg:credentials output and defined as below:
[Properties]:
Host: <host>
Port: 5432
Service: [blank]
Maintenance DB: <database>
Username: <user>
[SSL]:
SSL: require
Server Root Certificate File: [blank]
Server Certificate Revocation List: [blank]
Client Certificate File: [blank]
Client Key File: [blank]
SSL compression: on
[SSH Tunnel] and [Advanced] left default
so as per Heroku guidelines SSL is enabled (set to: [**require**]).
Any ideas how to provide/fix the certificate referred by the error message?
It's likely that pgAdmin is picking up a bundle of CA certificates that has been configured on your system, in which case require would try to verify the server certificate against that bundle.
Typically, this would be a root.crt file located in %APPDATA%\postgresql\ (C:\Users\YourUserName\AppData\Roaming\postgresql\) under Windows, or in ~/.postgresql/ under Linux.
If there is such a file, try to move it out of the way temporarily to check if it works better.
The problem with moving it out of the way is that you are then no longer verifying any remote PostgreSQL certificates against anything but it still works (with require, it would fail with verify-full).
You can solve this by putting the root.crt file in the right place and adding the server certificate to the list of trusted certificates.
It can be bit tricky to find a remote PostgreSQL certificate, but this simple Python application should let you do it (replace hostname and port as required):
import socket
import ssl
import struct
hostname = '...'
port = 5432
sock = socket.socket()
sock.connect((hostname, port))
# We first connect without encryption and tell the server
# we want to upgrade to SSL/TLS.
initiate_ssl_command = struct.Struct('!ii').pack(8, 80877103)
sock.sendall(initiate_ssl_command)
resp = sock.recv(1)
print "Response should be S: %s" % (resp,)
# We then initiate an SSL/TLS connection on this socket.
ssl_sock = ssl.wrap_socket(sock, cert_reqs=ssl.CERT_NONE)
ssl_sock.do_handshake()
peer_cert = ssl_sock.getpeercert(True)
print ssl.DER_cert_to_PEM_cert(peer_cert)
ssl_sock.close()
(For details about what this code does, see the PostgreSQL protocol documentation, in particular the "SSL Session Encryption" section and the SSLRequest message, which is similar to what STARTTLS does in other protocols such as SMTP.)
Security warning: Here, you're just hoping that this particular connection has not been attacked and returns the genuine certificate the first time. It's what you'll use as a trust anchor for subsequent connections. (It's very similar to accepting an SSH server key for the first connection, it will flag changes to the certificate if it changes.)
It's also worth noting that the certificate Subject DN might not match that of the server you're connecting to, therefore you might not be able to use PostgreSQL's verify-full mode (which is the only really secure mode, since it also verifies the host name as well as the trust anchor).
Ideally, Heroku (or whoever provides this service) should give you that certificate by another secure means, and make sure that the Subject DN in that certificate matches the host name they give you. (I'm not sure if this is the case at the moment, perhaps it is available from the administration interface.)