Fix pg_hba.conf matching so that replication connections only match records
authorTom Lane
Wed, 21 Apr 2010 03:32:53 +0000 (03:32 +0000)
committerTom Lane
Wed, 21 Apr 2010 03:32:53 +0000 (03:32 +0000)
with database = replication.  The previous coding would allow them to match
ordinary records too, but that seems like a recipe for security breaches.
Improve the messages associated with no-such-pg_hba.conf entry to report
replication connections as such, since that's now a critical aspect of
whether the connection matches.  Make some cursory improvements in the related
documentation, too.

doc/src/sgml/client-auth.sgml
doc/src/sgml/high-availability.sgml
doc/src/sgml/recovery-config.sgml
src/backend/libpq/auth.c
src/backend/libpq/hba.c
src/backend/replication/libpqwalreceiver/libpqwalreceiver.c

index 38b13954ea5ac28d7300b27ac7b03a7ab8791423..d2f30a47c4eed8f70b6f5b48389b1937fcb86c63 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
 
  Client Authentication
   
    The general format of the pg_hba.conf file is
    a set of records, one per line. Blank lines are ignored, as is any
-   text after the # comment character. A record is made
+   text after the # comment character.
+   Records cannot be continued across lines.
+   A record is made
    up of a number of fields which are separated by spaces and/or tabs.
    Fields can contain white space if the field value is quoted.
-   Quoting one of the keywords in database or username field (e.g "all"
-   or "replication") makes the name lose its special character, and just
-   match a database or username with that name. Records cannot be
-   continued across lines.
+   Quoting one of the keywords in a database or username field (e.g.,
+   all or replication) makes the word lose its special
+   character, and just match a database or user with that name.
   
 
   
@@ -185,7 +186,8 @@ hostnossl  database  user
        name as the requested database.  (samegroup is an
        obsolete but still accepted spelling of samerole.)
        The value replication specifies that the record
-       matches if streaming replication is requested.
+       matches if a replication connection is requested (note that
+       replication connections do not specify any particular database).
        Otherwise, this is the name of
        a specific PostgreSQL database.
        Multiple database names can be supplied by separating them with
@@ -241,7 +243,7 @@ hostnossl  database  user
        Typical examples of a CIDR-address are
        172.20.143.89/32 for a single host, or
        172.20.143.0/24 for a small network, or
-       10.6.0.0/16 for a larger one.  
+       10.6.0.0/16 for a larger one.
        0.0.0.0/0 (all balls) represents all addresses.
        To specify a single host, use a CIDR mask of 32 for IPv4 or
        128 for IPv6.  In a network address, do not omit trailing zeroes.
index d2a8ea77998a1e832c9883e972e82d9bb2e0366e..766d223bd2bd0cc018a3a99f0f24f082d0c9276f 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
 
  High Availability, Load Balancing, and Replication
@@ -617,7 +617,7 @@ protocol to make nodes agree on a serializable transactional order.
   
 
   
-   Preparing Master for Standby Servers
+   Preparing <span class="marked">the </span>Master for Standby Servers
 
    
     Set up continuous archiving on the primary to an archive directory
@@ -629,9 +629,13 @@ protocol to make nodes agree on a serializable transactional order.
    
 
    
-    If you want to use streaming replication, set up authentication to allow
-    streaming replication connections and set max_wal_senders in
-    the configuration file of the primary server.
+    If you want to use streaming replication, set up authentication on the
+    primary server to allow replication connections from the standby
+    server(s); that is, provide a suitable entry or entries in
+    pg_hba.conf with the database field set to
+    replication.  Also ensure max_wal_senders is set
+    to a sufficiently large value in the configuration file of the primary
+    server.
    
 
    
@@ -641,7 +645,7 @@ protocol to make nodes agree on a serializable transactional order.
   
 
   
-   Setting <span class="marked">up the standby s</span>erver
+   Setting <span class="marked">Up a Standby S</span>erver
 
    
     To set up the standby server, restore the base backup taken from primary
@@ -656,7 +660,7 @@ protocol to make nodes agree on a serializable transactional order.
      
      Do not use pg_standby or similar tools with the built-in standby mode
      described here. restore_command should return immediately
