On Wed, Jan 17, 2018 at 09:09:50AM +0900, Michael Paquier wrote:
> On Tue, Jan 16, 2018 at 11:21:22AM -0500, Bruce Momjian wrote:
> > On Tue, Jan 16, 2018 at 02:33:05PM +0900, Michael Paquier wrote:
> > > This bit is important. I am happy that your patch mentions that
> > > intermediate certificates avoid the need to store root ones on the
> > > client. Should the docs mention terms like "chain of trust"?
> > 
> > I think the question is how much do we want to "teach" people in our
> > docs.  We do oddly but wisely link from our docs to HP OpenVMS docs
> > about how the chain of trust works:
> > 
> >     http://h41379.www4.hpe.com/doc/83final/ba554_90007/ch04s02.html
> > 
> > I will write up a paragraph about the concepts for our docs for the
> > group's review.
> 
> As a separate patch, I think that it would be fine as well.

I ended up merging the "chain of trust" changes into the "intermediate"
patch since they affect adjacent sections of the docs.  You can see this
as the first attached patch.

> > > Perhaps the docs could also include an example of command to create a
> > > root and an intermediate certificate in runtime.sgml or such?
> > 
> > Yes, I have thought about that.  My presentation has clear examples that
> > we can use, again based on Stephen and David's scripts using v3_ca.  I
> > will work up a possible patch for that too.
> 
> That too.

I did that as a separate patch, which is the second attachment.

> > > On top of that, src/test/ssl does not provide any kind of coverage for
> > > that. It would be an area of improvement for those tests.
> > 
> > Wow, I have no idea how to do that.  Let me look.  Seems I have more
> > work to do.
> 
> You would need to update src/test/ssl/Makefile to generate those
> intermediate CAs, and then make ServerSetup::switch_server_cert smarter
> in the way the series of certificates are handled. A suggestion I have
> would be to create each certificate file separately and change the
> routine so as it uses an array in input, the order of the items defining
> what's the order the the data. For the client there is sslrootcert, so I
> guess that a small routine able to take a set of certs and append them
> to a single file would make it as well (switch_server_cert should use
> it).

I don't think I will work on the testing changes.

-- 
  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..ec85132
*** a/doc/src/sgml/libpq.sgml
--- b/doc/src/sgml/libpq.sgml
*************** ldap://ldap.acme.com/cn=dbserver,cn=host
*** 7574,7590 ****
     the server certificate. This means that it is possible to spoof the server
     identity (for example by modifying a DNS record or by taking over the server
     IP address) without the client knowing. In order to prevent spoofing,
!    <acronym>SSL</acronym> certificate verification must be used.
    </para>
  
    <para>
     If the parameter <literal>sslmode</literal> is set to <literal>verify-ca</literal>,
     libpq will verify that the server is trustworthy by checking the
!    certificate chain up to a trusted certificate authority
!    (<acronym>CA</acronym>). If <literal>sslmode</literal> is set to <literal>verify-full</literal>,
!    libpq will <emphasis>also</emphasis> verify that the server host name matches its
!    certificate. The SSL connection will fail if the server certificate cannot
!    be verified. <literal>verify-full</literal> is recommended in most
     security-sensitive environments.
    </para>
  
--- 7574,7610 ----
     the server certificate. This means that it is possible to spoof the server
     identity (for example by modifying a DNS record or by taking over the server
     IP address) without the client knowing. In order to prevent spoofing,
!    the client must be able to verify the server's identity via a chain of
!    trust.  A chain of trust is established by placing a root (self-signed)
!    certificate authority (<acronym>CA</acronym>) certificate on one
!    computer and a leaf certificate <emphasis>signed</emphasis> by the
!    root certificate on another computer.  It is also possible to use an
!    <quote>intermediate</quote> certificate which is signed by the root
!    certificate and signs leaf certificates.
!   </para>
! 
!   <para>
!    To allow the client to verify the identity of the server, place a root
!    certificate on the client and a leaf certificate signed by the root
!    certificate on the server.  To allow the server to verify the identity
!    of the client, place a root certificate on the server and a leaf and
!    optional intermediate certificates signed by the root certificate on
!    the client.  Intermediate certificates (usually stored with the leaf
!    certificate) can also be used to link the leaf certificate to the
!    root certificate.
    </para>
  
    <para>
+    Once a chain of trust has been established, there are two ways for
+    the client to validate the leaf certificate sent by the server.
     If the parameter <literal>sslmode</literal> is set to <literal>verify-ca</literal>,
     libpq will verify that the server is trustworthy by checking the
!    certificate chain up to the root certificate stored on the client.
!    If <literal>sslmode</literal> is set to <literal>verify-full</literal>,
!    libpq will <emphasis>also</emphasis> verify that the server host
!    name matches the name stored in the server certificate. The
!    SSL connection will fail if the server certificate cannot be
!    verified. <literal>verify-full</literal> is recommended in most
     security-sensitive environments.
    </para>
  
*************** 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>
--- 7621,7633 ----
    </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
*** 7641,7651 ****
    <title>Client Certificates</title>
  
    <para>
!    If the server requests a trusted client certificate,
!    <application>libpq</application> will send the certificate stored in
     file <filename>~/.postgresql/postgresql.crt</filename> in the user's home
!    directory.  The certificate must be signed by one of the certificate
!    authorities (<acronym>CA</acronym>) trusted by the server.  A matching
     private key file <filename>~/.postgresql/postgresql.key</filename> must also
     be present. The private
     key file must not allow any access to world or group; achieve this by the
