We have been confused by the behavior of intermediate certificates in Postgres for many years. Some people put the intermediate certificates only on the server and they were supplied to the client, while other people couldn't get that to work. In our documentation we recommended storing intermediate certificates on the client and server.
As part of research for my security talks: https://momjian.us/main/presentations/security.html I asked Stephen Frost and David Steele for details on the arcane art of SSL certificate creation. They showed me scripts they use and explained that they properly pass intermediate certificates to clients. The trick was to use the v3_ca extension when creating root and intermediate certificates. My talk documents this behavior. In this talk: https://momjian.us/main/writings/pgsql/tls.pdf slide 47 and 49 use -extensions v3_ca. Slides 73 and 74 show that the intermediate is not needed on the client if it is created with v3_ca and exist on the server. Slide 75 shows that the server certificate must be first in server.crt. I have created the attached doc patch to add this information to our docs. I would like to backpatch this since what we have now, while it works, is inaccurate. -- Bruce Momjian <br...@momjian.us> http://momjian.us EnterpriseDB http://enterprisedb.com + As you are, so once was I. As I am, so you will be. + + Ancient Roman grave inscription +
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml new file mode 100644 index 4e46451..801b033 *** a/doc/src/sgml/libpq.sgml --- b/doc/src/sgml/libpq.sgml *************** ldap://ldap.acme.com/cn=dbserver,cn=host *** 7601,7613 **** </para> <para> ! To allow server certificate verification, the certificate(s) of one or more ! trusted <acronym>CA</acronym>s must be ! placed in the file <filename>~/.postgresql/root.crt</filename> in the user's home ! directory. If intermediate <acronym>CA</acronym>s appear in ! <filename>root.crt</filename>, the file must also contain certificate ! chains to their root <acronym>CA</acronym>s. (On Microsoft Windows the file is named ! <filename>%APPDATA%\postgresql\root.crt</filename>.) </para> <para> --- 7601,7613 ---- </para> <para> ! To allow server certificate verification, one or more root certificates ! must be placed in the file <filename>~/.postgresql/root.crt</filename> ! in the user's home directory. (On Microsoft Windows the file is named ! <filename>%APPDATA%\postgresql\root.crt</filename>.) Intermediate ! certificates should also be added to the file if they are needed to link ! the certificate chain sent by the server to the root certificates ! stored on the client. </para> <para> *************** ldap://ldap.acme.com/cn=dbserver,cn=host *** 7660,7682 **** </para> <para> ! In some cases, the client certificate might be signed by an ! <quote>intermediate</quote> certificate authority, rather than one that is ! directly trusted by the server. To use such a certificate, append the ! certificate of the signing authority to the <filename>postgresql.crt</filename> ! file, then its parent authority's certificate, and so on up to a certificate ! authority, <quote>root</quote> or <quote>intermediate</quote>, that is trusted by ! the server, i.e. signed by a certificate in the server's root CA file ! (<xref linkend="guc-ssl-ca-file"/>). ! </para> ! ! <para> ! Note that the client's <filename>~/.postgresql/root.crt</filename> lists the top-level CAs ! that are considered trusted for signing server certificates. In principle it need ! not list the CA that signed the client's certificate, though in most cases ! that CA would also be trusted for server certificates. </para> - </sect2> <sect2 id="libpq-ssl-protection"> --- 7660,7672 ---- </para> <para> ! The first certificate in <filename>postgresql.crt</filename> must be the ! clients's certificate because it must match the client's private key. ! The certificates of <quote>intermediate</quote> certificate authorities ! can be optionally appended to the file. Doing this avoids requiring ! the intermediate certificates to be stored on the server (<xref ! linkend="guc-ssl-ca-file"/>). </para> </sect2> <sect2 id="libpq-ssl-protection"> diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml new file mode 100644 index a2ebd3e..6352d53 *** a/doc/src/sgml/runtime.sgml --- b/doc/src/sgml/runtime.sgml *************** pg_dumpall -p 5432 | psql -d postgres -p *** 2247,2286 **** </para> <para> ! In some cases, the server certificate might be signed by an ! <quote>intermediate</quote> certificate authority, rather than one that is ! directly trusted by clients. To use such a certificate, append the ! certificate of the signing authority to the <filename>server.crt</filename> file, ! then its parent authority's certificate, and so on up to a certificate ! authority, <quote>root</quote> or <quote>intermediate</quote>, that is trusted by ! clients, i.e. signed by a certificate in the clients' ! <filename>root.crt</filename> files. </para> <sect2 id="ssl-client-certificates"> <title>Using Client Certificates</title> <para> ! To require the client to supply a trusted certificate, place ! certificates of the certificate authorities (<acronym>CA</acronym>s) ! you trust in a file named <filename>root.crt</filename> in the data directory, set the parameter <xref linkend="guc-ssl-ca-file"/> in ! <filename>postgresql.conf</filename> to <literal>root.crt</literal>, ! and add the authentication option <literal>clientcert=1</literal> to the ! appropriate <literal>hostssl</literal> line(s) in <filename>pg_hba.conf</filename>. ! A certificate will then be requested from the client during ! SSL connection startup. (See <xref linkend="libpq-ssl"/> for a ! description of how to set up certificates on the client.) The server will verify that the client's certificate is signed by one of the trusted certificate authorities. </para> <para> ! If intermediate <acronym>CA</acronym>s appear in ! <filename>root.crt</filename>, the file must also contain certificate ! chains to their root <acronym>CA</acronym>s. Certificate Revocation List ! (CRL) entries ! are also checked if the parameter <xref linkend="guc-ssl-crl-file"/> is set. <!-- If this URL changes replace it with a URL to www.archive.org. --> (See <ulink url="http://h71000.www7.hp.com/doc/83final/ba554_90007/ch04s02.html"></ulink> --- 2247,2292 ---- </para> <para> ! The first certificate in <filename>server.crt</filename> must be the ! server's certificate because it must match the server's private key. ! The certificates of <quote>intermediate</quote> certificate authorities ! can also be appended to the file. Doing this avoids the necessity of ! storing intermediate certificates on clients, assuming the root and ! intermediate certificates were created with <literal>v3_ca</literal> ! extensions. This allows easier expiration of intermediate certificates. ! </para> ! ! <para> ! It is not necessary to add the root certificate to ! <filename>server.crt</filename>. Instead, clients must have the root ! certificate of the server's certificate chain. </para> <sect2 id="ssl-client-certificates"> <title>Using Client Certificates</title> <para> ! To require the client to supply a trusted certificate, ! place certificates of the root certificate authorities ! (<acronym>CA</acronym>s) you trust in a file in the data directory, set the parameter <xref linkend="guc-ssl-ca-file"/> in ! <filename>postgresql.conf</filename> to the new file name, and add the ! authentication option <literal>clientcert=1</literal> to the appropriate ! <literal>hostssl</literal> line(s) in <filename>pg_hba.conf</filename>. ! A certificate will then be requested from the client during SSL ! connection startup. (See <xref linkend="libpq-ssl"/> for a description ! of how to set up certificates on the client.) The server will verify that the client's certificate is signed by one of the trusted certificate authorities. </para> <para> ! Intermediate certificates that chain up to existing root certificates ! can also appear in the <xref linkend="guc-ssl-ca-file"/> file if ! you wish to avoid storing them on clients (assuming the root and ! intermediate certificates were created with <literal>v3_ca</literal> ! extensions). Certificate Revocation List (CRL) entries are also ! checked if the parameter <xref linkend="guc-ssl-crl-file"/> is set. <!-- If this URL changes replace it with a URL to www.archive.org. --> (See <ulink url="http://h71000.www7.hp.com/doc/83final/ba554_90007/ch04s02.html"></ulink> *************** pg_dumpall -p 5432 | psql -d postgres -p *** 2297,2310 **** </para> <para> - Note that the server's <filename>root.crt</filename> lists the top-level - CAs that are considered trusted for signing client certificates. - In principle it need - not list the CA that signed the server's certificate, though in most cases - that CA would also be trusted for client certificates. - </para> - - <para> If you are setting up client certificates, you may wish to use the <literal>cert</literal> authentication method, so that the certificates control user authentication as well as providing connection security. --- 2303,2308 ----