-     if the file does not exist, the server will retry the command again if
+     if the file does not exist; the server will retry the command again if
      necessary. See 
      for using tools like pg_standby.
     
@@ -776,22 +780,20 @@ trigger_file = '/path/to/trigger_file'
    
     Authentication
     
-     It is very important that the access privilege for replication be setup
-     properly so that only trusted users can read the WAL stream, because it is
-     easy to extract privileged information from it.
+     It is very important that the access privileges for replication be set up
+     so that only trusted users can read the WAL stream, because it is
+     easy to extract privileged information from it.  Standby servers must
+     authenticate to the primary as a superuser account.
+     So a role with the SUPERUSER and LOGIN
+     privileges needs to be created on the primary.
     
     
-     Only the superuser is allowed to connect to the primary as the replication
-     standby. So a role with the SUPERUSER and LOGIN
-     privileges needs to be created in the primary.
-    
-    
-     Client authentication for replication is controlled by the
+     Client authentication for replication is controlled by a
      pg_hba.conf record specifying replication in the
      database field. For example, if the standby is running on
      host IP 192.168.1.100 and the superuser's name for replication
      is foo, the administrator can add the following line to the
-     pg_hba.conf file on the primary.
+     pg_hba.conf file on the primary:
 
 
 # Allow the user "foo" from host 192.168.1.100 to connect to the primary
@@ -809,18 +811,13 @@ host    replication     foo             192.168.1.100/32        md5
      port 5432, the superuser's name for replication is
      foo, and the password is foopass, the administrator
      can add the following line to the recovery.conf file on the
-     standby.
+     standby:
 
 
 # The standby connects to the primary that is running on host 192.168.1.50
 # and port 5432 as the user "foo" whose password is "foopass".
 primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'
 
-
-      You do not need to specify database=replication in the
-      primary_conninfo. The required option will be added
-      automatically. If you mention the database parameter at all within
-      primary_conninfo then a FATAL error will be raised.
     
    
 
@@ -1959,7 +1956,7 @@ mv /tmp/pg_control /path/to/backup/data/global
       pg_control contains the location where WAL replay will
       begin after restoring from the backup; backing it up first ensures
       that it points to the last restartpoint when the backup started, not
-      some later restartpoint that happened while files were copied to the 
+      some later restartpoint that happened while files were copied to the
       backup.
      
     
index 85d3b7eb3fb6d142ba98923da63bdd7e4b591c06..58fa5b0b107af895da3289910e78569f32daccac 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
 
   Recovery Configuration
   
 
    
