จะตรวจสอบใบรับรอง SSL ของเซิร์ฟเวอร์ PostgreSQL ได้อย่างไร


14

สมมติว่ามีเซิร์ฟเวอร์ PostgreSQL ทำงานอยู่และเปิดใช้งาน SSL แล้ว การใช้เครื่องมือ "มาตรฐาน" Linux และ PostgreSQL ฉันจะตรวจสอบใบรับรอง SSL ได้อย่างไร

openssl x509 -text ...ฉันหวังสำหรับการส่งออกคล้ายกับสิ่งที่คุณจะได้รับจากการทำงาน และฉันหวังว่าจะได้รับคำตอบจากบรรทัดคำสั่งหนึ่งหรือสองซับดังนั้นฉันไม่ต้องหันไปใช้แพ็คเก็ตดมกลิ่น

ฉันไม่สามารถเข้าถึงเซิร์ฟเวอร์ PostgreSQL ดังนั้นฉันจึงไม่สามารถดูไฟล์การกำหนดค่าได้โดยตรง

ฉันไม่ได้ลงชื่อเข้าใช้ superuser ดังนั้นฉันจึงไม่สามารถรับค่าของการssl_cert_fileตั้งค่าpg_read_fileได้

การใช้openssl s_client -connect ...ไม่ทำงานเนื่องจาก PostgreSQL ดูเหมือนจะไม่ต้องการจับมือ SSL ทันที

จากการดูpsqlเอกสารอย่างรวดเร็วฉันไม่สามารถหาพารามิเตอร์บรรทัดคำสั่งที่ทำให้มันแสดงข้อมูลเมื่อเริ่มต้น (แม้ว่ามันจะแสดงข้อมูลตัวเลขบางอย่างให้ฉัน)

คำตอบ:


7

ดูเหมือนว่าs_clientเครื่องมือของ OpenSSL ได้เพิ่มการสนับสนุน Postgres โดยใช้-starttlsใน 1.1.1 ดังนั้นตอนนี้คุณสามารถใช้เครื่องมือบรรทัดคำสั่งของ OpenSSL ได้อย่างเต็มประสิทธิภาพโดยไม่มีสคริปต์ตัวช่วยเพิ่มเติม:

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

อ้างอิง:


10

ตามแนวคิดในข้อคิดเห็นของ Craig Ringer:

ทางเลือกหนึ่งคือการแก้ไขopenssl s_clientการจับมือกับโปรโตคอล PostgreSQL คุณสามารถทำได้ด้วย Java ด้วยการส่ง SSLSocketFactory ที่กำหนดเองไปยัง PgJDBC ฉันไม่แน่ใจว่ามีตัวเลือกง่าย ๆ

... ฉันเขียนโรงงานซ็อกเก็ต SSL แบบง่าย ฉันคัดลอกรหัสของคลาสของ PgJDBC NonValidatingFactoryและเพิ่งเพิ่มรหัสเพื่อพิมพ์ใบรับรอง

นี่คือสิ่งที่ดูเหมือนเมื่อทุกคนพูดและทำ:

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

คุณร็อค เพิ่งเพิ่มสิ่งนี้ไปยัง install-cert github.com/spyhunter99/installcert
spy

Awsome ขอบคุณมาก สำหรับคนที่ไม่ต้องการใช้ PGSimpleDataSource นี่คือตัวแปรสำหรับการใช้การตั้งค่าไดรเวอร์ 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

หากคุณไม่ต้องการรำคาญกับการติดตั้งจาวาและการคอมไพล์และคุณมี python อยู่แล้วคุณสามารถลองใช้สคริปต์ python นี้ได้ที่: https://github.com/thusoy/postgres-mitm/blob/master/postgres_get_server_cert.py

ฉันใช้มันเพื่อตรวจสอบวันที่ของใบรับรอง:

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

หรือสำหรับใบรับรองเต็มรูปแบบเป็นข้อความ:

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

1
หากต้องการใช้งานโดยไม่ต้องติดตั้ง: curl https://raw.githubusercontent.com/thusoy/postgres-mitm/master/postgres_get_server_cert.py | python - example.com:5432(แต่ต้องแน่ใจในสิ่งที่คุณดำเนินการในลักษณะนี้ !!)
35411

3

