Comment examiner le certificat SSL du serveur PostgreSQL?


14

Supposons qu'un serveur PostgreSQL soit en cours d'exécution et que SSL soit activé. En utilisant les outils Linux et PostgreSQL "standard", comment puis-je examiner son certificat SSL?

J'espère une sortie similaire à ce que vous obtiendriez en cours d'exécution openssl x509 -text .... Et j'espère une réponse en ligne de commande à une ou deux lignes pour ne pas avoir à utiliser un renifleur de paquets.

Je n'ai pas accès au serveur PostgreSQL, donc je ne peux pas regarder directement ses fichiers de configuration.

Je n'ai pas de connexion de superutilisateur, donc je ne peux pas obtenir la valeur du ssl_cert_fileparamètre et ensuite pg_read_filedessus.

L'utilisation openssl s_client -connect ...ne fonctionne pas car PostgreSQL ne semble pas vouloir faire immédiatement la prise de contact SSL.

D'un rapide coup d'œil à la psqldocumentation, je n'ai pas pu trouver un paramètre de ligne de commande qui le fasse afficher ces informations au démarrage. (Bien qu'il me montre certaines informations chiffrées.)

Réponses:


7

Il semble que l' s_clientoutil OpenSSL a ajouté la prise en charge Postgres à l'aide de la version -starttls1.1.1, vous pouvez donc désormais utiliser toute la puissance des outils de ligne de commande d'OpenSSL sans scripts d'aide supplémentaires:

openssl s_client -starttls postgres -connect my.postgres.host:5432 # etc...

Les références:


10

En suivant l'idée dans le commentaire de Craig Ringer:

Une option consiste à patcher openssl s_clientpour établir une poignée de main avec le protocole PostgreSQL. Vous pouvez probablement aussi le faire avec Java, en passant un SSLSocketFactory personnalisé à PgJDBC. Je ne suis pas sûr qu'il existe des options simples.

... J'ai écrit une simple fabrique de sockets SSL. J'ai copié le code de la propre NonValidatingFactoryclasse de PgJDBC et j'ai juste ajouté du code pour imprimer les certificats.

Voici à quoi cela ressemblait, quand tout a été dit et fait:

import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.sql.Connection;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.postgresql.ds.PGSimpleDataSource;
import org.postgresql.ssl.WrappedFactory;

public class ShowPostgreSQLCert {
    public static void main(String[] args) throws Throwable {
        PGSimpleDataSource ds = new PGSimpleDataSource();
        ds.setServerName( ... );
        ds.setSsl(true);
        ds.setUser( ... );
        ds.setDatabaseName( ... );
        ds.setPassword( ... );
        ds.setSslfactory(DumperFactory.class.getName());
        try (Connection c = ds.getConnection()) { }
    }

    public static class DumperFactory extends WrappedFactory {
        public DumperFactory(String arg) throws GeneralSecurityException {
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(null, new TrustManager[] { new DumperTM() }, null);
            _factory = ctx.getSocketFactory();
        }
    }

    public static class DumperTM implements X509TrustManager {
        public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
        public void checkClientTrusted(X509Certificate[] certs, String authType) { }
        public void checkServerTrusted(X509Certificate[] certs, String authType) {
            for (int i=0; i<certs.length; ++i) {
                System.out.println("Cert " + (i+1) + ":");
                System.out.println("    Subject: " + certs[i].getSubjectX500Principal().getName());
                System.out.println("    Issuer: " + certs[i].getIssuerX500Principal().getName());
            }
        }
    }
}

tu gères. Je viens de l'ajouter à install-cert github.com/spyhunter99/installcert
spy

Merci beaucoup. Pour les personnes ne souhaitant pas utiliser PGSimpleDataSource. Voici la variante d'utilisation de la configuration normale du pilote JDBC: String connectionURL = "jdbc:postgresql://server:62013/dbname"; Properties props = new Properties(); props.setProperty("user", "username"); props.setProperty("password", "password"); props.setProperty("ssl", "true"); props.setProperty("sslfactory", DumperFactory.class.getName()); Connection con = null; // Load the Driver class. Class.forName("org.postgresql.Driver"); con = DriverManager.getConnection(connectionURL, props);
Markus

7

Si vous ne voulez pas vous soucier d'installer java et de compiler, et que vous avez déjà python, vous pouvez essayer ce script python: https://github.com/thusoy/postgres-mitm/blob/master/postgres_get_server_cert.py