-      This chapter describes the settings available in
-      recovery.conf file. They apply only for the duration of
-      the recovery.  (A sample file, share/recovery.conf.sample,
-      exists in the installation's share/ directory.)  They must
-      be reset for any subsequent recovery you wish to perform. They cannot
-      be changed once recovery has begun.
+    This chapter describes the settings available in the
+    recovery.conf file. They apply only for the duration of the
+    recovery.  They must be reset for any subsequent recovery you wish to
+    perform.  They cannot be changed once recovery has begun.
    
 
    
      value, write two quotes ('').
    
 
+   
+    A sample file, share/recovery.conf.sample,
+    is provided in the installation's share/ directory.
+   
+
   
 
     Archive recovery settings
@@ -208,9 +211,10 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
          
           Specifies whether to start the PostgreSQL server as
           a standby. If this parameter is on, the server will
-          not end recovery when the end of archived WAL is reached, but
-          will keep trying to continue recovery using restore_command
-          and by connecting to the primary server as specified by the
+          not stop recovery when the end of archived WAL is reached, but
+          will keep trying to continue recovery by fetching new WAL segments
+          using restore_command
+          and/or by connecting to the primary server as specified by the
           primary_conninfo setting.
          
         
@@ -229,14 +233,19 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
           defaults are used.
          
          
-          The built-in replication requires that a host name (or host address)
-          or port number which the primary server listens on be
-          specified in this string. Also ensure that a role with
-          the SUPERUSER and LOGIN privileges on the
-          primary is set (see
-          ). Note that
-          the password needs to be set if the primary demands password
-          authentication.
+          The connection string should specify the host name (or address)
+          of the primary server, as well as the port number if it is not
+          the same as the standby server's default.
+          Also specify a user name corresponding to a role that has the
+          SUPERUSER and LOGIN privileges on the
+          primary (see
+          ).
+          A password needs to be provided too, if the primary demands password
+          authentication.  (The password can be provided either in
+          the primary_conninfo string or in a separate
+          ~/.pgpass file on the standby server.)
+          Do not specify a database name in the
+          primary_conninfo string.
          
          
           This setting has no effect if standby_mode is off.
index ac26317d264f164af53e0bd62def944405c294e6..c4e234186e69d1dbb9d2a26977efdf4d4f2e876f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.199 2010/04/19 19:02:18 sriggs Exp $
+ *   $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.200 2010/04/21 03:32:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,6 +36,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/md5.h"
 #include "miscadmin.h"
+#include "replication/walsender.h"
 #include "storage/ipc.h"
 
 
@@ -250,6 +251,7 @@ auth_failed(Port *port, int status)
    switch (port->hba->auth_method)
    {
        case uaReject:
+       case uaImplicitReject:
            errstr = gettext_noop("authentication failed for user \"%s\": host rejected");
            break;
        case uaKrb5:
@@ -363,12 +365,14 @@ ClientAuthentication(Port *port)
        case uaReject:
 
            /*
-            * An explicit "reject" entry in pg_hba.conf. Take pity on the poor
-            * user and issue a helpful error message.
-            * NOTE: this is not a security breach, because all the info
-            * reported here is known at the frontend and must be assumed
-            * known to bad guys. We're merely helping out the less clueful
-            * good guys.
+            * An explicit "reject" entry in pg_hba.conf.  This report exposes
+            * the fact that there's an explicit reject entry, which is perhaps
+            * not so desirable from a security standpoint; but the message
+            * for an implicit reject could confuse the DBA a lot when the
+            * true situation is a match to an explicit reject.  And we don't
+            * want to change the message for an implicit reject.  As noted
+            * below, the additional information shown here doesn't expose
+            * anything not known to an attacker.
             */
            {
                char        hostinfo[NI_MAXHOST];
@@ -378,29 +382,50 @@ ClientAuthentication(Port *port)
                                   NULL, 0,
                                   NI_NUMERICHOST);
 
+               if (am_walsender)
+               {
 #ifdef USE_SSL
-               ereport(FATAL,
-                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
-                        errmsg("pg_hba.conf rejects host \"%s\", user \"%s\", database \"%s\", %s",
-                             hostinfo, port->user_name, port->database_name,
-                               port->ssl ? _("SSL on") : _("SSL off"))));
+                   ereport(FATAL,
+                           (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                            errmsg("pg_hba.conf rejects replication connection for host \"%s\", user \"%s\", %s",
+                                   hostinfo, port->user_name,
+                                   port->ssl ? _("SSL on") : _("SSL off"))));
 #else
-               ereport(FATAL,
-                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
-                        errmsg("pg_hba.conf rejects host \"%s\", user \"%s\", database \"%s\"",
-                          hostinfo, port->user_name, port->database_name)));
+                   ereport(FATAL,
+                           (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                            errmsg("pg_hba.conf rejects replication connection for host \"%s\", user \"%s\"",
+                                   hostinfo, port->user_name)));
 #endif
+               }
+               else
+               {
+#ifdef USE_SSL
+                   ereport(FATAL,
+                           (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                            errmsg("pg_hba.conf rejects connection for host \"%s\", user \"%s\", database \"%s\", %s",
+                                   hostinfo, port->user_name,
+                                   port->database_name,
+                                   port->ssl ? _("SSL on") : _("SSL off"))));
+#else
+                   ereport(FATAL,
+                           (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                            errmsg("pg_hba.conf rejects connection for host \"%s\", user \"%s\", database \"%s\"",
+                                   hostinfo, port->user_name,
+                                   port->database_name)));
+#endif
+               }
                break;
            }
 
        case uaImplicitReject:
 
            /*
-            * No matching entry so tell the user we fell through.
-            * NOTE: this is not a security breach, because all the info
-            * reported here is known at the frontend and must be assumed
-            * known to bad guys. We're merely helping out the less clueful
-            * good guys.
+            * No matching entry, so tell the user we fell through.
+            *
+            * NOTE: the extra info reported here is not a security breach,
+            * because all that info is known at the frontend and must be
+            * assumed known to bad guys.  We're merely helping out the less
+            * clueful good guys.
             */
            {
                char        hostinfo[NI_MAXHOST];
@@ -410,18 +435,38 @@ ClientAuthentication(Port *port)
                                   NULL, 0,
                                   NI_NUMERICHOST);
 
