¿Cómo examinar el certificado SSL del servidor PostgreSQL?

14

Supongamos que hay un servidor PostgreSQL en ejecución y tiene SSL habilitado. Usando herramientas "estándar" de Linux y PostgreSQL, ¿cómo puedo examinar su certificado SSL?

Espero una salida similar a la que obtendría al ejecutar openssl x509 -text .... Y espero una respuesta de línea de comando de una o dos líneas para no tener que recurrir a ejecutar un sniffer de paquetes.

No tengo acceso al servidor PostgreSQL, por lo que no puedo ver sus archivos de configuración directamente.

No tengo un inicio de sesión de superusuario, por lo que no puedo obtener el valor de la ssl_cert_fileconfiguración y luego pg_read_fileen él.

El uso openssl s_client -connect ...no funciona porque PostgreSQL no parece querer hacer el protocolo de enlace SSL de inmediato.

De un vistazo rápido a la psqldocumentación, no pude encontrar un parámetro de línea de comandos que lo haga mostrar esa información en el inicio. (Aunque me muestra cierta información de cifrado).

csd
fuente

Respuestas:

7

Parece que la s_clientherramienta de OpenSSL agregó soporte para Postgres usando la -starttlsversión 1.1.1, por lo que ahora puede usar toda la potencia de las herramientas de línea de comandos de OpenSSL sin scripts de ayuda adicionales:

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

Referencias

Adam Batkin
fuente
10

Siguiendo la idea en el comentario de Craig Ringer:

Una opción es aplicar un parche openssl s_cliental protocolo de enlace con el protocolo PostgreSQL. Probablemente también pueda hacerlo con Java, pasando un SSLSocketFactory personalizado a PgJDBC. No estoy seguro de que haya opciones simples.

... Escribí una simple fábrica de sockets SSL. Copié el código de la propia NonValidatingFactoryclase de PgJDBC y acabo de agregar código para imprimir los certificados.

Esto es lo que parecía, cuando todo estaba dicho y hecho:

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());
            }
        }
    }
}
csd
fuente
tú Molas. Acabo de agregar esto a install-cert github.com/spyhunter99/installcert
espía el
Impresionante muchas gracias. Para las personas que no desean utilizar PGSimpleDataSource. Aquí la variante para usar la configuración normal del controlador 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 no quiere molestarse en instalar java y compilar, y ya tiene Python, puede probar este script de Python: https://github.com/thusoy/postgres-mitm/blob/master/postgres_get_server_cert.py.

Lo uso para verificar las fechas del certificado:

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

O para el certificado completo como texto:

postgres_get_server_cert.py example.com:5432 | openssl x509 -noout -text
mivk
fuente
1
Para usarlo sin instalar: curl https://raw.githubusercontent.com/thusoy/postgres-mitm/master/postgres_get_server_cert.py | python - example.com:5432(¡pero asegúrese de lo que ejecuta de esta manera!)
Yajo
3

La respuesta de csd realmente me salvó. Aquí hay un tutorial más detallado para aquellos de nosotros que no conocemos o hemos olvidado Java.

  1. Asegúrese de que su servidor pueda compilar Java. Pruebe el comando "which javac", si genera algo como "... no javac en ...", entonces necesita instalar un JDK (JRE no funcionará, tiene "java" pero no "javac").

  2. Instale postgresql-jdbc si aún no lo tiene. Para RHEL6, el comando es "yum install postgresql-jdbc". Averigua dónde están instalados los archivos jar. Habrá varios de ellos, uno para cada versión. Usé "/usr/share/java/postgresql-jdbc3.jar".

  3. Copie el código de CDS e inserte la información de la base de datos (la otra respuesta), o use mi versión ligeramente modificada al final de esta respuesta. Guárdelo en un archivo llamado exactamente "ShowPostgreSQLCert.java". Los asuntos en mayúsculas / minúsculas, llámalo como sea y no se compilará.

  4. En el directorio con el archivo ShowPostgreSQLCert.java, ejecute el siguiente comando (modifique la ubicación de postgresql-jdbc3.jar si es necesario): "javac -cp /usr/share/java/postgresql-jdbc3.jar ShowPostgreSQLCert.java". Ahora debería tener 3 archivos .class en el mismo directorio.

  5. Finalmente, ejecute el siguiente comando: "java -cp.: / Usr / share / java / postgresql-jdbc3.jar ShowPostgreSQLCert". Los "." después de "-cp" significa que debe buscar en el directorio actual los archivos .class. Puede insertar la ruta completa a los archivos de clase aquí, solo recuerde mantener el ":" entre la ruta y la ubicación del archivo .jar.

  6. Si necesita ejecutar el comando en una máquina diferente, debe tener el mismo archivo jar instalado (postgresql-jdbc3.jar), o probablemente solo puede copiarlo desde el servidor en el que compiló los archivos .class. Luego simplemente copie los archivos .class y ejecute el comando desde 5. después de modificar las rutas.

Modifiqué ligeramente el código para que pueda pasar la información de la base de datos en la línea de comando en lugar de compilarla en el archivo .class. Simplemente ejecútelo sin argumentos y mostrará un mensaje que muestra qué argumentos espera. El código + modificaciones del csd es:

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());
            }
        }
    }
}
vidarsk
fuente
1

Agregué un código de /programming/3313020/write-x509-certificate-into-pem-formatted-string-in-java para generar los certificados como PEM, y eliminé la necesidad de especificar un db, nombre de usuario o contraseña (no son necesarios para obtener el certificado).

Usando esto, pude verificar que un reinicio de PostgreSQL desafortunadamente parece necesario para cambiar a un nuevo certificado.

Al no ser un desarrollador de Java, mis pasos para compilar y ejecutar probablemente no sean tan buenos, pero funcionan, siempre que pueda encontrar un jdbc postgresql

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

Compilar:

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

Correr:

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

Salida de muestra:

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:
...

Fuente:

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]));
            }
        }
    }
}
Cameron Kerr
fuente