The sepgsql tests have not been integrated into the Meson build system yet. I propose to fix that here.

One problem there was that the tests use a very custom construction where a top-level shell script internally calls make. I have converted this to a TAP script that does the preliminary checks and then calls pg_regress directly, without make. This seems to get the job done. Also, once you have your SELinux environment set up as required, the test now works fully automatically; you don't have to do any manual prep work. The whole thing is guarded by PG_TEST_EXTRA=sepgsql now.

Some comments and questions:

- Do we want to keep the old way to run the test? I don't know all the testing scenarios that people might be interested in, but of course it would also be good to cut down on the duplication in the test files.

- Strangely, there was apparently so far no way to get to the build directory from a TAP script. They only ever want to read files from the source directory. So I had to add that.

- If you go through the pre-test checks in contrib/sepgsql/test_sepgsql, I have converted most of these checks to the Perl script. Some of the checks are obsolete, because they check whether the database has been correctly initialized, which is now done by the TAP script anyway. One check that I wasn't sure about is the

# 'psql' command must be executable from test domain

The old test was checking the installation tree, which I guess could be set up in random ways. But do we need this kind of check if we are using a temporary installation?

As mentioned in the patch, the documentation needs to be updated. This depends on the outcome of the question above whether we want to keep the old tests in some way.
From 2f8e73932b1068caf696582487de9e100fcd46be Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Mon, 13 May 2024 07:55:55 +0200
Subject: [PATCH v1] Convert sepgsql tests to TAP

Add a TAP test for sepgsql.  This automates the previously required
manual setup before the test.  The actual tests are still run by
pg_regress, but not called from within the TAP Perl script.

TODO: remove the old test scripts?
---
 contrib/sepgsql/.gitignore       |   3 +
 contrib/sepgsql/Makefile         |   3 +
 contrib/sepgsql/meson.build      |  11 +-
 contrib/sepgsql/t/001_sepgsql.pl | 248 +++++++++++++++++++++++++++++++
 doc/src/sgml/regress.sgml        |  11 ++
 doc/src/sgml/sepgsql.sgml        |   2 +
 meson.build                      |   1 +
 src/Makefile.global.in           |   1 +
 8 files changed, 279 insertions(+), 1 deletion(-)
 create mode 100644 contrib/sepgsql/t/001_sepgsql.pl

diff --git a/contrib/sepgsql/.gitignore b/contrib/sepgsql/.gitignore
index 31613e011f5..7cadb9419e9 100644
--- a/contrib/sepgsql/.gitignore
+++ b/contrib/sepgsql/.gitignore
@@ -1,7 +1,10 @@
 /sepgsql.sql
+# FIXME
 /sepgsql-regtest.fc
 /sepgsql-regtest.if
 /sepgsql-regtest.pp
 /tmp
 # Generated subdirectories
+/log/
 /results/
+/tmp_check/
diff --git a/contrib/sepgsql/Makefile b/contrib/sepgsql/Makefile
index afca75b693f..5cc9697736c 100644
--- a/contrib/sepgsql/Makefile
+++ b/contrib/sepgsql/Makefile
@@ -15,6 +15,9 @@ OBJS = \
 DATA_built = sepgsql.sql
 PGFILEDESC = "sepgsql - SELinux integration"
 
+TAP_TESTS = 1
+
+# FIXME
 # Note: because we don't tell the Makefile there are any regression tests,
 # we have to clean those result files explicitly
 EXTRA_CLEAN = -r $(pg_regress_clean_files) tmp/ *.pp sepgsql-regtest.if 
sepgsql-regtest.fc
diff --git a/contrib/sepgsql/meson.build b/contrib/sepgsql/meson.build
index 9544efe0287..5817ba30a58 100644
--- a/contrib/sepgsql/meson.build
+++ b/contrib/sepgsql/meson.build
@@ -40,4 +40,13 @@ contrib_targets += custom_target('sepgsql.sql',
   install_dir: contrib_data_args['install_dir'],
 )
 
