Kerberos test suite

Started by Peter Eisentrautalmost 8 years ago15 messages
#1Peter Eisentraut
peter.eisentraut@2ndquadrant.com
1 attachment(s)

Here is a patch with a test suite for the Kerberos/GSSAPI authentication
functionality. It's very similar in principle to the recently added
LDAP tests, and similar caveats apply.

You will need the client and server parts of a krb5 package
installation, possibly named krb5-workstation and krb5-server, or
perhaps krb5-user and krb5-kdc.

(If it appears to hang for you in the "setting up Kerberos" step, you
might need more entropy/wait a while. That problem appears to be
limited to some virtual machine setups, but the specifics are not clear.)

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

0001-Tests-for-Kerberos-GSSAPI-authentication.patchtext/plain; charset=UTF-8; name=0001-Tests-for-Kerberos-GSSAPI-authentication.patch; x-mac-creator=0; x-mac-type=0Download
From 108530921d7a3780cd5d1a31aebadd29621de429 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Wed, 14 Feb 2018 09:10:50 -0500
Subject: [PATCH] Tests for Kerberos/GSSAPI authentication

---
 configure                       |   2 +
 configure.in                    |   1 +
 src/Makefile.global.in          |   1 +
 src/test/kerberos/.gitignore    |   2 +
 src/test/kerberos/Makefile      |  25 +++++++
 src/test/kerberos/README        |  25 +++++++
 src/test/kerberos/t/001_auth.pl | 143 ++++++++++++++++++++++++++++++++++++++++
 src/test/perl/PostgresNode.pm   |   8 ++-
 8 files changed, 205 insertions(+), 2 deletions(-)
 create mode 100644 src/test/kerberos/.gitignore
 create mode 100644 src/test/kerberos/Makefile
 create mode 100644 src/test/kerberos/README
 create mode 100644 src/test/kerberos/t/001_auth.pl

diff --git a/configure b/configure
index 7dcca506f8..84b547be79 100755
--- a/configure
+++ b/configure
@@ -708,6 +708,7 @@ with_uuid
 with_systemd
 with_selinux
 with_openssl
+with_krb_srvnam
 krb_srvtab
 with_python
 with_perl
@@ -5814,6 +5815,7 @@ fi
 
 
 
+
 cat >>confdefs.h <<_ACEOF
 #define PG_KRB_SRVNAM "$with_krb_srvnam"
 _ACEOF
diff --git a/configure.in b/configure.in
index 4d26034579..dbd7fa2c48 100644
--- a/configure.in
+++ b/configure.in
@@ -650,6 +650,7 @@ PGAC_ARG_REQ(with, krb-srvnam,
              [NAME], [default service principal name in Kerberos (GSSAPI) [postgres]],
              [],
              [with_krb_srvnam="postgres"])
+AC_SUBST(with_krb_srvnam)
 AC_DEFINE_UNQUOTED([PG_KRB_SRVNAM], ["$with_krb_srvnam"],
                    [Define to the name of the default PostgreSQL service principal in Kerberos (GSSAPI). (--with-krb-srvnam=NAME)])
 
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index d980f81046..1c6c0d3eea 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -188,6 +188,7 @@ with_selinux	= @with_selinux@
 with_systemd	= @with_systemd@
 with_libxml	= @with_libxml@
 with_libxslt	= @with_libxslt@
+with_krb_srvnam	= @with_krb_srvnam@
 with_system_tzdata = @with_system_tzdata@
 with_uuid	= @with_uuid@
 with_zlib	= @with_zlib@