--- 7661,7672 ----
    <title>Client Certificates</title>
  
    <para>
!    If the server attempts to verify the identity of the
!    client by requesting the client's leaf certificate,
!    <application>libpq</application> will send the certificates stored in
     file <filename>~/.postgresql/postgresql.crt</filename> in the user's home
!    directory.  The certificates must chain to the root certificate trusted
!    by the server.  A matching
     private key file <filename>~/.postgresql/postgresql.key</filename> must also
     be present. The private
     key file must not allow any access to world or group; achieve this by the
*************** 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">
--- 7681,7692 ----
    </para>
  
    <para>
!    The first certificate in <filename>postgresql.crt</filename> must be the
!    client's certificate because it must match the client's private key.
!    <quote>Intermediate</quote> certificates can be optionally appended
!    to the file &mdash; doing so avoids requiring storage of intermediate
!    certificates 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 ----
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
new file mode 100644
index a2ebd3e..dca113b
*** a/doc/src/sgml/runtime.sgml
--- b/doc/src/sgml/runtime.sgml
*************** pg_dumpall -p 5432 | psql -d postgres -p
*** 2385,2394 ****
    </sect2>
  
    <sect2 id="ssl-certificate-creation">
!    <title>Creating a Self-signed Certificate</title>
  
     <para>
!      To create a quick self-signed certificate for the server, valid for 365
       days, use the following <productname>OpenSSL</productname> command,
       replacing <replaceable>yourdomain.com</replaceable> with the server's host name:
  <programlisting>
--- 2385,2394 ----
    </sect2>
  
    <sect2 id="ssl-certificate-creation">
!    <title>Creating Certificates</title>
  
     <para>
!      To create a simple self-signed certificate for the server, valid for 365
       days, use the following <productname>OpenSSL</productname> command,
       replacing <replaceable>yourdomain.com</replaceable> with the server's host name:
  <programlisting>
*************** chmod og-rwx server.key
*** 2406,2419 ****
     </para>
  
     <para>
!     A self-signed certificate can be used for testing, but a certificate
!     signed by a certificate authority (<acronym>CA</acronym>) (either one of the
!     global <acronym>CAs</acronym> or a local one) should be used in production
!     so that clients can verify the server's identity. If all the clients
!     are local to the organization, using a local <acronym>CA</acronym> is
!     recommended.
     </para>
  
    </sect2>
  
   </sect1>
--- 2406,2476 ----
     </para>
  
     <para>
!     While a self-signed certificate can be used for testing, a certificate
!     signed by a certificate authority (<acronym>CA</acronym>) (usually an
!     enterprise-wide root <acronym>CAs</acronym>) should be used in production.
     </para>
  
+    <para>
+     To create a server certificate whose identity can be validated
+     by clients, first create a public/private key pair and certificate
+     signing request (<acronym>CSR</acronym>):
+ <programlisting>
+ openssl req -new -nodes -text -out root.csr \
+   -keyout root.key -subj "/CN=<replaceable>root.yourdomain.com</replaceable>"
+ chmod og-rwx root.key
+ </programlisting>
+     Then, sign the request with the the private key to create a root
+ certificate authority:
+ <programlisting>
+ openssl x509 -req -in root.csr -text -days 365 \
+   -extfile /etc/ssl/openssl.cnf -extensions v3_ca \
+   -signkey root.key -out root.crt
+ </programlisting>
+     Finally, create a server certificate signed by the new root certificate
+     authority:
+ <programlisting>
+ openssl req -new -nodes -text -out server.csr \
+   -keyout server.key -subj "/CN=<replaceable>dbhost.yourdomain.com</replaceable>"
+ chmod og-rwx server.key
+ 
+ openssl x509 -req -in server.csr -text -days 365 \
+   -CA root.crt -CAkey root.key -CAcreateserial \
+   -out server.crt
+ </programlisting>
+     <filename>server.crt</filename> and <filename>server.key</filename>
+     should be stored on the server, and <filename>root.crt</filename> should
+     be stored on the client so the client can verify that the server's leaf
+     certificate was signed by its trusted root certificate.
+    </para>
+ 
+    <para>
+     It is also possible to create a chain of trust that includes
+     intermediate certificates:
+ <programlisting>
+ openssl req -new -nodes -text -out root.csr \
+   -keyout root.key -subj "/CN=<replaceable>root.yourdomain.com</replaceable>"
+ chmod og-rwx root.key
+ openssl x509 -req -in root.csr -text -days 365 \
+   -extfile /etc/ssl/openssl.cnf -extensions v3_ca \
+   -signkey root.key -out root.crt
+ 
+ openssl req -new -nodes -text -out intermediate.csr \
+   -keyout intermediate.key -subj "/CN=<replaceable>intermediate.yourdomain.com</replaceable>"
+ chmod og-rwx intermediate.key
+ openssl x509 -req -in intermediate.csr -text -days 365 \
+   -extfile /etc/ssl/openssl.cnf -extensions v3_ca \
+   -CA root.crt -CAkey root.key -CAcreateserial \
+   -out intermediate.crt
+ 
+ openssl req -new -nodes -text -out server.csr \
+   -keyout server.key -subj "/CN=<replaceable>dbhost.yourdomain.com</replaceable>"
+ chmod og-rwx server.key
+ openssl x509 -req -in server.csr -text -days 365 \
+   -CA intermediate.crt -CAkey intermediate.key -CAcreateserial \
+   -out server.crt
+ </programlisting>
+    </para>
    </sect2>
  
   </sect1>

Reply via email to