-# TODO: implement sepgsql tests
+tests += {
+  'name': 'sepgsql',
+  'sd': meson.current_source_dir(),
+  'bd': meson.current_build_dir(),
+  'tap': {
+    'tests': [
+      't/001_sepgsql.pl',
+    ],
+  },
+}
diff --git a/contrib/sepgsql/t/001_sepgsql.pl b/contrib/sepgsql/t/001_sepgsql.pl
new file mode 100644
index 00000000000..82bba5774ce
--- /dev/null
+++ b/contrib/sepgsql/t/001_sepgsql.pl
@@ -0,0 +1,248 @@
+
+# Copyright (c) 2024, PostgreSQL Global Development Group
+
+use strict;
+use warnings FATAL => 'all';
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+
+use Test::More;
+
+if (!$ENV{PG_TEST_EXTRA} || $ENV{PG_TEST_EXTRA} !~ /\bsepgsql\b/)
+{
+       plan skip_all =>
+         'Potentially unsafe test sepgsql not enabled in PG_TEST_EXTRA';
+}
+
+note "checking selinux environment";
+
+# matchpathcon must be present to assess whether the installation environment
+# is OK.
+note "checking for matchpathcon";
+if (system('matchpathcon -n . >/dev/null 2>&1') != 0)
+{
+       diag <<EOS;
+
+The matchpathcon command must be available.
+Please install it or update your PATH to include it
+(it is typically in '/usr/sbin', which might not be in your PATH).
+matchpathcon is typically included in the libselinux-utils package.
+EOS
+       die;
+}
+
+# runcon must be present to launch psql using the correct environment
+note "checking for runcon";
+if (system('runcon --help >/dev/null 2>&1') != 0)
+{
+       diag <<EOS;
+
+The runcon command must be available.
+runcon is typically included in the coreutils package.
+EOS
+       die;
+}
+
+# check sestatus too, since that lives in yet another package
+note "checking for sestatus";
+if (system('sestatus >/dev/null 2>&1') != 0)
+{
+       diag <<EOS;
+
+The sestatus command must be available.
+sestatus is typically included in the policycoreutils package.
+EOS
+       die;
+}
+
+# check that the user is running in the unconfined_t domain
+note "checking current user domain";
+my $DOMAIN = `id -Z 2>/dev/null | sed 's/:/ /g' | awk '{print \$3}'`;
+chomp $DOMAIN;
+note "current user domain is '$DOMAIN'";
+if ($DOMAIN ne 'unconfined_t')
+{
+       diag <<'EOS';
+
+The regression tests must be launched from the unconfined_t domain.
+
+The unconfined_t domain is typically the default domain for user
+shell processes.  If the default has been changed on your system,
+you can revert the changes like this:
+
+  $ sudo semanage login -d `whoami`
+
+Or, you can add a setting to log in using the unconfined_t domain:
+
+  $ sudo semanage login -a -s unconfined_u -r s0-s0:c0.c255 `whoami`
+
+EOS
+       die;
+}
+
+# SELinux must be configured in enforcing mode
+note "checking selinux operating mode";
+my $CURRENT_MODE =
+  `LANG=C sestatus | grep '^Current mode:' | awk '{print \$3}'`;
+chomp $CURRENT_MODE;
+note "current operating mode is '$CURRENT_MODE'";
+if ($CURRENT_MODE eq 'enforcing')
+{
+       # OK
+}
+elsif ($CURRENT_MODE eq 'permissive' || $CURRENT_MODE eq 'disabled')
+{
+       diag <<'EOS';
+
+Before running the regression tests, SELinux must be enabled and
+must be running in enforcing mode.
+
+If SELinux is currently running in permissive mode, you can
+switch to enforcing mode using the 'setenforce' command.
+
+  $ sudo setenforce 1
+
+The system default setting is configured in /etc/selinux/config,
+or using a kernel boot parameter.
+EOS
+       die;
+}
+else
+{
+       diag <<EOS;
+
+Unable to determine the current selinux operating mode.  Please
+verify that the sestatus command is installed and in your PATH.
+EOS
+       die;
+}
+
+# 'sepgsql-regtest' policy module must be loaded
+note "checking for sepgsql-regtest policy";
+my $SELINUX_MNT = `sestatus | grep '^SELinuxfs mount:' | awk '{print \$3}'`;
+chomp $SELINUX_MNT;
+if ($SELINUX_MNT eq "")
+{
+       diag <<EOS;
+
+Unable to find SELinuxfs mount point.
+
+The sestatus command should report the location where SELinuxfs
+is mounted, but did not do so.
+EOS
+       die;
+}
+if (!-e "${SELINUX_MNT}/booleans/sepgsql_regression_test_mode")
+{
+       diag <<'EOS';
+
+The 'sepgsql-regtest' policy module appears not to be installed.
+Without this policy installed, the regression tests will fail.
+You can install this module using the following commands:
+
+  $ make -f /usr/share/selinux/devel/Makefile
+  $ sudo semodule -u sepgsql-regtest.pp
+
+To confirm that the policy package is installed, use this command:
+
+  $ sudo semodule -l | grep sepgsql
+
+EOS
+       die;
+}
+
+# Verify that sepgsql_regression_test_mode is active.
+note "checking whether policy is enabled";
+foreach
+  my $policy ('sepgsql_regression_test_mode', 'sepgsql_enable_users_ddl')
+{
+       my $POLICY_STATUS = `getsebool $policy | awk '{print \$3}'`;
+       chomp $POLICY_STATUS;
+       note "$policy is '$POLICY_STATUS'";
+       if ($POLICY_STATUS ne "on")
+       {
+               diag <<EOS;
+
+The SELinux boolean '$policy' must be
+turned on in order to enable the rules necessary to run the
+regression tests.
+
+EOS
+
+               if ($POLICY_STATUS eq "")
+               {
+                       diag <<EOS;
+We attempted to determine the state of this Boolean using
+'getsebool', but that command did not produce the expected
+output.  Please verify that getsebool is available and in
+your PATH.
+EOS
+               }
+               else
+               {
+                       diag <<EOS;
+You can turn on this variable using the following commands:
+
+  \$ sudo setsebool $policy on
+
+For security reasons, it is suggested that you turn off this
+variable when regression testing is complete and the associated
+rules are no longer needed.
+EOS
+               }
+               die;
+       }
+}
+
+
+#
+# checking complete - let's run the tests
+#
+
+note "running sepgsql regression tests";
+
+my $node;
+
+$node = PostgreSQL::Test::Cluster->new('test');
+$node->init;
+$node->append_conf('postgresql.conf', 'log_statement=none');
+
+{
+       local %ENV = $node->_get_env();
+
+       my $result = run_log(
+               [
+                       'postgres', '--single',
+                       '-F', '-c',
+                       'exit_on_error=true', '-D',
+                       $node->data_dir, 'template0'
+               ],
+               '<',
+               $ENV{builddir} . '/sepgsql.sql');
+       ok($result, 'sepgsql installation script');
+}
+
+$node->append_conf('postgresql.conf', 'shared_preload_libraries=sepgsql');
+$node->start;
+
+my @tests = qw(label dml ddl alter misc);
+
+# Check if the truncate permission exists in the loaded policy, and if so,
+# run the truncate test
+#
+# Testing the TRUNCATE regression test can be done by manually adding
+# the permission with CIL if necessary:
+#     sudo semodule -cE base
+#     sudo sed -i -E 's/(class db_table.*?) \)/\1 truncate\)/' base.cil
+#     sudo semodule -i base.cil
+push @tests, 'truncate' if -f '/sys/fs/selinux/class/db_table/perms/truncate';
+
+$node->command_ok(
+       [
+               $ENV{PG_REGRESS}, '--bindir=', '--inputdir=.', '--launcher',
+               './launcher', @tests
+       ],
+       'sepgsql tests');
+
+done_testing();
diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml
index d1042e02228..9aebd9f3bed 100644
--- a/doc/src/sgml/regress.sgml
+++ b/doc/src/sgml/regress.sgml
@@ -284,6 +284,17 @@ <title>Additional Test Suites</title>
      </listitem>
     </varlistentry>
 