diff --git a/src/test/kerberos/.gitignore b/src/test/kerberos/.gitignore
new file mode 100644
index 0000000000..871e943d50
--- /dev/null
+++ b/src/test/kerberos/.gitignore
@@ -0,0 +1,2 @@
+# Generated by test suite
+/tmp_check/
diff --git a/src/test/kerberos/Makefile b/src/test/kerberos/Makefile
new file mode 100644
index 0000000000..1363529498
--- /dev/null
+++ b/src/test/kerberos/Makefile
@@ -0,0 +1,25 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/test/kerberos
+#
+# Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/test/kerberos/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/test/kerberos
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+export with_krb_srvnam
+
+check:
+	$(prove_check)
+
+installcheck:
+	$(prove_installcheck)
+
+clean distclean maintainer-clean:
+	rm -rf tmp_check
diff --git a/src/test/kerberos/README b/src/test/kerberos/README
new file mode 100644
index 0000000000..94cf7ff42e
--- /dev/null
+++ b/src/test/kerberos/README
@@ -0,0 +1,25 @@
+src/test/kerberos/README
+
+Tests for Kerberos/GSSAPI functionality
+=======================================
+
+This directory contains a test suite for Kerberos/GSSAPI
+functionality.  This requires a full MIT Kerberos installation,
+including server and client tools, and is therefore kept separate and
+not run by default.  You might need to adjust some paths in the test
+file to have it find MIT Kerberos in a place that hadn't been thought
+of yet.
+
+Also, this test suite creates a KDC server that listens for TCP/IP
+connections on localhost without any real access control, so it is not
+safe to run this on a system where there might be untrusted local
+users.
+
+Running the tests
+=================
+
+    make check
+
+or
+
+    make installcheck
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
new file mode 100644
index 0000000000..5d6f3df8bf
--- /dev/null
+++ b/src/test/kerberos/t/001_auth.pl
@@ -0,0 +1,143 @@
+use strict;
+use warnings;
+use TestLib;
+use PostgresNode;
+use Test::More tests => 4;
+
+my ($krb5_bin_dir, $krb5_sbin_dir);
+
+if ($^O eq 'darwin')
+{
+	$krb5_bin_dir = '/usr/local/opt/krb5/bin';
+	$krb5_sbin_dir = '/usr/local/opt/krb5/sbin';
+}
+elsif ($^O eq 'linux')
+{
+	$krb5_sbin_dir = '/usr/sbin';
+}
+
+$ENV{PATH} = "$krb5_sbin_dir:$ENV{PATH}" if $krb5_sbin_dir;
+$ENV{PATH} = "$krb5_bin_dir:$ENV{PATH}" if $krb5_bin_dir;
+
+my $realm = 'EXAMPLE.COM';
+
+my $krb5_conf = "${TestLib::tmp_check}/krb5.conf";
+my $kdc_conf = "${TestLib::tmp_check}/kdc.conf";
+my $krb5_log = "${TestLib::tmp_check}/krb5libs.log";
+my $kdc_log = "${TestLib::tmp_check}/krb5kdc.log";
+my $kdc_port = int(rand() * 16384) + 49152;
+my $kdc_datadir = "${TestLib::tmp_check}/krb5kdc";
+my $kdc_pidfile = "${TestLib::tmp_check}/krb5kdc.pid";
+my $keytab = "${TestLib::tmp_check}/krb5.keytab";
+
+note "setting up Kerberos";
+
+my ($stdout, $krb5_version);
+IPC::Run::run [ 'krb5-config', '--version' ], '>', \$stdout or die "could not execute krb5-config";
+$stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/ or die "could not get Kerberos version";
+$krb5_version = $1;
+
+append_to_file($krb5_conf,
+qq![logging]
+default = FILE:$krb5_log
+kdc = FILE:$kdc_log
+
+[libdefaults]
+default_realm = $realm
+
+[realms]
+$realm = {
+    kdc = localhost:$kdc_port
+}!);
+
+append_to_file($kdc_conf,
+qq![kdcdefaults]
+!);
+# For new-enough versions of krb5, use the _listen settings rather
+# than the _ports settings so that we can bind to localhost only.
+if ($krb5_version >= 1.15)
+{
+	append_to_file($kdc_conf,
+qq!kdc_listen = localhost:$kdc_port
+kdc_tcp_listen = localhost:$kdc_port
+!);
+}
+else
+{
+	append_to_file($kdc_conf,
+qq!kdc_ports = $kdc_port
+kdc_tcp_ports = $kdc_port
+!);
+}
+append_to_file($kdc_conf,
+qq!
+[realms]
+$realm = {
+    database_name = $kdc_datadir/principal
+    admin_keytab = FILE:$kdc_datadir/kadm5.keytab
+    acl_file = $kdc_datadir/kadm5.acl
+    key_stash_file = $kdc_datadir/_k5.$realm
+}!);
+
+mkdir $kdc_datadir or die;
+
+$ENV{'KRB5_CONFIG'} = $krb5_conf;
+$ENV{'KRB5_KDC_PROFILE'} = $kdc_conf;
+
+my $service_principal = "$ENV{with_krb_srvnam}/localhost";
+
+system_or_bail 'kdb5_util', 'create', '-s', '-P', 'secret0';
+
+system_or_bail 'kadmin.local', '-q', "addprinc -pw secret1 test1";
+
+system_or_bail 'kadmin.local', '-q', "addprinc -randkey $service_principal";
+system_or_bail 'kadmin.local', '-q', "ktadd -k $keytab $service_principal";
+
+system_or_bail 'krb5kdc', '-P', $kdc_pidfile;
+
+END
+{
+    kill 'INT', `cat $kdc_pidfile` if -f $kdc_pidfile;
+}
+
+note "setting up PostgreSQL instance";
+
+my $node = get_new_node('node');
+$node->init;
+$node->append_conf('postgresql.conf', "listen_addresses = 'localhost'");
+$node->append_conf('postgresql.conf', "krb_server_keyfile = '$keytab'");
+$node->start;
+
+$node->safe_psql('postgres', 'CREATE USER test1;');
+
+note "running tests";
+
+sub test_access
+{
+    my ($node, $role, $expected_res, $test_name) = @_;
+
+    my $res = $node->psql('postgres', 'SELECT 1', tcpip => 1, extra_params => [ '-U', $role ]);
+    is($res, $expected_res, $test_name);
+}
+
+unlink($node->data_dir . '/pg_hba.conf');
+$node->append_conf('pg_hba.conf', qq{host all all localhost gss map=mymap});
+$node->restart;
+
+test_access($node, 'test1', 2, 'fails without ticket');
+
+system_or_bail 'echo secret1 | kinit test1';
+
+test_access($node, 'test1', 2, 'fails without mapping');
+
+$node->append_conf('pg_ident.conf', qq{mymap  /^(.*)\@$realm\$  \\1});
+$node->restart;
+
+test_access($node, 'test1', 0, 'succeeds with mapping');
+
+truncate($node->data_dir . '/pg_ident.conf', 0);
+unlink($node->data_dir . '/pg_hba.conf');
+$node->append_conf('pg_hba.conf', qq{host all all localhost gss include_realm=0});
+$node->restart;
+
+test_access($node, 'test1', 0, 'succeeds with include_realm=0');
diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm
index 1d5ac4ee35..fcc6de3671 100644
--- a/src/test/perl/PostgresNode.pm
+++ b/src/test/perl/PostgresNode.pm
@@ -1129,8 +1129,7 @@ sub psql
 	my $stderr            = $params{stderr};
 	my $timeout           = undef;
 	my $timeout_exception = 'psql timed out';