Je l'utilise pour vérifier les dates de certificats:

postgres_get_server_cert.py example.com:5432 | openssl x509 -noout -dates

Ou pour le certificat complet sous forme de texte:

postgres_get_server_cert.py example.com:5432 | openssl x509 -noout -text

1
Pour l'utiliser sans installer: curl https://raw.githubusercontent.com/thusoy/postgres-mitm/master/postgres_get_server_cert.py | python - example.com:5432(mais assurez-vous de ce que vous exécutez de cette façon !!)
Yajo

3

La réponse de CSD m'a vraiment sauvé. Voici une procédure plus détaillée pour ceux d'entre nous qui ne connaissent pas ou ont oublié Java.

  1. Assurez-vous que votre serveur peut compiler java. Essayez la commande "which javac", si elle génère quelque chose comme "... no javac in ...", alors vous devez installer un JDK (JRE ne fonctionnera pas, il a "java" mais pas "javac").

  2. Installez postgresql-jdbc si vous ne l'avez pas déjà. Pour RHEL6, la commande est "yum install postgresql-jdbc". Déterminez où les fichiers jar sont installés. Il y en aura plusieurs, un pour chaque version. J'ai utilisé "/usr/share/java/postgresql-jdbc3.jar".

  3. Copiez le code csd et insérez les informations de la base de données (l'autre réponse), ou utilisez ma version légèrement modifiée à la fin de cette réponse. Enregistrez-le dans un fichier appelé exactement "ShowPostgreSQLCert.java". Questions majuscules / minuscules, appelez-le n'importe quoi d'autre et il ne sera pas compilé.

  4. Dans le répertoire contenant le fichier ShowPostgreSQLCert.java, exécutez la commande suivante (modifiez l'emplacement de postgresql-jdbc3.jar si nécessaire): "javac -cp /usr/share/java/postgresql-jdbc3.jar ShowPostgreSQLCert.java". Vous devriez maintenant avoir 3 fichiers .class dans le même répertoire.

  5. Enfin, exécutez la commande suivante: "java -cp.: / Usr / share / java / postgresql-jdbc3.jar ShowPostgreSQLCert". Le "." après "-cp" signifie qu'il devrait chercher dans le répertoire courant les fichiers .class. Vous pouvez insérer le chemin d'accès complet aux fichiers de classe ici, n'oubliez pas de garder le ":" entre le chemin et l'emplacement du fichier .jar.

  6. Si vous devez exécuter la commande sur une autre machine, vous devez avoir le même fichier jar installé (postgresql-jdbc3.jar), ou vous pouvez probablement le copier à partir du serveur sur lequel vous avez compilé les fichiers .class. Ensuite, copiez simplement les fichiers .class et exécutez la commande à partir de 5. après avoir modifié les chemins.

J'ai légèrement modifié le code afin que vous puissiez transmettre des informations de base de données sur la ligne de commande au lieu de les compiler dans le fichier .class. Il suffit de l'exécuter sans aucun argument et il affichera un message indiquant les arguments qu'il attend. Le code + modifications de csd est:

import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.sql.Connection;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.postgresql.ds.PGSimpleDataSource;
import org.postgresql.ssl.WrappedFactory;

public class ShowPostgreSQLCert {
    public static void main(String[] args) throws Throwable {
        PGSimpleDataSource ds = new PGSimpleDataSource();
        if( args.length != 4 ) {
            System.out.println("Not enough arguments. Usage: ShowPostgreSQLCert ServerName User DatabaseName Password");
            System.exit(1);
        }
        ds.setServerName( args[0] );
        ds.setSsl(true);
        ds.setUser( args[1] );
        ds.setDatabaseName( args[2] );
        ds.setPassword( args[3] );
        ds.setSslfactory(DumperFactory.class.getName());
        try (Connection c = ds.getConnection()) { }
    }

    public static class DumperFactory extends WrappedFactory {
        public DumperFactory(String arg) throws GeneralSecurityException {
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(null, new TrustManager[] { new DumperTM() }, null);
            _factory = ctx.getSocketFactory();
        }
    }

    public static class DumperTM implements X509TrustManager {
        public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
        public void checkClientTrusted(X509Certificate[] certs, String authType) { }
        public void checkServerTrusted(X509Certificate[] certs, String authType) {
            for (int i=0; i<certs.length; ++i) {
                System.out.println("Cert " + (i+1) + ":");
                System.out.println("    Subject: " + certs[i].getSubjectX500Principal().getName());
                System.out.println("    Issuer: " + certs[i].getIssuerX500Principal().getName());
            }
        }
    }
}

1

J'ai ajouté du code à partir de /programming/3313020/write-x509-certificate-into-pem-formatted-string-in-java pour sortir les certificats en tant que PEM, et supprimé la nécessité de spécifier une base de données, nom d'utilisateur ou mot de passe (ils ne sont pas nécessaires pour obtenir le certificat).

Grâce à cela, j'ai pu vérifier qu'un redémarrage de PostgreSQL ne semble malheureusement pas nécessaire pour passer à un nouveau certificat.

N'étant pas développeur Java, mes étapes de construction et d'exécution ne sont probablement pas si bonnes, mais elles fonctionnent, tant que vous pouvez trouver un jdbc postgresql

# locate postgresql | grep jar
/path/to/a/lib/postgresql-9.1-901-1.jdbc4.jar   <-- this one will do
...

Compiler:

javac -cp /path/to/a/lib/postgresql-9.1-901-1.jdbc4.jar ./ShowPostgreSQLCert.java

Courir:

java -cp /path/to/a/lib/postgresql-9.1-901-1.jdbc4.jar:. ShowPostgreSQLCert 127.0.0.1

Exemple de sortie:

Cert 1:
    Subject: CN=...
    Issuer: CN=...
    Not Before: Fri Oct 21 11:14:06 NZDT 2016
    Not After: Sun Oct 21 11:24:00 NZDT 2018
-----BEGIN CERTIFICATE-----
MIIHEjCCBfqgAwIBAgIUUbiRZjruNAEo2j1QPqBh6GzcNrwwDQYJKoZIhvcNAQEL
...
IcIXcVQxPzVrpIDT5G6jArVt+ERLEWs2V09iMwY7//CQb0ivpVg=
-----END CERTIFICATE-----

Cert 2:
...

La source:

import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.sql.Connection;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.postgresql.ds.PGSimpleDataSource;
import org.postgresql.ssl.WrappedFactory;

import javax.xml.bind.DatatypeConverter;
import java.security.cert.X509Certificate;
import java.io.StringWriter;

public class ShowPostgreSQLCert {
    public static void main(String[] args) throws Throwable {
        PGSimpleDataSource ds = new PGSimpleDataSource();
        if( args.length != 1 ) {
            System.out.println("Not enough arguments.");
            System.out.println("Usage: ShowPostgreSQLCert ServerName");
            System.exit(1);
        }
        ds.setServerName( args[0] );
        ds.setSsl(true);
        ds.setUser( "" );
        ds.setDatabaseName( "" );
        ds.setPassword( "" );
        ds.setSslfactory(DumperFactory.class.getName());
        try (Connection c = ds.getConnection()) { }
        catch (org.postgresql.util.PSQLException e) {
            // Don't actually want to login
        }
    }

    public static class DumperFactory extends WrappedFactory {
        public DumperFactory(String arg) throws GeneralSecurityException {
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(null, new TrustManager[] { new DumperTM() }, null);
            _factory = ctx.getSocketFactory();
        }
    }

    public static String certToString(X509Certificate cert) {
        StringWriter sw = new StringWriter();
        try {
            sw.write("-----BEGIN CERTIFICATE-----\n");
            sw.write(DatatypeConverter.printBase64Binary(cert.getEncoded()).replaceAll("(.{64})", "$1\n"));
            sw.write("\n-----END CERTIFICATE-----\n");
        } catch (java.security.cert.CertificateEncodingException e) {
            e.printStackTrace();
        }
        return sw.toString();
    }

    public static class DumperTM implements X509TrustManager {
        public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
        public void checkClientTrusted(X509Certificate[] certs, String authType) { }
        public void checkServerTrusted(X509Certificate[] certs, String authType) {
            for (int i=0; i<certs.length; ++i) {

                System.out.println("Cert " + (i+1) + ":");
                System.out.println("    Subject: " + certs[i].getSubjectX500Principal().getName());
                System.out.println("    Issuer: " + certs[i].getIssuerX500Principal().getName());
                System.out.println("    Not Before: " + certs[i].getNotBefore().toString());
                System.out.println("    Not After: " + certs[i].getNotAfter().toString());

                System.out.println(certToString(certs[i]));
            }
        }
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.