pg_hba.conf host name wildcard support

Started by Peter Eisentrautabout 15 years ago4 messages
#1Peter Eisentraut
peter_e@gmx.net

So, as previously indicated, let's add some wildcard support to the
pg_hba.conf host name feature. After looking around a bit, two syntaxes
appear to be on offer:

1. TCP Wrappers style, leading dot indicates suffix match.
So .example.com matches anything.example.com. Not sure how useful that
would be, but it could be implemented in about 3 lines of code.

2. Full regular expressions. I'd suggest the pg_ident.conf style, where
a leading slash indicates a regex. An example could be /^dbserver\d\.
With some code refactoring, this would also only take a few extra lines
of code.

Comments, other ideas?

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#1)
Re: pg_hba.conf host name wildcard support

Peter Eisentraut <peter_e@gmx.net> writes:

So, as previously indicated, let's add some wildcard support to the
pg_hba.conf host name feature. After looking around a bit, two syntaxes
appear to be on offer:

1. TCP Wrappers style, leading dot indicates suffix match.
So .example.com matches anything.example.com. Not sure how useful that
would be, but it could be implemented in about 3 lines of code.

2. Full regular expressions. I'd suggest the pg_ident.conf style, where
a leading slash indicates a regex. An example could be /^dbserver\d\.
With some code refactoring, this would also only take a few extra lines
of code.

I'd lean to #1 myself. Regexes would be a perpetual foot-gun because
(a) dot is a metacharacter to a regex and (b) a non-anchored pattern
is default but would be insecure in most usages.

There is a SQL-ish solution to those two objections: use LIKE or SIMILAR
TO pattern language not standard regex. But #1 would be far more
familiar to most admin types.

regards, tom lane

#3Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#2)
Re: pg_hba.conf host name wildcard support

Tom Lane <tgl@sss.pgh.pa.us> writes:

Peter Eisentraut <peter_e@gmx.net> writes:

1. TCP Wrappers style, leading dot indicates suffix match.
So .example.com matches anything.example.com. Not sure how useful that
would be, but it could be implemented in about 3 lines of code.

I'd lean to #1 myself.

FWIW, +1

--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#4Peter Eisentraut
peter_e@gmx.net
In reply to: Peter Eisentraut (#1)
1 attachment(s)
Re: pg_hba.conf host name wildcard support

On tor, 2010-10-21 at 06:38 +0300, Peter Eisentraut wrote:

So, as previously indicated, let's add some wildcard support to the
pg_hba.conf host name feature. After looking around a bit, two syntaxes
appear to be on offer:

1. TCP Wrappers style, leading dot indicates suffix match.
So .example.com matches anything.example.com. Not sure how useful that
would be, but it could be implemented in about 3 lines of code.

Here is a patch for that.

Attachments:

hba-host-pattern.patchtext/x-patch; charset=UTF-8; name=hba-host-pattern.patchDownload
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 228cfff..7ab9305 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -283,6 +283,14 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
       </para>
 
       <para>
+       A host name specification that starts with a dot
+       (<literal>.</literal>) matches a suffix of the actual host
+       name.  So <literal>.example.com</literal> would match
+       <literal>foo.example.com</literal> (but not just
+       <literal>example.com</literal>).
+      </para>
+
+      <para>
        When host names are specified
        in <filename>pg_hba.conf</filename>, you should make sure that
        name resolution is reasonably fast.  It can be of advantage to
@@ -311,6 +319,12 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
        </para>
 
        <para>
+        Also, a reverse lookup is necessary to implement the suffix
+        matching feature, because the actual client host name needs to
+        be known in order to match it against the pattern.
+       </para>
+
+       <para>
         Note that this behavior is consistent with other popular
         implementations of host name-based access control, such as the
         Apache HTTP Server and TCP Wrappers.
@@ -605,6 +619,12 @@ host    postgres        all             192.168.93.0/24         ident
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
 host    postgres        all             192.168.12.10/32        md5
 
+# Allow any user from hosts in the example.com domain to connect to
+# any database if the user's password is correctly supplied.
+#
+# TYPE  DATABASE        USER            ADDRESS                 METHOD
+host    all             all             .example.com            md5
+
 # In the absence of preceding "host" lines, these two lines will
 # reject all connections from 192.168.54.1 (since that entry will be
 # matched first), but allow Kerberos 5 connections from anywhere else
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 1736359..d9d11d8 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -565,6 +565,26 @@ ipv6eq(struct sockaddr_in6 *a, struct sockaddr_in6 *b)
 #endif /* HAVE_IPV6 */
 
 /*
+ * Check whether host name matches pattern.
+ */
+static bool
+hostname_match(const char *pattern, const char *actual_hostname)
+{
+	if (pattern[0] == '.')		/* suffix match */
+	{
+		size_t plen = strlen(pattern);
+		size_t hlen = strlen(actual_hostname);
+
+		if (hlen < plen)
+			return false;
+
+		return (pg_strcasecmp(pattern, actual_hostname + (hlen - plen)) == 0);
+	}
+	else
+		return (pg_strcasecmp(pattern, actual_hostname) == 0);
+}
+
+/*
  * Check to see if a connecting IP matches a given host name.
  */
 static bool
@@ -588,7 +608,7 @@ check_hostname(hbaPort *port, const char *hostname)
 		port->remote_hostname = pstrdup(remote_hostname);
 	}
 
-	if (pg_strcasecmp(port->remote_hostname, hostname) != 0)
+	if (!hostname_match(hostname, port->remote_hostname))
 		return false;
 
 	/* Lookup IP from host name and check against original IP */
diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample
index 87fed80..87f8499 100644
--- a/src/backend/libpq/pg_hba.conf.sample
+++ b/src/backend/libpq/pg_hba.conf.sample
@@ -32,7 +32,8 @@
 # ADDRESS specifies the set of hosts the record matches.  It can be a
 # host name, or it is made up of an IP address and a CIDR mask that is
 # an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that
-# specifies the number of significant bits in the mask.
+# specifies the number of significant bits in the mask.  A host name
+# that starts with a dot (.) matches a suffix of the actual host name.
 # Alternatively, you can write an IP address and netmask in separate
 # columns to specify the set of hosts.  Instead of a CIDR-address, you
 # can write "samehost" to match any of the server's own IP addresses,