From 22ffcb11df5567f7669016846a6386273365e29b Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Tue, 31 Oct 2023 15:19:15 -0400
Subject: [PATCH v3 1/5] TDE: doc updates

---
 doc/src/sgml/config.sgml              | 127 +++++++++++++---
 doc/src/sgml/database-encryption.sgml | 149 ++++++++++++++++++
 doc/src/sgml/filelist.sgml            |   1 +
 doc/src/sgml/installation.sgml        |   2 +-
 doc/src/sgml/postgres.sgml            |   1 +
 doc/src/sgml/ref/allfiles.sgml        |   1 +
 doc/src/sgml/ref/initdb.sgml          |  47 ++++++
 doc/src/sgml/ref/pg_alterckey.sgml    | 210 ++++++++++++++++++++++++++
 doc/src/sgml/ref/pg_ctl-ref.sgml      |  14 ++
 doc/src/sgml/ref/pgupgrade.sgml       |  20 ++-
 doc/src/sgml/ref/postgres-ref.sgml    |  13 ++
 doc/src/sgml/reference.sgml           |   1 +
 doc/src/sgml/storage.sgml             |   5 +
 13 files changed, 571 insertions(+), 20 deletions(-)
 create mode 100644 doc/src/sgml/database-encryption.sgml
 create mode 100644 doc/src/sgml/ref/pg_alterckey.sgml

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index bd70ff2e4b..c40cbc78e3 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1564,19 +1564,31 @@ include_dir 'conf.d'
         mechanism is used.
        </para>
        <para>
-        The command must print the passphrase to the standard output and exit
-        with code 0.  In the parameter value, <literal>%p</literal> is
-        replaced by a prompt string.  (Write <literal>%%</literal> for a
-        literal <literal>%</literal>.)  Note that the prompt string will
-        probably contain whitespace, so be sure to quote adequately.  A single
-        newline is stripped from the end of the output if present.
+        The command must print the passphrase to the standard output
+        and exit with code 0.  It can prompt from the terminal if
+        <option>--authprompt</option> is used.  In the parameter
+        value, <literal>%R</literal> is replaced by a file descriptor
+        number opened to the terminal that started the server.  A file
+        descriptor is only available if enabled at server start via
+        <option>-R</option>.  If <literal>%R</literal> is specified and
+        no file descriptor is available, the server will not start.  Value
+        <literal>%p</literal> is replaced by a pre-defined prompt string.
+        (Write <literal>%%</literal> for a literal <literal>%</literal>.)
+        Note that the prompt string will probably contain whitespace,
+        so be sure to quote its use adequately.  Newlines are stripped
+        from the end of the output if present.
        </para>
+
        <para>