+    <varlistentry>
+     <term><literal>sepgsql</literal></term>
+     <listitem>
+      <para>
+       Runs the test suite under <filename>contrib/sepgsql</filename>.  This
+       requires an SELinux environment that is set up in a specific way; see
+       <xref linkend="sepgsql-regression"/>.
+      </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry>
      <term><literal>ssl</literal></term>
      <listitem>
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
index 1b848f1977c..a21e2d86279 100644
--- a/doc/src/sgml/sepgsql.sgml
+++ b/doc/src/sgml/sepgsql.sgml
@@ -151,6 +151,8 @@ <title>Installation</title>
  <sect2 id="sepgsql-regression">
   <title>Regression Tests</title>
 
+  <!-- FIXME: update this section -->
+
   <para>
    Due to the nature of <productname>SELinux</productname>, running the
    regression tests for <filename>sepgsql</filename> requires several extra
diff --git a/meson.build b/meson.build
index 1c0579d5a6b..49e61b33960 100644
--- a/meson.build
+++ b/meson.build
@@ -3375,6 +3375,7 @@ foreach test_dir : tests
       # Add temporary install, the build directory for non-installed binaries 
and
       # also test/ for non-installed test binaries built separately.
       env = test_env
+      env.set('builddir', test_dir['bd'])
       env.prepend('PATH', temp_install_bindir, test_dir['bd'], test_dir['bd'] 
/ 'test')
 
       foreach name, value : t.get('env', {})
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index a00c909681e..9f87cae879b 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -477,6 +477,7 @@ echo "# +++ tap check in $(subdir) +++" && \
 rm -rf '$(CURDIR)'/tmp_check && \
 $(MKDIR_P) '$(CURDIR)'/tmp_check && \
 cd $(srcdir) && \
+   builddir='$(CURDIR)' \
    TESTLOGDIR='$(CURDIR)/tmp_check/log' \
    TESTDATADIR='$(CURDIR)/tmp_check' \
    $(with_temp_install) \

base-commit: 3ca43dbbb67fbfb96dec8de2e268b96790555148
-- 
2.44.0

Reply via email to