Como examinar o certificado SSL do servidor PostgreSQL?

14

Suponha que haja um servidor PostgreSQL em execução e que tenha o SSL ativado. Usando ferramentas Linux "padrão" e PostgreSQL, como posso examinar seu certificado SSL?

Espero uma saída semelhante à que você obteria da execução openssl x509 -text .... E estou esperando uma resposta de linha de comando de um ou dois liners para que eu não precise recorrer à execução de um farejador de pacotes.

Como não tenho acesso ao servidor PostgreSQL, não posso olhar diretamente para os arquivos de configuração.

Eu não tenho um logon de superusuário, portanto não consigo obter o valor da ssl_cert_fileconfiguração e, pg_read_fileem seguida, utilizá-la.

O uso openssl s_client -connect ...não funciona porque o PostgreSQL parece não querer executar o handshake SSL imediatamente.

De uma rápida olhada na psqldocumentação, não foi possível encontrar um parâmetro de linha de comando que o faça mostrar essas informações na inicialização. (Embora ele me mostre certas informações cifradas.)

csd
fonte

Respostas:

6

Parece que a s_clientferramenta do OpenSSL adicionou suporte ao Postgres usando o -starttlsno 1.1.1, então agora você pode usar todo o poder das ferramentas de linha de comando do OpenSSL sem scripts auxiliar adicionais:

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

Referências:

Adam Batkin
fonte
10

Seguindo a ideia do comentário de Craig Ringer:

Uma opção é corrigir o openssl s_clientaperto de mão com o protocolo PostgreSQL. Provavelmente, você também pode fazê-lo com Java, passando um SSLSocketFactory personalizado para PgJDBC. Não tenho certeza se existem opções simples.

... Eu escrevi uma fábrica de soquetes SSL simples. Copiei o código da própria NonValidatingFactoryclasse do PgJDBC e apenas adicionei o código para imprimir os certificados.

Aqui está o que parecia, quando tudo foi dito e feito:

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
fonte
você é demais. Acabei de adicionar isso ao install-cert github.com/spyhunter99/installcert
spy
Awsome muito obrigado. Para pessoas que não desejam usar o PGSimpleDataSource. Aqui está a variante para usar a configuração normal do driver 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

Se você não deseja se preocupar com a instalação de java e compilação, e já possui python, tente este script python: https://github.com/thusoy/postgres-mitm/blob/master/postgres_get_server_cert.py

Eu o uso para verificar as datas do certificado:

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

Ou para o certificado completo como texto:

postgres_get_server_cert.py example.com:5432 | openssl x509 -noout -text
mivk
fonte
1
Para usá-lo sem instalar: curl https://raw.githubusercontent.com/thusoy/postgres-mitm/master/postgres_get_server_cert.py | python - example.com:5432(mas certifique-se de que você executar este caminho !!)
Yajo
3

A resposta do csd realmente me salvou. Aqui está uma explicação mais detalhada para quem não conhece ou esqueceu o java.

  1. Verifique se o seu servidor pode compilar java. Tente o comando "what javac", se ele exibir algo como "... no javac in ...", você precisará instalar um JDK (o JRE não funcionará, ele possui "java", mas não "javac").

  2. Instale o postgresql-jdbc se ainda não o tiver. Para RHEL6, o comando é "yum install postgresql-jdbc". Descubra onde os arquivos jar estão instalados. Haverá vários deles, um para cada versão. Eu usei "/usr/share/java/postgresql-jdbc3.jar".

  3. Copie o código do csd e insira as informações do banco de dados (a outra resposta) ou use minha versão ligeiramente modificada no final desta resposta. Salve-o em um arquivo chamado exatamente "ShowPostgreSQLCert.java". Questões em maiúsculas / minúsculas, chame-o de qualquer outra coisa e não será compilado.

  4. No diretório com o arquivo ShowPostgreSQLCert.java, execute o seguinte comando (modifique o local do postgresql-jdbc3.jar, se necessário): "javac -cp /usr/share/java/postgresql-jdbc3.jar ShowPostgreSQLCert.java". Agora você deve ter 3 arquivos .class no mesmo diretório.

  5. Por fim, execute o seguinte comando: "java -cp.: / Usr / share / java / postgresql-jdbc3.jar ShowPostgreSQLCert". O "." depois de "-cp" significa que ele deve procurar no diretório atual os arquivos .class. Você pode inserir o caminho completo para os arquivos de classe aqui, lembre-se de manter o ":" entre o caminho e o local do arquivo .jar.

  6. Se você precisar executar o comando em uma máquina diferente, precisará ter o mesmo arquivo jar instalado (postgresql-jdbc3.jar), ou provavelmente poderá copiá-lo do servidor em que compilou os arquivos .class. Em seguida, basta copiar os arquivos .class e executar o comando a partir de 5. depois de modificar os caminhos.

Modifiquei levemente o código para que você possa transmitir informações do banco de dados na linha de comando, em vez de compilá-las no arquivo .class. Basta executá-lo sem argumentos e ele exibirá uma mensagem mostrando quais argumentos ele espera. O código do csd + modificaçõ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
fonte
1

Adicionei algum código de /programming/3313020/write-x509-certificate-into-pem-formatted-string-in-java para gerar os certificados como PEM e removi a necessidade de especificar um db, nome de usuário ou senha (eles não são necessários para obter o certificado).

Usando isso, pude verificar se, infelizmente, é necessário reiniciar o PostgreSQL para mudar para um novo certificado.

Como não sou desenvolvedor Java, minhas etapas para criar e executar provavelmente não são tão boas, mas funcionam, desde que você encontre um jdbc do 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

Para correr:

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

Saída de amostra:

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

Fonte:

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
fonte