คำตอบของ csdช่วยฉันอย่างแท้จริง นี่คือคำแนะนำแบบละเอียดมากขึ้นสำหรับพวกเราที่ไม่ทราบหรือลืมจาวา

  1. ตรวจสอบให้แน่ใจว่าเซิร์ฟเวอร์ของคุณสามารถคอมไพล์ Java ได้ ลองใช้คำสั่ง "ซึ่ง javac" หากมันแสดงผลออกมาเป็น "... ไม่มี javac ใน ... " คุณต้องติดตั้ง JDK (JRE ใช้งานไม่ได้มันมี "java" แต่ไม่ใช่ "javac")

  2. ติดตั้ง postgresql-jdbc หากคุณยังไม่มี สำหรับ RHEL6 คำสั่งคือ "yum install postgresql-jdbc" กำหนดตำแหน่งที่ติดตั้งไฟล์ jar จะมีหลายคนหนึ่งสำหรับแต่ละรุ่น ฉันใช้ "/usr/share/java/postgresql-jdbc3.jar"

  3. คัดลอกรหัสของ csd และใส่ข้อมูลฐานข้อมูล (คำตอบอื่น ๆ ) หรือใช้เวอร์ชันที่แก้ไขเล็กน้อยของฉันในตอนท้ายของคำตอบนี้ บันทึกไว้ในไฟล์ที่เรียกว่า "ShowPostgreSQLCert.java" เรื่องบน / ตัวเล็กเรียกว่าอะไรและมันจะไม่รวบรวม

  4. ในไดเร็กทอรีที่มีไฟล์ ShowPostgreSQLCert.java ให้รันคำสั่งต่อไปนี้ (แก้ไขตำแหน่งของ postgresql-jdbc3.jar หากต้องการ): "javac -cp /usr/share/java/postgresql-jdbc3.jar ShowPostgreSQLCert.java" ตอนนี้คุณควรมี 3 .class ไฟล์ในไดเรกทอรีเดียวกัน

  5. ขั้นตอนสุดท้ายให้รันคำสั่งต่อไปนี้: "java -cp.: / usr / share / java / postgresql-jdbc3.jar ShowPostgreSQLCert" ส่วน "." after "-cp" หมายความว่าควรค้นหา dir ปัจจุบันสำหรับไฟล์. class คุณสามารถแทรกพา ธ แบบเต็มไปยังไฟล์คลาสได้ที่นี่เพียงจำไว้ว่าให้ ":" อยู่ระหว่างพา ธ และตำแหน่งของไฟล์. jar

  6. หากคุณต้องการเรียกใช้คำสั่งบนเครื่องอื่นคุณจำเป็นต้องติดตั้งไฟล์ jar เดียวกัน (postgresql-jdbc3.jar) หรือคุณอาจจะเพียงแค่คัดลอกจากเซิร์ฟเวอร์ที่คุณรวบรวมไฟล์. class จากนั้นเพียงคัดลอกไฟล์. class ทับและเรียกใช้คำสั่งจาก 5 หลังจากปรับเปลี่ยนพา ธ

ฉันปรับเปลี่ยนรหัสเล็กน้อยเพื่อให้คุณสามารถส่งผ่านข้อมูลฐานข้อมูลในบรรทัดคำสั่งแทนที่จะรวบรวมเป็นไฟล์. class เพียงแค่เรียกใช้โดยไม่มีข้อโต้แย้งใด ๆ และมันจะแสดงข้อความแสดงว่าข้อโต้แย้งใดที่มันคาดหวัง รหัส + การแก้ไขของ csd คือ:

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

ฉันเพิ่มรหัสบางส่วนจาก/programming/3313020/write-x509-certificate-into-pem-formatted-string-in-javaออกใบรับรองเป็น PEM และลบความจำเป็นในการระบุ db ชื่อผู้ใช้หรือรหัสผ่าน (ไม่ต้องการรับใบรับรอง)

เมื่อใช้สิ่งนี้ฉันสามารถตรวจสอบได้ว่าการรีสตาร์ท PostgreSQL นั้นดูเหมือนว่าจำเป็นต้องเปลี่ยนเป็นใบรับรองใหม่

ไม่ใช่นักพัฒนา Java ขั้นตอนในการสร้างและเรียกใช้อาจไม่ยอดเยี่ยมนัก แต่ทำงานได้ตราบใดที่คุณสามารถหา postgresql jdbc

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

เพื่อรวบรวม:

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

วิ่ง:

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

ตัวอย่างผลลัพธ์:

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

ที่มา:

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]));
            }
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.