-        The command does not actually have to prompt the user for a
-        passphrase.  It can read it from a file, obtain it from a keychain
-        facility, or similar.  It is up to the user to make sure the chosen
-        mechanism is adequately secure.
+        Sample scripts can be found in
+        <filename>$SHAREDIR/auth_commands</filename>,
+        where <literal>$SHAREDIR</literal> means the
+        <productname>PostgreSQL</productname> installation's shared-data
+        directory, often <filename>/usr/local/share/postgresql</filename>
+        (use <command>pg_config --sharedir</command> to determine it if
+        you're not sure).
        </para>
+
        <para>
         This parameter can only be set in the <filename>postgresql.conf</filename>
         file or on the server command line.
@@ -1598,10 +1610,12 @@ include_dir 'conf.d'
         parameter is off (the default), then
         <varname>ssl_passphrase_command</varname> will be ignored during a
         reload and the SSL configuration will not be reloaded if a passphrase
-        is needed.  That setting is appropriate for a command that requires a
-        TTY for prompting, which might not be available when the server is
-        running.  Setting this parameter to on might be appropriate if the
-        passphrase is obtained from a file, for example.
+        is needed.  This setting is appropriate for a command that requires a
+        terminal for prompting, which will likely not be available when the server is
+        running.  (<option>--authprompt</option> closes the terminal file
+        descriptor soon after server start.)   Setting this parameter on
+        might be appropriate, for example, if the passphrase is obtained
+        from a file.
        </para>
        <para>
         This parameter can only be set in the <filename>postgresql.conf</filename>
@@ -2773,7 +2787,9 @@ include_dir 'conf.d'
         <varname>max_wal_senders</varname> is non-zero.
         Note that changing <varname>wal_level</varname> to
         <literal>minimal</literal> makes previous base backups unusable
-        for point-in-time recovery and standby servers.
+        for point-in-time recovery and standby servers, which may
+        lead to data loss.  Cluster file encryption also does not support
+        <varname>wal_level</varname> <literal>minimal</literal>.
        </para>
        <para>
         In <literal>logical</literal> level, the same information is logged as
@@ -3126,9 +3142,10 @@ include_dir 'conf.d'
        </para>
 
        <para>
-        If data checksums are enabled, hint bit updates are always WAL-logged
-        and this setting is ignored. You can use this setting to test how much
-        extra WAL-logging would occur if your database had data checksums
+        If data checksums or cluster file encryption is enabled,
+        hint bit updates are always WAL-logged and this setting is
+        ignored. You can use this setting to test how much extra
+        WAL-logging would occur if your database had data checksums
         enabled.
        </para>
 
@@ -8520,6 +8537,64 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
     </variablelist>
    </sect1>
 
+   <sect1 id="runtime-config-encryption">
+    <title>Cluster File Encryption</title>
+
+    <variablelist>
+     <varlistentry id="guc-cluster-key-command" xreflabel="cluster_key_command">
+      <term><varname>cluster_key_command</varname> (<type>string</type>)
+      <indexterm>
+       <primary><varname>cluster_key_command</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        This option specifies an external command to obtain the cluster-level
+        key for cluster file encryption during server initialization and
+        server start.
+       </para>
+       <para>
+        The command must print the cluster key to the standard
+        output as 64 hexadecimal characters, and exit with code 0.
+        The command can prompt for the passphrase or PIN from the
+        terminal if <option>--authprompt</option> is used.  In the
+        parameter value, <literal>%R</literal> is replaced by a file
+        descriptor number opened to the terminal that started the server.
+        A file descriptor is only available if enabled at server start
+        via <option>-R</option>.  If <literal>%R</literal> is specified
+        and no file descriptor is available, the server will not start.
+        Value <literal>%p</literal> is replaced by a pre-defined
+        prompt string.  Value <literal>%d</literal> is replaced by the
+        directory containing the keys;  this is useful if the command
+        must create files with the keys, e.g., to store a cluster-level
+        key encrypted by a key stored in a hardware security module.
+        (Write <literal>%%</literal> for a literal <literal>%</literal>.)
+        Note that the prompt string will probably contain whitespace,
+        so be sure to quote its use adequately.  Newlines are stripped
+        from the end of the output if present.
+       </para>
+
+       <para>
+        Sample script can be found in
+        <filename>$SHAREDIR/auth_commands</filename>,
+        where <literal>$SHAREDIR</literal> means the
+        <productname>PostgreSQL</productname> installation's shared-data
+        directory, often <filename>/usr/local/share/postgresql</filename>
+        (use <command>pg_config --sharedir</command> to determine it if
+        you're not sure).
+       </para>
+
+       <para>
+        This parameter can only be set by
+        <application>initdb</application>, in the
+        <filename>postgresql.conf</filename> file, or on the server
+        command line.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </sect1>
+
    <sect1 id="runtime-config-client">
     <title>Client Connection Defaults</title>
 
@@ -10619,6 +10694,22 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-file-encryption-method" xreflabel="file_encryption_method">
+      <term><varname>file_encryption_method</varname> (<type>boolean</type>)
+      <indexterm>
+       <primary>Cluster file encryption method</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Reports the cluster file
+        encryption method.  See <xref
+        linkend="app-initdb-cluster-key-command"/> for more
+        information.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-data-directory-mode" xreflabel="data_directory_mode">
       <term><varname>data_directory_mode</varname> (<type>integer</type>)
       <indexterm>
diff --git a/doc/src/sgml/database-encryption.sgml b/doc/src/sgml/database-encryption.sgml
new file mode 100644
index 0000000000..ded406bb56
--- /dev/null
+++ b/doc/src/sgml/database-encryption.sgml
@@ -0,0 +1,149 @@
+<!-- doc/src/sgml/database-encryption.sgml -->
+
+<chapter id="cluster-file-encryption">
+ <title>Cluster File Encryption</title>
+
+ <indexterm zone="cluster-file-encryption">
+  <primary>Cluster File Encryption</primary>
+ </indexterm>
+
+ <para>
+  The purpose of cluster file encryption is to prevent users with read
+  access on the directories used to store database files and write-ahead
+  log files from being able to access the data stored in those files.
+  For example, when using cluster file encryption, users who have read
+  access to the cluster directories for backup purposes will not be able
+  to decrypt the data stored in these files.  Read-only access for a group
+  of users can be enabled using the <application>initdb</application>
+  <option>--allow-group-access</option> option.  Cluster file encryption
+  also provides data-at-rest security, protecting users from data loss
+  should the physical storage media be stolen or improperly erased before
+  disposal.
+ </para>
+
+ <para>
+  Cluster file encryption does not protect against unauthorized file
+  system writes.  Such writes can allow data decryption if used to weaken
+  the system's security and the weakened system is later supplied with
+  the externally-stored cluster encryption key.  This also does not always
+  detect if users with write access remove or modify database files.
+ </para>
+
+ <para>
+  This also does not protect against users who have read access to database
+  process memory because all in-memory data pages and data encryption keys
+  are stored unencrypted in memory.  Therefore, an attacker who is able
+  to read memory can read the data encryption keys and decrypt the entire
+  cluster.  The Postgres operating system user and the operating system
+  administrator, e.g., the <literal>root</literal> user, have such access.
+ </para>
+
+ <sect1 id="cluster-encryption-keys">
+  <title>Keys</title>
+
+  <para>
+   Cluster file encryption uses two levels of encryption &mdash; an upper
+   key which encrypts lower-level keys.  The upper-level key is often
+   referred to as a Key Encryption Key (<acronym>KEK</acronym>).  This key
+   is <emphasis>not</emphasis> stored in the file system, but provided at
+   <command>initdb</command> time and each time the server is started.  This
+   key can be easily changed via <command>pg_alterckey</command> without
+   requiring any changes to the the data files or <command>WAL</command>
+   files.
+  </para>
+
+  <para>
+   The lower level keys are data encryption keys, specifically for relations
+   and <acronym>WAL</acronym>.  The relation key is used to encrypt database
+   heap and index files.  The WAL key is used to encrypt write-ahead log
+   (WAL) files.  Two different keys are used so that primary and standby
+   servers can use different relation keys, but the same WAL key, so that
+   these keys can (in a future release) be rotated by switching the
+   primary to the standby and then changing the WAL key.  Eventually,
+   encryption will be able to added to non-encrypted clusters by creating
+   encrypted replicas and switching over to them.
+  </para>
+
+  <para>
+   Postgres stores the data encryption (lower-level) keys in the data
+   directory encrypted (wrapped) by key encryption (upper-level) key.
+   Though the data encryption keys technically exist in the file system,
+   the key encryption key does not, so the data encryption keys are
+   securely stored.  Data encryption keys are used to securely encrypt
+   other database files.
+  </para>
+ </sect1>
+
+ <sect1 id="cluster-encryption-initialization">
+  <title>Initialization</title>
+
+  <para>
+   Cluster file encryption is enabled when
+   <productname>PostgreSQL</productname> is built
+   with <literal>--with-openssl</literal> and <xref
+   linkend="app-initdb-cluster-key-command"/> is specified
+   during <command>initdb</command>.  The cluster key
+   provided by the <option>--cluster-key-command</option>
+   option during <command>initdb</command> and the one generated
+   by <xref linkend="guc-cluster-key-command"/> in the
+   <filename>postgresql.conf</filename> must match for the database
+   cluster to start. Note that the cluster key command
+   passed to <command>initdb</command> must return a key of
+   64 hexadecimal characters. For example:
+<programlisting>
+initdb -D dbname --cluster-key-command='ckey_passphrase.sh'
+</programlisting>
+   Cluster file encryption does not support a <varname>wal_level</varname>
+   of <literal>minimal</literal>.
+  </para>
+ </sect1>
+
+ <sect1 id="cluster-encryption-operation">
+  <title>Operation</title>
+
+  <para>
+   During the <command>initdb</command> process, if
+   <option>--cluster-key-command</option> is specified, two data-level
+   encryption keys are created.   These two keys are then encrypted with
+   the key encryption key (KEK) supplied by the cluster key command before
+   being stored in the database directory.  The key or passphrase that
+   derives the key must be supplied from the terminal or stored in a
+   trusted key store, such as key vault software or a hardware security
+   module.
+  </para>
+
+  <para>
+   If the <productname>PostgreSQL</productname> server has
+   been initialized to require a cluster key, each time the
+   server starts the <filename>postgresql.conf</filename>
+   <varname>cluster_key_command</varname> command will be executed
+   and the cluster key retrieved.  The data encryption keys in the
+   <filename>pg_cryptokeys</filename> directory will then be decrypted
+   using the supplied key and integrity-checked to ensure it matches the
+   initdb-supplied key.  (If this check fails, the server will refuse
+   to start.)  The cluster encryption key will then be removed from
+   system memory.  The decrypted data encryption keys will remain in
+   shared memory until the server is stopped.
+  </para>
+
+  <para>
+   The data encryption keys are randomly generated and can be 128, 192,
+   or 256-bits in length, depending on whether <literal>AES128</literal>,
+   <literal>AES192</literal>, or <literal>AES256</literal> is specified.
+   They are encrypted by the key encryption key (KEK) using Advanced
+   Encryption Standard (<acronym>AES256</acronym>) encryption in Key
+   Wrap Padded Mode, which also provides KEK authentication;  see <ulink
+   url="https://tools.ietf.org/html/rfc5649">RFC 5649</ulink>. While
+   128-bit encryption is sufficient for most sites, 256-bit encryption
+   is thought to be more immune to future quantum cryptographic attacks.
+  </para>
+
+  <para>
+   If you prefer to create the random keys on your own, you can create
+   a empty directory with a <filename>pg_cryptokeys/live</filename>
+   subdirectory, generate the keys there using your tools. and use the
+   <command>initdb</command> <option>--copy-encryption-keys</option>
+   to copy those keys into the newly-created  cluster.
+  </para>
+ </sect1>
+</chapter>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 4c63a7e768..99bf98f6a5 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -50,6 +50,7 @@
 <!ENTITY wal           SYSTEM "wal.sgml">
 <!ENTITY logical-replication    SYSTEM "logical-replication.sgml">
 <!ENTITY jit    SYSTEM "jit.sgml">
+<!ENTITY database-encryption SYSTEM "database-encryption.sgml">
 
 <!-- programmer's guide -->
 <!ENTITY bgworker   SYSTEM "bgworker.sgml">
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 9b25e9fdb1..3881c968d1 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -1019,7 +1019,7 @@ build-postgresql:
        <listitem>
         <para>
          Build with support for <acronym>SSL</acronym> (encrypted)
-         connections. The only <replaceable>LIBRARY</replaceable>
+         connections and cluster file encryption.  The only <replaceable>LIBRARY</replaceable>
          supported is <option>openssl</option>. This requires the
          <productname>OpenSSL</productname> package to be installed.
          <filename>configure</filename> will check for the required
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index f629524be0..eac99fe54e 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -171,6 +171,7 @@ break is not needed in a wider output rendering.
   &wal;
   &logical-replication;
   &jit;
+  &database-encryption;
   &regress;
 
  </part>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 54b5f22d6e..36a591a91b 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -197,6 +197,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY dropuser           SYSTEM "dropuser.sgml">
 <!ENTITY ecpgRef            SYSTEM "ecpg-ref.sgml">
 <!ENTITY initdb             SYSTEM "initdb.sgml">
+<!ENTITY pgalterckey        SYSTEM "pg_alterckey.sgml">
 <!ENTITY pgamcheck          SYSTEM "pg_amcheck.sgml">
 <!ENTITY pgarchivecleanup   SYSTEM "pgarchivecleanup.sgml">
 <!ENTITY pgBasebackup       SYSTEM "pg_basebackup.sgml">
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index d43c91575c..777e8a3401 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -189,6 +189,18 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry id="app-initdb-cluster-key-command" xreflabel="cluster key command">
+      <term><option>-c <replaceable class="parameter">command</replaceable></option></term>
+      <term><option>--cluster-key-command=<replaceable class="parameter">command</replaceable></option></term>
+      <listitem>
+       <para>
+        This option specifies an external command to obtain the cluster-level
+        key for cluster file encryption during server initialization and
+        server start;  see <xref linkend="guc-cluster-key-command"/> for details.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="app-initdb-option-pgdata">
       <term><option>-D <replaceable class="parameter">directory</replaceable></option></term>
       <term><option>--pgdata=<replaceable class="parameter">directory</replaceable></option></term>
@@ -277,6 +289,18 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry id="app-initdb-file-encryption-method"
+      xreflabel="file encryption">
+      <term><option>-K <replaceable class="parameter">encryption_method</replaceable></option></term>
+      <term><option>--file-encryption-method=<replaceable class="parameter">encryption_method</replaceable></option></term>
+      <listitem>
+       <para>
+        Specifies the cluster file encryption method.  The
+        value values are <literal>AES128</literal> (the default), <literal>AES192</literal>, and <literal>AES256</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="app-initdb-option-locale">
       <term><option>--locale=<replaceable>locale</replaceable></option></term>
       <listitem>
@@ -384,6 +408,17 @@ PostgreSQL documentation
        <para>
         This option has no effect when <option>--no-sync</option> is used.
        </para>
+       </listitem>
+     </varlistentry>
+
+     <varlistentry id="app-initdb-option-authprompt">
+      <term><option>-R</option></term>
+      <term><option>--authprompt</option></term>
+      <listitem>
+       <para>
+        Allows the <option>--cluster-key-command</option> command
+        to prompt for a passphrase or PIN.
+       </para>
       </listitem>
      </varlistentry>
 
@@ -412,6 +447,18 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry id="app-initdb-option-copy-encryption-keys">
+      <term><option>-u <replaceable>datadir</replaceable></option></term>
+      <term><option>--copy-encryption-keys=<replaceable>datadir</replaceable></option></term>
+      <listitem>
+       <para>
+        Copies cluster file encryption keys from another cluster; required
+        when using <application>pg_upgrade</application> on a cluster
+        with cluster file encryption enabled.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="app-initdb-option-username">
       <term><option>-U <replaceable class="parameter">username</replaceable></option></term>
       <term><option>--username=<replaceable class="parameter">username</replaceable></option></term>
diff --git a/doc/src/sgml/ref/pg_alterckey.sgml b/doc/src/sgml/ref/pg_alterckey.sgml
new file mode 100644
index 0000000000..867c439b8e
--- /dev/null
+++ b/doc/src/sgml/ref/pg_alterckey.sgml
@@ -0,0 +1,210 @@
+<!--
+doc/src/sgml/ref/pg_alterckey.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="app-pg_alterckey">
+ <indexterm zone="app-pg_alterckey">
+  <primary>pg_alterckey</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle><application>pg_alterckey</application></refentrytitle>
+  <manvolnum>1</manvolnum>
+  <refmiscinfo>Application</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>pg_alterckey</refname>
+  <refpurpose>alter the <productname>PostgreSQL</productname> cluster key</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+  <cmdsynopsis>
+   <command>pg_alterckey</command>
+
+   <group choice="plain">
+    <arg choice="plain"><option>-R</option></arg>
+    <arg choice="plain"><option>--authprompt</option></arg>
+   </group>
+
+   <arg choice="plain">
+    <replaceable class="parameter">old_cluster_key_command</replaceable>
+    <replaceable class="parameter">new_cluster_key_command</replaceable>
+   </arg>
+
+   <arg choice="opt">
+    <group choice="plain">
+     <arg choice="plain"><option>-D</option></arg>
+     <arg choice="plain"><option>--pgdata</option></arg>
+    </group>
+    <replaceable class="parameter">datadir</replaceable>
+   </arg>
+
+  </cmdsynopsis>
+
+  <cmdsynopsis>
+   <command>pg_alterckey</command>
+
+   <group choice="opt">
+    <arg choice="plain"><option>-R</option></arg>
+    <arg choice="plain"><option>--authprompt</option></arg>
+   </group>
+
+   <group choice="plain">
+    <arg choice="plain"><option>-r</option></arg>
+    <arg choice="plain"><option>--repair</option></arg>
+   </group>
+
+   <arg choice="opt">
+    <group choice="opt">
+     <arg choice="plain"><option>-D</option></arg>
+     <arg choice="plain"><option>--pgdata</option></arg>
+    </group>
+    <replaceable class="parameter">datadir</replaceable>
+   </arg>
+
+  </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="r1-app-pg_alterckey-1">
+  <title>Description</title>
+  <para>
+   <command>pg_alterckey</command> alters the cluster key used
+   for cluster file encryption.  The cluster key is initially set
+   during <xref linkend="app-initdb"/>.  The command can be run while the
+   server is running or stopped.  The new password must be used the next
+   time the server is started.
+  </para>
+
+  <para>
+   <command>pg_alterckey</command> changes the key encryption key
+   (<acronym>KEK</acronym>) which encrypts the data encryption keys;
+   it does not change the data encryption keys.  It does this by
+   decrypting each data encryption key using the <replaceable
+   class="parameter">old_cluster_key_command</replaceable>,
+   re-encrypting it using the <replaceable
+   class="parameter">new_cluster_key_command</replaceable>, and then
+   writes the result back to the cluster directory.
+  </para>
+
+  <para>
+   See the <xref linkend="app-initdb"/> documentation for how to define
+   the old and new passphrase commands.  You can use different executables
+   for these commands, or you can use the same executable with different
+   arguments to specify retrieval of the old or new key.
+  </para>
+
+  <para>
+   <command>pg_alterckey</command> manages data encryption keys,
+   which are critical to allowing Postgres to access its decrypted
+   data.  For this reason, it is very careful to preserve these
+   keys in most possible failure conditions, e.g., operating system
+   failure during cluster encryption key rotation.
+  </para>
+
+  <para>
+   When started, <command>pg_alterckey</command> repairs any files that
+   remain from previous failures before altering the cluster encryption
+   key.  During this repair phase, <command>pg_alterckey</command> will
+   either roll back the cluster key or roll forward the changes that
+   were previously requested.  The server will not start if repair is
+   needed, though a running server is unaffected by an unrepaired cluster
+   key configuration.  Therefore, if <command>pg_alterckey</command>
+   fails for any reason, it is recommended you run the command with
+   <option>--repair</option> to simply roll back or forward any previous
+   changes.  This will report if it rolled the cluster key back or forward,
+   and then run the command again to change the cluster key if needed.
+  </para>
+
+  <para>
+   You can specify the data directory on the command line, or use
+   the environment variable <envar>PGDATA</envar>.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Options</title>
+
+   <para>
+    <variablelist>
+     <varlistentry>
+      <term><option>-R</option></term>
+      <term><option>--authprompt</option></term>
+      <listitem>
+       <para>
+        Allows the <option>old_cluster_key_command</option> and
+        <option>new_cluster_key_command</option> commands
+        to prompt for a passphrase or PIN.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+
+   <para>
+    Other options:
+
+    <variablelist>
+     <varlistentry>
+       <term><option>-V</option></term>
+       <term><option>--version</option></term>
+       <listitem>
+       <para>
+       Print the <application>pg_alterckey</application> version and exit.
+       </para>
+       </listitem>
+     </varlistentry>
+
+     <varlistentry>
+       <term><option>-?</option></term>
+       <term><option>--help</option></term>
+       <listitem>
+       <para>
+       Show help about <application>pg_alterckey</application> command line
+       arguments, and exit.
+       </para>
+       </listitem>
+     </varlistentry>
+
+    </variablelist>
+   </para>
+
+ </refsect1>
+
+ <refsect1>
+  <title>Environment</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><envar>PGDATA</envar></term>
+
+    <listitem>
+     <para>
+      Default data directory location
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><envar>PG_COLOR</envar></term>
+    <listitem>
+     <para>
+      Specifies whether to use color in diagnostic messages. Possible values
+      are <literal>always</literal>, <literal>auto</literal> and
+      <literal>never</literal>.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="app-initdb"/></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/pg_ctl-ref.sgml b/doc/src/sgml/ref/pg_ctl-ref.sgml
index 46906966eb..e3705c724e 100644
--- a/doc/src/sgml/ref/pg_ctl-ref.sgml
+++ b/doc/src/sgml/ref/pg_ctl-ref.sgml
@@ -38,6 +38,7 @@ PostgreSQL documentation
    <arg choice="opt"><option>-s</option></arg>
    <arg choice="opt"><option>-o</option> <replaceable>options</replaceable></arg>
    <arg choice="opt"><option>-p</option> <replaceable>path</replaceable></arg>
+   <arg choice="opt"><option>-R</option></arg>
    <arg choice="opt"><option>-c</option></arg>
   </cmdsynopsis>
 
@@ -72,6 +73,7 @@ PostgreSQL documentation
    <arg choice="opt"><option>-t</option> <replaceable>seconds</replaceable></arg>
    <arg choice="opt"><option>-s</option></arg>
    <arg choice="opt"><option>-o</option> <replaceable>options</replaceable></arg>
+   <arg choice="opt"><option>-R</option></arg>
    <arg choice="opt"><option>-c</option></arg>
   </cmdsynopsis>
 
@@ -373,6 +375,18 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-R</option></term>
+      <term><option>--authprompt</option></term>
+      <listitem>
+       <para>
+        Allows <option>ssl_passphrase_command</option> or
+        <option>cluster_key_command</option> to prompt for a passphrase
+        or PIN.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-s</option></term>
       <term><option>--silent</option></term>
diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 46e8a0b746..12cab55fc2 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -182,6 +182,15 @@ PostgreSQL documentation
       </para></listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-R</option></term>
+      <term><option>--authprompt</option></term>
+      <listitem><para>allows <option>ssl_passphrase_command</option> or
+      <option>cluster_key_command</option> to prompt for a passphrase
+      or PIN.
+      </para></listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-s</option> <replaceable>dir</replaceable></term>
       <term><option>--socketdir=</option><replaceable>dir</replaceable></term>
@@ -342,7 +351,9 @@ make prefix=/usr/local/pgsql.new install
      Again, use compatible <command>initdb</command>
      flags that match the old cluster. Many
      prebuilt installers do this step automatically. There is no need to
-     start the new cluster.
+     start the new cluster.  If upgrading a cluster that uses
+     cluster file encryption, the <command>initdb</command> option
+     <option>--copy-encryption-keys</option> must be specified.
     </para>
    </step>
 
@@ -947,6 +958,13 @@ psql --username=postgres --file=script.sql postgres
    is down.
   </para>
 
+  <para>
+   If the old cluster uses file encryption, the new cluster must use
+   the same keys, so <command>pg_upgrade</command> copies them to the
+   new cluster.  It is necessary to initialize the new cluster with
+   the same <varname>cluster_key_command</varname> and the same
+   file encryption method.
+  </para>
  </refsect1>
 
  <refsect1>
diff --git a/doc/src/sgml/ref/postgres-ref.sgml b/doc/src/sgml/ref/postgres-ref.sgml
index b13a16a117..1eb767865b 100644
--- a/doc/src/sgml/ref/postgres-ref.sgml
+++ b/doc/src/sgml/ref/postgres-ref.sgml
@@ -305,6 +305,19 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-R <replaceable class="parameter">file-descriptor</replaceable></option></term>
+      <listitem>
+       <para>
+        Makes <command>postgres</command> prompt for a passphrase or PIN
+        from the specified open numeric file descriptor.  The descriptor
+        is closed after the key is read.  The file descriptor number
+        <literal>-1</literal> duplicates standard error for the terminal;
+        this is useful for single-user mode.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-s</option></term>
       <listitem>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index e11b4b6130..4f6c9d1ca5 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -241,6 +241,7 @@
    </para>
   </partintro>
 
+   &pgalterckey;
    &clusterdb;
    &createdb;
    &createuser;
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index 3ea4e5526d..fc187342d5 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -77,6 +77,11 @@ Item
  <entry>Subdirectory containing transaction commit timestamp data</entry>
 </row>
 
+<row>
+ <entry><filename>pg_cryptokeys</filename></entry>
+ <entry>Subdirectory containing file encryption keys</entry>
+</row>
+
 <row>
  <entry><filename>pg_dynshmem</filename></entry>
  <entry>Subdirectory containing files used by the dynamic shared memory
-- 
2.40.1