+               if (am_walsender)
+               {
 #ifdef USE_SSL
-               ereport(FATAL,
-                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
-                        errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
-                             hostinfo, port->user_name, port->database_name,
-                               port->ssl ? _("SSL on") : _("SSL off"))));
+                   ereport(FATAL,
+                           (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                            errmsg("no pg_hba.conf entry for replication connection from host \"%s\", user \"%s\", %s",
+                                   hostinfo, port->user_name,
+                                   port->ssl ? _("SSL on") : _("SSL off"))));
 #else
-               ereport(FATAL,
-                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
-                        errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"",
-                          hostinfo, port->user_name, port->database_name)));
+                   ereport(FATAL,
+                           (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                            errmsg("no pg_hba.conf entry for replication connection from host \"%s\", user \"%s\"",
+                                   hostinfo, port->user_name)));
+#endif
+               }
+               else
+               {
+#ifdef USE_SSL
+                   ereport(FATAL,
+                           (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                            errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
+                                   hostinfo, port->user_name,
+                                   port->database_name,
+                                   port->ssl ? _("SSL on") : _("SSL off"))));
+#else
+                   ereport(FATAL,
+                           (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                            errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"",
+                                   hostinfo, port->user_name,
+                                   port->database_name)));
 #endif
+               }
                break;
            }
 
index 11443f76e2dd6ad468948cdc58f1e82eb67e816f..e5fb65f75d39a24c9f807712397144b5a3d5825a 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.205 2010/04/19 19:02:18 sriggs Exp $
+ *   $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.206 2010/04/21 03:32:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -513,7 +513,13 @@ check_db(const char *dbname, const char *role, Oid roleid, char *param_str)
         tok != NULL;
         tok = strtok(NULL, MULTI_VALUE_SEP))
    {
-       if (strcmp(tok, "all\n") == 0)
+       if (am_walsender)
+       {
+           /* walsender connections can only match replication keyword */
+           if (strcmp(tok, "replication\n") == 0)
+               return true;
+       }
+       else if (strcmp(tok, "all\n") == 0)
            return true;
        else if (strcmp(tok, "sameuser\n") == 0)
        {
@@ -526,9 +532,8 @@ check_db(const char *dbname, const char *role, Oid roleid, char *param_str)
            if (is_member(roleid, dbname))
                return true;
        }
-       else if (strcmp(tok, "replication\n") == 0 &&
-                am_walsender)
-           return true;
+       else if (strcmp(tok, "replication\n") == 0)
+           continue;           /* never match this if not walsender */
        else if (strcmp(tok, dbname) == 0)
            return true;
    }
@@ -1812,7 +1817,7 @@ load_ident(void)
  *
  * Note that STATUS_ERROR indicates a problem with the hba config file.
  * If the file is OK but does not contain any entry matching the request,
- * we return STATUS_OK and method = uaReject.
+ * we return STATUS_OK and method = uaImplicitReject.
  */
 int
 hba_getauthmethod(hbaPort *port)
index d41858e49a201de8975d04514a75cd40189f98ff..1807fde9e4b1de7698749b70b41d6bd938aa834b 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c,v 1.9 2010/04/19 14:10:45 mha Exp $
+ *   $PostgreSQL: pgsql/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c,v 1.10 2010/04/21 03:32:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -91,7 +91,7 @@ libpqrcv_connect(char *conninfo, XLogRecPtr startpoint)
    streamConn = PQconnectdb(conninfo_repl);
    if (PQstatus(streamConn) != CONNECTION_OK)
        ereport(ERROR,
-               (errmsg("could not connect to the primary server : %s",
+               (errmsg("could not connect to the primary server: %s",
                        PQerrorMessage(streamConn))));
 
    /*