Jak sprawdzić certyfikat SSL serwera PostgreSQL?

14

Załóżmy, że działa serwer PostgreSQL i ma włączony protokół SSL. Jak mogę sprawdzić certyfikat SSL za pomocą „standardowych” narzędzi Linux i PostgreSQL?

Mam nadzieję, że dane wyjściowe będą podobne do tych, które można uzyskać po uruchomieniu openssl x509 -text .... Mam nadzieję na jedno- lub dwuliniową odpowiedź w wierszu poleceń, więc nie muszę uciekać się do uruchamiania sniffera pakietów.

Nie mam dostępu do serwera PostgreSQL, więc nie mogę bezpośrednio przeglądać jego plików konfiguracyjnych.

Nie mam loginu superużytkownika, więc nie mogę uzyskać wartości ssl_cert_fileustawienia, a następnie pg_read_filego włączyć .

Korzystanie openssl s_client -connect ...nie działa, ponieważ PostgreSQL nie chce od razu wykonać uzgadniania SSL.

Po szybkim spojrzeniu na psqldokumentację nie mogłem znaleźć parametru wiersza polecenia, który wyświetlałby te informacje podczas uruchamiania. (Chociaż pokazuje mi pewne informacje o szyfrach).

csd
źródło

Odpowiedzi:

7

Wygląda na to, że s_clientnarzędzie OpenSSL dodało obsługę Postgres przy użyciu wersji -starttls1.1.1, dzięki czemu możesz teraz korzystać z pełnej mocy narzędzi wiersza poleceń OpenSSL bez dodatkowych skryptów pomocniczych:

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

Bibliografia:

Adam Batkin
źródło
10

Zgodnie z ideą zawartą w komentarzu Craiga Ringera:

Jedną z opcji jest załatanie openssl s_clientuzgadniania za pomocą protokołu PostgreSQL. Prawdopodobnie można to również zrobić w Javie, przekazując niestandardowy obiekt SSLSocketFactory do PgJDBC. Nie jestem pewien, czy są jakieś proste opcje.

... napisałem prostą fabrykę gniazd SSL. Skopiowałem kod własnej NonValidatingFactoryklasy PgJDBC i właśnie dodałem kod, aby wydrukować certyfikaty.

Oto jak to wyglądało, kiedy wszystko zostało powiedziane i zrobione:

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
źródło
rządzisz. Właśnie dodałem to do install-cert github.com/spyhunter99/installcert
spy
Bardzo dziękuję. Dla osób, które nie chcą korzystać z PGSimpleDataSource. Oto wariant użycia normalnej konfiguracji sterownika 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

Jeśli nie chcesz zawracać sobie głowy instalowaniem Java i kompilacją, a masz już Pythona, możesz wypróbować ten skrypt Pythona: https://github.com/thusoy/postgres-mitm/blob/master/postgres_get_server_cert.py

Używam go do sprawdzania dat certyfikatów:

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

Lub dla pełnego certyfikatu jako tekst:

postgres_get_server_cert.py example.com:5432 | openssl x509 -noout -text
mivk
źródło
1
Aby użyć go bez instalowania: curl https://raw.githubusercontent.com/thusoy/postgres-mitm/master/postgres_get_server_cert.py | python - example.com:5432(ale upewnij się, co zrobisz w ten sposób !!)
Yajo
3

Odpowiedź csd naprawdę mnie uratowała. Oto bardziej szczegółowy przewodnik dla tych z nas, którzy nie znają Java lub zapomnieli o niej.

  1. Upewnij się, że Twój serwer może skompilować Java. Spróbuj wykonać polecenie „który javac”, jeśli wyświetla coś takiego jak „... brak javac w ...”, to musisz zainstalować JDK (JRE nie będzie działać, ma on „java”, ale nie „javac”).

  2. Zainstaluj postgresql-jdbc, jeśli jeszcze go nie masz. W przypadku RHEL6 polecenie brzmi „yum install postgresql-jdbc”. Dowiedz się, gdzie są zainstalowane pliki jar. Będzie ich kilka, po jednej dla każdej wersji. Użyłem „/usr/share/java/postgresql-jdbc3.jar”.

  3. Skopiuj kod csd i wstaw informacje o bazie danych (druga odpowiedź) lub użyj mojej nieco zmodyfikowanej wersji na końcu tej odpowiedzi. Zapisz go w pliku o nazwie „ShowPostgreSQLCert.java”. Duże / małe litery mają znaczenie, nazwij to jakikolwiek inny, a to się nie skompiluje.

  4. W katalogu z plikiem ShowPostgreSQLCert.java uruchom następującą komendę (w razie potrzeby zmień położenie postgresql-jdbc3.jar): „javac -cp /usr/share/java/postgresql-jdbc3.jar ShowPostgreSQLCert.java”. Powinieneś teraz mieć 3 pliki .class w tym samym katalogu.

  5. Na koniec uruchom następującą komendę: „java -cp.: / Usr / share / java / postgresql-jdbc3.jar ShowPostgreSQLCert”. „.” po „-cp” oznacza, że ​​powinien szukać w bieżącym katalogu plików .class. Tutaj możesz wstawić pełną ścieżkę do plików klas, pamiętaj tylko, aby zachować „:” między ścieżką a lokalizacją pliku .jar.

  6. Jeśli chcesz uruchomić polecenie na innym komputerze, musisz mieć zainstalowany ten sam plik jar (postgresql-jdbc3.jar), lub prawdopodobnie możesz go po prostu skopiować z serwera, na którym skompilowałeś pliki .class. Następnie po prostu skopiuj pliki .class i uruchom polecenie z 5. po zmodyfikowaniu ścieżek.

Lekko zmodyfikowałem kod, abyś mógł przekazywać informacje z bazy danych w wierszu poleceń zamiast kompilować je w pliku .class. Po prostu uruchom go bez żadnych argumentów, a wyświetli się komunikat, którego argumentów oczekuje. Kod csd + modyfikacje to:

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
źródło
1

Dodałem trochę kodu z /programming/3313020/write-x509-certificate-into-pem-formatted-string-in-java, aby wyprowadzić certyfikaty jako PEM, i usunąłem potrzebę określania db, nazwa użytkownika lub hasło (nie są potrzebne do uzyskania certyfikatu).

Dzięki temu mogłem sprawdzić, czy ponowne uruchomienie PostgreSQL wydaje się niestety konieczne, aby przełączyć się na nowy certyfikat.

Nie będąc programistą Java, moje kroki, aby zbudować i uruchomić, prawdopodobnie nie są tak świetne, ale działają, o ile można znaleźć postgresql jdbc

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

Kompilować:

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

Biegać:

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

Przykładowe dane wyjściowe:

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

Źródło:

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
źródło