-	my @psql_params =
-	  ('psql', '-XAtq', '-d', $self->connstr($dbname), '-f', '-');
+	my $connstr           = $self->connstr($dbname);
 
 	# If the caller wants an array and hasn't passed stdout/stderr
 	# references, allocate temporary ones to capture them so we
@@ -1152,6 +1151,11 @@ sub psql
 	$params{on_error_stop} = 1 unless defined $params{on_error_stop};
 	$params{on_error_die}  = 0 unless defined $params{on_error_die};
 
+	$connstr .= ' host=localhost' if defined $params{tcpip};
+
+	my @psql_params =
+	  ('psql', '-XAtq', '-d', $connstr, '-f', '-');
+
 	push @psql_params, '-v', 'ON_ERROR_STOP=1' if $params{on_error_stop};
 	push @psql_params, @{ $params{extra_params} }
 	  if defined $params{extra_params};
-- 
2.16.1

#2Michael Paquier
michael@paquier.xyz
In reply to: Peter Eisentraut (#1)
Re: Kerberos test suite

On Wed, Feb 14, 2018 at 09:27:04AM -0500, Peter Eisentraut wrote:

Here is a patch with a test suite for the Kerberos/GSSAPI authentication
functionality. It's very similar in principle to the recently added
LDAP tests, and similar caveats apply.

You will need the client and server parts of a krb5 package
installation, possibly named krb5-workstation and krb5-server, or
perhaps krb5-user and krb5-kdc.

Thanks. Could you document that on the README please? krb5-user and
krb5-kdc is a split from Debian. For darwin, are you using macports or
homebrew? I would assume the later, and it would be nice to precise
that in the README as well. On Debian you need to install as well
krb5-admin-server as it includes kadmin.local which the test needs.
Once I understood that I have been able to run the tests.

(If it appears to hang for you in the "setting up Kerberos" step, you
might need more entropy/wait a while. That problem appears to be
limited to some virtual machine setups, but the specifics are not
clear.)

That's one of those "move your mouse" or "type randomly your keyboard"
to generate more entropy for the installation setup?

You have forgotten to update ALWAYS_SUBDIRS in src/test/Makefile.

+my ($stdout, $krb5_version);
+IPC::Run::run [ 'krb5-config', '--version' ], '>', \$stdout or die
"could not execute krb5-config";
+$stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/ or die "could not get
Kerberos version";
+$krb5_version = $1;
Time for a new routine command_log which executes the command, then
returns stdout and stderr to the caller?

+system_or_bail 'echo secret1 | kinit test1';
Using IPC::Run stuff would be better here.

@@ -1153,6 +1152,11 @@ sub psql
$params{on_error_stop} = 1 unless defined $params{on_error_stop};
$params{on_error_die} = 0 unless defined $params{on_error_die};

+   $connstr .= ' host=localhost' if defined $params{tcpip};
+
+   my @psql_params =
+     ('psql', '-XAtq', '-d', $connstr, '-f', '-');
This bit I don't like.  Wouldn't it be enough to abuse of extra_params
and use a custom connection string?  The last value wins in a psql
command. 
--
Michael
#3Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Peter Eisentraut (#1)
Re: Kerberos test suite

On Thu, Feb 15, 2018 at 3:27 AM, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

Here is a patch with a test suite for the Kerberos/GSSAPI authentication
functionality. It's very similar in principle to the recently added
LDAP tests, and similar caveats apply.

+not run by default.  You might need to adjust some paths in the test
+file to have it find MIT Kerberos in a place that hadn't been thought
+of yet.

FWIW it passes for me if I add this:

+elsif ($^O eq 'freebsd')
+{
+       $krb5_bin_dir = '/usr/local/bin';
+       $krb5_sbin_dir = '/usr/local/sbin';
+}

One thing that surprised me is that your test runs my system's
installed psql command out of my $PATH, not the one under test. Is
that expected?

--
Thomas Munro
http://www.enterprisedb.com

#4Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Thomas Munro (#3)
Re: Kerberos test suite

On 2/27/18 00:56, Thomas Munro wrote:

FWIW it passes for me if I add this:

+elsif ($^O eq 'freebsd')
+{
+       $krb5_bin_dir = '/usr/local/bin';
+       $krb5_sbin_dir = '/usr/local/sbin';

I suppose you only need the second one, right? The first one should be
in the path.

+}

One thing that surprised me is that your test runs my system's
installed psql command out of my $PATH, not the one under test. Is
that expected?

Oh, you probably have a psql in /usr/local/bin, which we prepend to the
path, per the above. We should probably append instead. The ldap test
suite has the same issue.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#5Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Peter Eisentraut (#4)
Re: Kerberos test suite

On Thu, Mar 1, 2018 at 4:43 AM, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

On 2/27/18 00:56, Thomas Munro wrote:

FWIW it passes for me if I add this:

+elsif ($^O eq 'freebsd')
+{
+       $krb5_bin_dir = '/usr/local/bin';
+       $krb5_sbin_dir = '/usr/local/sbin';

I suppose you only need the second one, right? The first one should be
in the path.

Erm. Turned out I had a couple of different versions in my path, but
your test only works with the binaries in the paths shown above (the
binaries installed from ports, or rather pkg install krb5, not the
ones from the base system).

munro@asterix $ /usr/bin/krb5-config --version
FreeBSD heimdal 1.1.0
munro@asterix $ /usr/local/bin/krb5-config --version
Kerberos 5 release 1.15.2

One thing that surprised me is that your test runs my system's
installed psql command out of my $PATH, not the one under test. Is
that expected?

Oh, you probably have a psql in /usr/local/bin, which we prepend to the
path, per the above. We should probably append instead. The ldap test
suite has the same issue.

Hmm. Not sure if appending is right either, since then you wouldn't
get the krb5_bin_dir etc in preference to the user's preexisting PATH,
so you'd break my attempt to use krb5-config 1.15.2 instead of heimdal
1.1.0. Maybe you need to prepend the user's krb5_bin_dir etc, and
then finally prepend the tmp install bin dir wherever that happens?
Or maybe you want to build absolute paths from the paths you have? I
didn't look at how this perl code is structured...

--
Thomas Munro
http://www.enterprisedb.com

#6Robbie Harwood
rharwood@redhat.com
In reply to: Michael Paquier (#2)
Re: Kerberos test suite

Michael Paquier <michael@paquier.xyz> writes:

On Wed, Feb 14, 2018 at 09:27:04AM -0500, Peter Eisentraut wrote:

(If it appears to hang for you in the "setting up Kerberos" step, you
might need more entropy/wait a while. That problem appears to be
limited to some virtual machine setups, but the specifics are not
clear.)

That's one of those "move your mouse" or "type randomly your keyboard"
to generate more entropy for the installation setup?

Right.

Whether this message can show up will depend on how the krb5 was built
(and how new it is). krb5 > 1.15 has support for getrandom(), so on
most Linux distros, if the machine has successfully ever created 256
bits of entropy, it won't block. On other platforms, use of
/dev/urandom requires a build flag.

Thanks,
--Robbie

#7Robbie Harwood
rharwood@redhat.com
In reply to: Thomas Munro (#5)
Re: Kerberos test suite

Thomas Munro <thomas.munro@enterprisedb.com> writes:

Peter Eisentraut <peter.eisentraut@2ndquadrant.com> wrote:

On 2/27/18 00:56, Thomas Munro wrote:

FWIW it passes for me if I add this:

+elsif ($^O eq 'freebsd')
+{
+       $krb5_bin_dir = '/usr/local/bin';
+       $krb5_sbin_dir = '/usr/local/sbin';

I suppose you only need the second one, right? The first one should be
in the path.

Erm. Turned out I had a couple of different versions in my path, but
your test only works with the binaries in the paths shown above (the
binaries installed from ports, or rather pkg install krb5, not the
ones from the base system).

munro@asterix $ /usr/bin/krb5-config --version
FreeBSD heimdal 1.1.0

This one's Heimdal; the test is intended to only work with MIT. Perhaps
it should check the output of krb5-config instead of failing
confusingly?

munro@asterix $ /usr/local/bin/krb5-config --version
Kerberos 5 release 1.15.2

Thanks,
--Robbie

#8Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Michael Paquier (#2)
1 attachment(s)
Re: Kerberos test suite

On 2/27/18 00:21, Michael Paquier wrote:

Thanks. Could you document that on the README please? krb5-user and
krb5-kdc is a split from Debian. For darwin, are you using macports or
homebrew? I would assume the later, and it would be nice to precise
that in the README as well. On Debian you need to install as well
krb5-admin-server as it includes kadmin.local which the test needs.
Once I understood that I have been able to run the tests.

Added to README.

You have forgotten to update ALWAYS_SUBDIRS in src/test/Makefile.

Fixed, and updated to reflect recent changes with PG_TEST_EXTRA etc.

+my ($stdout, $krb5_version);
+IPC::Run::run [ 'krb5-config', '--version' ], '>', \$stdout or die
"could not execute krb5-config";
+$stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/ or die "could not get
Kerberos version";
+$krb5_version = $1;
Time for a new routine command_log which executes the command, then
returns stdout and stderr to the caller?

I didn't do anything about this. Do we have any more places where that
would be needed right now?

+system_or_bail 'echo secret1 | kinit test1';
Using IPC::Run stuff would be better here.

done

@@ -1153,6 +1152,11 @@ sub psql
$params{on_error_stop} = 1 unless defined $params{on_error_stop};
$params{on_error_die} = 0 unless defined $params{on_error_die};

+   $connstr .= ' host=localhost' if defined $params{tcpip};
+
+   my @psql_params =
+     ('psql', '-XAtq', '-d', $connstr, '-f', '-');
This bit I don't like.  Wouldn't it be enough to abuse of extra_params
and use a custom connection string?  The last value wins in a psql
command. 

done

Also included feedback from Thomas Munro.

New patch attached.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

v2-0001-Tests-for-Kerberos-GSSAPI-authentication.patchtext/plain; charset=UTF-8; name=v2-0001-Tests-for-Kerberos-GSSAPI-authentication.patch; x-mac-creator=0; x-mac-type=0Download
From f694b67401d93051c3442f60c06f8327391239cf Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Mon, 5 Mar 2018 14:42:11 -0500
Subject: [PATCH v2] Tests for Kerberos/GSSAPI authentication

---
 configure                       |   4 +
 configure.in                    |   2 +
 doc/src/sgml/regress.sgml       |  12 ++-
 src/Makefile.global.in          |   2 +
 src/test/Makefile               |   7 +-
 src/test/kerberos/.gitignore    |   2 +
 src/test/kerberos/Makefile      |  25 ++++++
 src/test/kerberos/README        |  35 ++++++++
 src/test/kerberos/t/001_auth.pl | 177 ++++++++++++++++++++++++++++++++++++++++
 9 files changed, 264 insertions(+), 2 deletions(-)
 create mode 100644 src/test/kerberos/.gitignore
 create mode 100644 src/test/kerberos/Makefile
 create mode 100644 src/test/kerberos/README
 create mode 100644 src/test/kerberos/t/001_auth.pl

diff --git a/configure b/configure
index 1242e310b4..3943711283 100755
--- a/configure
+++ b/configure
@@ -709,7 +709,9 @@ with_systemd
 with_selinux
 with_openssl
 with_ldap
+with_krb_srvnam
 krb_srvtab
+with_gssapi
 with_python
 with_perl
 with_tcl
@@ -5788,6 +5790,7 @@ $as_echo "$with_gssapi" >&6; }
 
 
 
+
 #
 # Kerberos configuration parameters
 #
@@ -5815,6 +5818,7 @@ fi
 
 
 
+
 cat >>confdefs.h <<_ACEOF
 #define PG_KRB_SRVNAM "$with_krb_srvnam"
 _ACEOF
diff --git a/configure.in b/configure.in
index aee3ab0867..1babdbb755 100644
--- a/configure.in
+++ b/configure.in
@@ -638,6 +638,7 @@ PGAC_ARG_BOOL(with, gssapi, no, [build with GSSAPI support],
   krb_srvtab="FILE:\$(sysconfdir)/krb5.keytab"
 ])
 AC_MSG_RESULT([$with_gssapi])
+AC_SUBST(with_gssapi)
 
 
 AC_SUBST(krb_srvtab)
@@ -650,6 +651,7 @@ PGAC_ARG_REQ(with, krb-srvnam,
              [NAME], [default service principal name in Kerberos (GSSAPI) [postgres]],
              [],
              [with_krb_srvnam="postgres"])
+AC_SUBST(with_krb_srvnam)
 AC_DEFINE_UNQUOTED([PG_KRB_SRVNAM], ["$with_krb_srvnam"],
                    [Define to the name of the default PostgreSQL service principal in Kerberos (GSSAPI). (--with-krb-srvnam=NAME)])
 
diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml
index 3c448dc5bc..673a8c2164 100644
--- a/doc/src/sgml/regress.sgml
+++ b/doc/src/sgml/regress.sgml
@@ -220,10 +220,20 @@ <title>Additional Test Suites</title>
    <varname>PG_TEST_EXTRA</varname> to a whitespace-separated list, for
    example:
 <programlisting>
-make check-world PG_TEST_EXTRA='ldap ssl'
+make check-world PG_TEST_EXTRA='kerberos ldap ssl'
 </programlisting>
    The following values are currently supported:
    <variablelist>
+    <varlistentry>
+     <term><literal>kerberos</literal></term>
+     <listitem>
+      <para>
+       Runs the test suite under <filename>src/test/kerberos</filename>.  This
+       requires an MIT Kerberos installation and opens TCP/IP listen sockets.
+      </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry>
      <term><literal>ldap</literal></term>
      <listitem>
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index dcb8dc5d90..15c14951e8 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -186,6 +186,8 @@ with_tcl	= @with_tcl@
 with_openssl	= @with_openssl@
 with_selinux	= @with_selinux@
 with_systemd	= @with_systemd@
+with_gssapi	= @with_gssapi@
+with_krb_srvnam	= @with_krb_srvnam@
 with_ldap	= @with_ldap@
 with_libxml	= @with_libxml@
 with_libxslt	= @with_libxslt@
diff --git a/src/test/Makefile b/src/test/Makefile
index 3de9428299..efb206aa75 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -17,6 +17,11 @@ SUBDIRS = perl regress isolation modules authentication recovery subscription
 # Test suites that are not safe by default but can be run if selected
 # by the user via the whitespace-separated list in variable
 # PG_TEST_EXTRA:
+ifeq ($(with_gssapi),yes)
+ifneq (,$(filter kerberos,$(PG_TEST_EXTRA)))
+SUBDIRS += kerberos
+endif
+endif
 ifeq ($(with_ldap),yes)
 ifneq (,$(filter ldap,$(PG_TEST_EXTRA)))
 SUBDIRS += ldap
@@ -32,7 +37,7 @@ endif
 # clean" etc to recurse into them.  (We must filter out those that we
 # have conditionally included into SUBDIRS above, else there will be
 # make confusion.)
-ALWAYS_SUBDIRS = $(filter-out $(SUBDIRS),examples ldap locale thread ssl)
+ALWAYS_SUBDIRS = $(filter-out $(SUBDIRS),examples kerberos ldap locale thread ssl)
 
 # We want to recurse to all subdirs for all standard targets, except that
 # installcheck and install should not recurse into the subdirectory "modules".
diff --git a/src/test/kerberos/.gitignore b/src/test/kerberos/.gitignore
new file mode 100644
index 0000000000..871e943d50
--- /dev/null
+++ b/src/test/kerberos/.gitignore
@@ -0,0 +1,2 @@
+# Generated by test suite
+/tmp_check/
diff --git a/src/test/kerberos/Makefile b/src/test/kerberos/Makefile
new file mode 100644
index 0000000000..4df4989470
--- /dev/null
+++ b/src/test/kerberos/Makefile
@@ -0,0 +1,25 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/test/kerberos
+#
+# Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/test/kerberos/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/test/kerberos
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+export with_gssapi with_krb_srvnam
+
+check:
+	$(prove_check)
+
+installcheck:
+	$(prove_installcheck)
+
+clean distclean maintainer-clean:
+	rm -rf tmp_check
diff --git a/src/test/kerberos/README b/src/test/kerberos/README
new file mode 100644
index 0000000000..cdfaeb89d3
--- /dev/null
+++ b/src/test/kerberos/README
@@ -0,0 +1,35 @@
+src/test/kerberos/README
+
+Tests for Kerberos/GSSAPI functionality
+=======================================
+
+This directory contains a test suite for Kerberos/GSSAPI
+functionality.  This requires a full MIT Kerberos installation,
+including server and client tools, and is therefore kept separate and
+not run by default.
+
+Also, this test suite creates a KDC server that listens for TCP/IP
+connections on localhost without any real access control, so it is not
+safe to run this on a system where there might be untrusted local
+users.
+
+Running the tests
+=================
+
+    make check
+
+or
+
+    make installcheck
+
+Requirements
+============
+
+MIT Kerberos server and client tools are required.  Heimdal is not
+supported.
+
+Debian/Ubuntu packages: krb5-admin-server krb5-kdc krb5-user
+
+RHEL/CentOS packages: krb5-server krb5-workstation
+
+FreeBSD port: krb5 (base system has Heimdal)
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
new file mode 100644
index 0000000000..f26460e627
--- /dev/null
+++ b/src/test/kerberos/t/001_auth.pl
@@ -0,0 +1,177 @@
+use strict;
+use warnings;
+use TestLib;
+use PostgresNode;
+use Test::More;
+
+if ($ENV{with_gssapi} eq 'yes')
+{
+	plan tests => 4;
+}
+else
+{
+	plan skip_all => 'GSSAPI/Kerberos not supported by this build';
+}
+
+my ($krb5_bin_dir, $krb5_sbin_dir);
+
+if ($^O eq 'darwin')
+{
+	$krb5_bin_dir = '/usr/local/opt/krb5/bin';
+	$krb5_sbin_dir = '/usr/local/opt/krb5/sbin';
+}
+elsif ($^O eq 'freebsd')
+{
+	$krb5_bin_dir = '/usr/local/bin';
+	$krb5_sbin_dir = '/usr/local/sbin';
+}
+elsif ($^O eq 'linux')
+{
+	$krb5_sbin_dir = '/usr/sbin';
+}
+
+my $krb5_config = 'krb5-config';
+my $kinit = 'kinit';
+my $kdb5_util = 'kdb5_util';
+my $kadmin_local = 'kadmin.local';
+my $krb5kdc = 'krb5kdc';
+
+if ($krb5_bin_dir && -d $krb5_bin_dir)
+{
+	$krb5_config = $krb5_bin_dir . '/' . $krb5_config;
+	$kinit = $krb5_bin_dir . '/' . $kinit;
+}
+if ($krb5_sbin_dir && -d $krb5_sbin_dir)
+{
+	$kdb5_util = $krb5_sbin_dir . '/' . $kdb5_util;
+	$kadmin_local = $krb5_sbin_dir . '/' . $kadmin_local;
+	$krb5kdc = $krb5_sbin_dir . '/' . $krb5kdc;
+}
+
+my $realm = 'EXAMPLE.COM';
+
+my $krb5_conf = "${TestLib::tmp_check}/krb5.conf";
+my $kdc_conf = "${TestLib::tmp_check}/kdc.conf";
+my $krb5_log = "${TestLib::tmp_check}/krb5libs.log";
+my $kdc_log = "${TestLib::tmp_check}/krb5kdc.log";
+my $kdc_port = int(rand() * 16384) + 49152;
+my $kdc_datadir = "${TestLib::tmp_check}/krb5kdc";
+my $kdc_pidfile = "${TestLib::tmp_check}/krb5kdc.pid";
+my $keytab = "${TestLib::tmp_check}/krb5.keytab";
+
+note "setting up Kerberos";
+
+my ($stdout, $krb5_version);
+run_log [ $krb5_config, '--version' ], '>', \$stdout or BAIL_OUT("could not execute krb5-config");
+BAIL_OUT("Heimdal is not supported") if $stdout =~ m/heimdal/;
+$stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/ or BAIL_OUT("could not get Kerberos version");
+$krb5_version = $1;
+
+append_to_file($krb5_conf,
+qq![logging]
+default = FILE:$krb5_log
+kdc = FILE:$kdc_log
+
+[libdefaults]
+default_realm = $realm
+
+[realms]
+$realm = {
+    kdc = localhost:$kdc_port
+}!);
+
+append_to_file($kdc_conf,
+qq![kdcdefaults]
+!);
+# For new-enough versions of krb5, use the _listen settings rather
+# than the _ports settings so that we can bind to localhost only.
+if ($krb5_version >= 1.15)
+{
+	append_to_file($kdc_conf,
+qq!kdc_listen = localhost:$kdc_port
+kdc_tcp_listen = localhost:$kdc_port
+!);
+}
+else
+{
+	append_to_file($kdc_conf,
+qq!kdc_ports = $kdc_port
+kdc_tcp_ports = $kdc_port
+!);
+}
+append_to_file($kdc_conf,
+qq!
+[realms]
+$realm = {
+    database_name = $kdc_datadir/principal
+    admin_keytab = FILE:$kdc_datadir/kadm5.keytab
+    acl_file = $kdc_datadir/kadm5.acl
+    key_stash_file = $kdc_datadir/_k5.$realm
+}!);
+
+mkdir $kdc_datadir or die;
+
+$ENV{'KRB5_CONFIG'} = $krb5_conf;
+$ENV{'KRB5_KDC_PROFILE'} = $kdc_conf;
+
+my $service_principal = "$ENV{with_krb_srvnam}/localhost";
+
+system_or_bail $kdb5_util, 'create', '-s', '-P', 'secret0';
+
+my $test1_password = 'secret1';
+system_or_bail $kadmin_local, '-q', "addprinc -pw $test1_password test1";
+
+system_or_bail $kadmin_local, '-q', "addprinc -randkey $service_principal";
+system_or_bail $kadmin_local, '-q', "ktadd -k $keytab $service_principal";
+
+system_or_bail $krb5kdc, '-P', $kdc_pidfile;
+
+END
+{
+    kill 'INT', `cat $kdc_pidfile` if -f $kdc_pidfile;
+}
+
+note "setting up PostgreSQL instance";
+
+my $node = get_new_node('node');
+$node->init;
+$node->append_conf('postgresql.conf', "listen_addresses = 'localhost'");
+$node->append_conf('postgresql.conf', "krb_server_keyfile = '$keytab'");
+$node->start;
+
+$node->safe_psql('postgres', 'CREATE USER test1;');
+
+note "running tests";
+
+sub test_access
+{
+	my ($node, $role, $expected_res, $test_name) = @_;
+
+	# need to connect over TCP/IP for Kerberos
+	my $res = $node->psql('postgres', 'SELECT 1',
+						  extra_params => [ '-d', $node->connstr('postgres').' host=localhost',
+											'-U', $role ]);
+	is($res, $expected_res, $test_name);
+}
+
+unlink($node->data_dir . '/pg_hba.conf');
+$node->append_conf('pg_hba.conf', qq{host all all localhost gss map=mymap});
+$node->restart;
+
+test_access($node, 'test1', 2, 'fails without ticket');
+
+run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
+
+test_access($node, 'test1', 2, 'fails without mapping');
+
+$node->append_conf('pg_ident.conf', qq{mymap  /^(.*)\@$realm\$  \\1});
+$node->restart;
+
+test_access($node, 'test1', 0, 'succeeds with mapping');
+
+truncate($node->data_dir . '/pg_ident.conf', 0);
+unlink($node->data_dir . '/pg_hba.conf');
+$node->append_conf('pg_hba.conf', qq{host all all localhost gss include_realm=0});
+$node->restart;
+
+test_access($node, 'test1', 0, 'succeeds with include_realm=0');

base-commit: 2f3e2340cd1b67e91cefdf45e4c915297d1034e2
-- 
2.16.2

#9Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Peter Eisentraut (#8)
Re: Kerberos test suite

On Tue, Mar 6, 2018 at 8:45 AM, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

New patch attached.

Passes here. LGTM.

Only complaint is your assumption that 'darwin' implies HomeBrew
installation paths, but you already did that in other tests before
this one. Perhaps we can sort that out globally in some future patch.

PS Once this lands I'll adjust my Commitfest testing bot to use
PG_TEST_EXTRA='kerberos ldap ssl'.

--
Thomas Munro
http://www.enterprisedb.com

#10Michael Paquier
michael@paquier.xyz
In reply to: Peter Eisentraut (#8)
Re: Kerberos test suite

On Mon, Mar 05, 2018 at 02:45:07PM -0500, Peter Eisentraut wrote:

On 2/27/18 00:21, Michael Paquier wrote:

+my ($stdout, $krb5_version);
+IPC::Run::run [ 'krb5-config', '--version' ], '>', \$stdout or die
"could not execute krb5-config";
+$stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/ or die "could not get
Kerberos version";
+$krb5_version = $1;
Time for a new routine command_log which executes the command, then
returns stdout and stderr to the caller?

I didn't do anything about this. Do we have any more places where that
would be needed right now?

I don't mind if this happens later on. I can send a patch as well if
necessary. There are a couple of places where things can be
consolidated using a single entry point:
- 010_pg_archivecleanup.pl
- PostgresNode::psql, where we could merge things.
- PostgresNode::poll_query_until
- PostgresNode::pg_recvlogical_upto
- TestLib::check_pg_config
- TestLib::program_help_ok
- TestLib::program_version_ok
- TestLib::program_options_handling_ok
- TestLib::command_like
- TestLib::command_like_safe
- TestLib::command_fails_like
- TestLib::command_checks_all

+if ($ENV{with_gssapi} eq 'yes')
+{
+   plan tests => 4;
+}
+else
+{
+   plan skip_all => 'GSSAPI/Kerberos not supported by this build';
+}
Thanks for adding this sanity check.

+my $kdc_port = int(rand() * 16384) + 49152;
That may not be worth worrying as that's an ephemeral port range but it
can make the test a bit unstable...

Perhaps the tests should be skipped on Windows or just produce an error?
Like LDAP tests, libraries are supported on Windows but the hardcoded
paths make things harder to handle there.
--
Michael

#11Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Thomas Munro (#9)
Re: Kerberos test suite

On 3/5/18 16:34, Thomas Munro wrote:

On Tue, Mar 6, 2018 at 8:45 AM, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

New patch attached.

Passes here. LGTM.

committed

Only complaint is your assumption that 'darwin' implies HomeBrew
installation paths, but you already did that in other tests before
this one. Perhaps we can sort that out globally in some future patch.

right

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#12Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Michael Paquier (#10)
Re: Kerberos test suite

On 3/5/18 21:08, Michael Paquier wrote:

+my $kdc_port = int(rand() * 16384) + 49152;
That may not be worth worrying as that's an ephemeral port range but it
can make the test a bit unstable...

This is what we've been using in the other tests as well. It's clearly
not optimal, but making it more robust in a platform-agnostic way seems
tricky.

Perhaps the tests should be skipped on Windows or just produce an error?
Like LDAP tests, libraries are supported on Windows but the hardcoded
paths make things harder to handle there.

Hmm, why couldn't someone install MIT krb5 on Windows and give it a try?
We don't need to block the platform outright.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#13Robbie Harwood
rharwood@redhat.com
In reply to: Peter Eisentraut (#12)
Re: Kerberos test suite

Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:

On 3/5/18 21:08, Michael Paquier wrote:

Perhaps the tests should be skipped on Windows or just produce an error?
Like LDAP tests, libraries are supported on Windows but the hardcoded
paths make things harder to handle there.

Hmm, why couldn't someone install MIT krb5 on Windows and give it a try?
We don't need to block the platform outright.

krb5kdc doesn't support windows; only the client portion works there.

Thanks,
--Robbie

#14Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Peter Eisentraut (#12)
Re: Kerberos test suite

Peter Eisentraut wrote:

On 3/5/18 21:08, Michael Paquier wrote:

+my $kdc_port = int(rand() * 16384) + 49152;
That may not be worth worrying as that's an ephemeral port range but it
can make the test a bit unstable...

This is what we've been using in the other tests as well. It's clearly
not optimal, but making it more robust in a platform-agnostic way seems
tricky.

We have port-testing code in PostgresNode.pm::get_new_node; maybe that
could be taking out of there into TestLib.pm?

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#15Noah Misch
noah@leadboat.com
In reply to: Peter Eisentraut (#11)
1 attachment(s)
Re: Kerberos test suite

On Tue, Mar 06, 2018 at 10:58:54AM -0500, Peter Eisentraut wrote:

On 3/5/18 16:34, Thomas Munro wrote:

On Tue, Mar 6, 2018 at 8:45 AM, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

New patch attached.

Passes here. LGTM.

committed

This fails on my machine, where /etc/hosts has:

127.0.0.1 localhost.localdomain localhost
::1 localhost6.localdomain6 localhost6

This is CentOS 7, but I may have written that myself. First failure:

psql: FATAL: no pg_hba.conf entry for host "127.0.0.1", user "test1", database "postgres", SSL off
not ok 3 - succeeds with mapping

Bypassing that, by recognizing localhost.localdomain in pg_hba.conf, unearths:

psql: GSSAPI continuation error: Unspecified GSS failure. Minor code may provide more information
GSSAPI continuation error: Server krbtgt/LOCALDOMAIN@EXAMPLE.COM not found in Kerberos database
not ok 3 - succeeds with mapping

On the client side, Kerberos is canonicalizing "localhost" to
"localhost.localdomain" as part of constructing the service principal.
"$service_principal = "$ENV{with_krb_srvnam}/localhost.localdomain" was a
quick workaround. For the long-term fix, let's use hostaddr= and a fictitious
host=, as attached. This makes us independent of local name resolution and
IPv6 configuration, and it's more like how PostgresNode operates on systems
that use TCP instead of unix_socket_directories (Windows). I considered
adding dns_canonicalize_hostname to $krb5_config, but that is new as of
krb5-1.12 and does not help the pg_hba.conf side of the problem.

Attachments:

kerberos-test-localhost-v1.patchtext/plain; charset=us-asciiDownload
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index 54f5647..1be89ae 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -48,6 +48,8 @@ if ($krb5_sbin_dir && -d $krb5_sbin_dir)
 	$krb5kdc      = $krb5_sbin_dir . '/' . $krb5kdc;
 }
 
+my $host     = 'auth-test-localhost.postgresql.example.com';
+my $hostaddr = '127.0.0.1';
 my $realm = 'EXAMPLE.COM';
 
 my $krb5_conf   = "${TestLib::tmp_check}/krb5.conf";
@@ -80,7 +82,7 @@ default_realm = $realm
 
 [realms]
 $realm = {
-    kdc = localhost:$kdc_port
+    kdc = $hostaddr:$kdc_port
 }!);
 
 append_to_file(
@@ -94,8 +96,8 @@ if ($krb5_version >= 1.15)
 {
 	append_to_file(
 		$kdc_conf,
-		qq!kdc_listen = localhost:$kdc_port
-kdc_tcp_listen = localhost:$kdc_port
+		qq!kdc_listen = $hostaddr:$kdc_port
+kdc_tcp_listen = $hostaddr:$kdc_port
 !);
 }
 else
@@ -122,7 +124,7 @@ mkdir $kdc_datadir or die;
 $ENV{'KRB5_CONFIG'}      = $krb5_conf;
 $ENV{'KRB5_KDC_PROFILE'} = $kdc_conf;
 
-my $service_principal = "$ENV{with_krb_srvnam}/localhost";
+my $service_principal = "$ENV{with_krb_srvnam}/$host";
 
 system_or_bail $kdb5_util, 'create', '-s', '-P', 'secret0';
 
@@ -143,7 +145,7 @@ note "setting up PostgreSQL instance";
 
 my $node = get_new_node('node');
 $node->init;
-$node->append_conf('postgresql.conf', "listen_addresses = 'localhost'");
+$node->append_conf('postgresql.conf', "listen_addresses = '$hostaddr'");
 $node->append_conf('postgresql.conf', "krb_server_keyfile = '$keytab'");
 $node->start;
 
@@ -160,7 +162,8 @@ sub test_access
 		'postgres',
 		'SELECT 1',
 		extra_params => [
-			'-d', $node->connstr('postgres') . ' host=localhost',
+			'-d',
+			$node->connstr('postgres') . " host=$host hostaddr=$hostaddr",
 			'-U', $role
 		]);
 	is($res, $expected_res, $test_name);
@@ -168,7 +171,8 @@ sub test_access
 }
 
 unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf', qq{host all all localhost gss map=mymap});
+$node->append_conf('pg_hba.conf',
+	qq{host all all $hostaddr/32 gss map=mymap});
 $node->restart;
 
 test_access($node, 'test1', 2, 'fails without ticket');
@@ -185,7 +189,7 @@ test_access($node, 'test1', 0, 'succeeds with mapping');
 truncate($node->data_dir . '/pg_ident.conf', 0);
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{host all all localhost gss include_realm=0});
+	qq{host all all $hostaddr/32 gss include_realm=0});
 $node->restart;
 
 test_access($node, 'test1', 0, 'succeeds with include_realm=0');