From 514693f716aede9d5bfccea94eaac5c2494f82c2 Mon Sep 17 00:00:00 2001 From: Thomas Munro Date: Sat, 31 Dec 2022 14:41:57 +1300 Subject: [PATCH v1 1/5] Add simple test for RADIUS authentication. This is similar to the existing tests for other authentication methods. It requires FreeRADIUS to be installed, and opens ports that may be considered insecure, so users have to opt in with PG_EXTRA_TESTS=radius. Reviewed-by: Michael Paquier Discussion: https://postgr.es/m/CA%2BhUKGKxNoVjkMCksnj6z3BwiS3y2v6LN6z7_CisLK%2Brv%2B0V4g%40mail.gmail.com --- doc/src/sgml/regress.sgml | 11 ++ src/test/authentication/README | 12 ++ src/test/authentication/meson.build | 1 + src/test/authentication/t/007_radius.pl | 185 ++++++++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 src/test/authentication/t/007_radius.pl diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index d1042e02228..b9dc28b8dea 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -284,6 +284,17 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' + + radius + + + Runs the test src/test/authentication/t/007_radius.pl. + This requires a FreeRADIUS installation and + opens UDP listen sockets. + + + + ssl diff --git a/src/test/authentication/README b/src/test/authentication/README index 602280a0713..13d7a7f4d84 100644 --- a/src/test/authentication/README +++ b/src/test/authentication/README @@ -26,3 +26,15 @@ Either way, this test initializes, starts, and stops a test Postgres cluster. See src/test/perl/README for more info about running these tests. + +Requirements +============ + +The RADIUS test is skipped unless "radius" is listed in PG_TEXT_EXTRA, because +it requires a FreeRADIUS installation and opens extra ports. FreeRADIUS can be +installed with: + +Debian: apt-get install freeradius +Homebrew: brew install freeradius-server +FreeBSD: pkg install freeradius3 +MacPorts: port install freeradius diff --git a/src/test/authentication/meson.build b/src/test/authentication/meson.build index 8f5688dcc13..59da3e64169 100644 --- a/src/test/authentication/meson.build +++ b/src/test/authentication/meson.build @@ -12,6 +12,7 @@ tests += { 't/004_file_inclusion.pl', 't/005_sspi.pl', 't/006_login_trigger.pl', + 't/007_radius.pl', ], }, } diff --git a/src/test/authentication/t/007_radius.pl b/src/test/authentication/t/007_radius.pl new file mode 100644 index 00000000000..ebfdf7b36e3 --- /dev/null +++ b/src/test/authentication/t/007_radius.pl @@ -0,0 +1,185 @@ + +# Copyright (c) 2021-2024, PostgreSQL Global Development Group + +use strict; +use warnings; +use File::Copy; +use PostgreSQL::Test::Utils; +use PostgreSQL::Test::Cluster; +use Test::More; + +my $radiusd_dir = "${PostgreSQL::Test::Utils::tmp_check}/radiusd_data"; +my $radiusd_conf = "radiusd.conf"; +my $radiusd_users = "users.txt"; +my $radiusd_prefix; +my $radiusd; + +if ($ENV{PG_TEST_EXTRA} !~ /\bradius\b/) +{ + plan skip_all => 'radius not enabled in PG_TEST_EXTRA'; +} +elsif ($^O eq 'freebsd') +{ + $radiusd = '/usr/local/sbin/radiusd'; +} +elsif ($^O eq 'linux' && -f '/usr/sbin/freeradius') +{ + $radiusd = '/usr/sbin/freeradius'; +} +elsif ($^O eq 'linux') +{ + $radiusd = '/usr/sbin/radiusd'; +} +elsif ($^O eq 'darwin' && -d '/opt/local') +{ + # typical path for MacPorts + $radiusd = '/opt/local/sbin/radiusd'; + $radiusd_prefix = '/opt/local'; +} +elsif ($^O eq 'darwin' && -d '/opt/homebrew') +{ + # typical path for Homebrew on ARM + $radiusd = '/opt/homebrew/bin/radiusd'; + $radiusd_prefix = '/opt/homebrew'; +} +elsif ($^O eq 'darwin' && -d '/usr/local') +{ + # typical path for Homebrew on Intel + $radiusd = '/usr/local/bin/radiusd'; + $radiusd_prefix = '/usr/local'; +} +else +{ + plan skip_all => + "radius tests not supported on $^O or dependencies not installed"; +} + +note "setting up radiusd"; + +my $radius_port = PostgreSQL::Test::Cluster::get_free_port(); + +mkdir $radiusd_dir or die "cannot create $radiusd_dir"; + +append_to_file("$radiusd_dir/$radiusd_users", + qq{test2 Cleartext-Password := "password2"}); + +my $conf = qq{ +client default { + ipaddr = "127.0.0.1" + secret = "shared-secret" +} + +modules { + files { + filename = "$radiusd_dir/users.txt" + } + pap { + } +} + +server default { + listen { + type = "auth" + ipv4addr = "127.0.0.1" + port = "$radius_port" + } + authenticate { + Auth-Type PAP { + pap + } + } + authorize { + files + pap + } +} + +log { + destination = "files" + localstatedir = "$radiusd_dir" + logdir = "$radiusd_dir" + file = "$radiusd_dir/radius.log" +} + +security { + require_message_authenticator = "yes" +} + +pidfile = "$radiusd_dir/radiusd.pid" +}; + +# Note that require_message_authenticator defaulted to "no" before 3.2.5, and +# then switched to "auto" (a new mode that fills the logs up with warning +# messages about clients that don't send MA), and presumably a later version +# will default to "yes". + +if ($radiusd_prefix) +{ + $conf .= "prefix=\"$radiusd_prefix\"\n"; +} + +append_to_file("$radiusd_dir/$radiusd_conf", $conf); + +system_or_bail $radiusd, '-xx', '-d', $radiusd_dir; + +END +{ + kill 'INT', `cat $radiusd_dir/radiusd.pid` + if -f "$radiusd_dir/radiusd.pid"; +} + +note "setting up PostgreSQL instance"; + +my $node = PostgreSQL::Test::Cluster->new('node'); +$node->init; +$node->append_conf('postgresql.conf', "log_connections = on\n"); +$node->start; + +$node->safe_psql('postgres', 'CREATE USER test1;'); +$node->safe_psql('postgres', 'CREATE USER test2;'); + +unlink($node->data_dir . '/pg_hba.conf'); +$node->append_conf( + 'pg_hba.conf', + qq{ +local all test2 radius radiusservers="127.0.0.1" radiussecrets="shared-secret" radiusports="$radius_port" +} +); +$node->restart; + +note "running tests"; + +sub test_access +{ + local $Test::Builder::Level = $Test::Builder::Level + 1; + + my ($node, $role, $expected_res, $test_name, %params) = @_; + my $connstr = "user=$role"; + + if ($expected_res eq 0) + { + $node->connect_ok($connstr, $test_name, %params); + } + else + { + # No checks of the error message, only the status code. + $node->connect_fails($connstr, $test_name, %params); + } +} + +$ENV{"PGPASSWORD"} = 'wrong'; +test_access( + $node, 'test1', 2, + 'authentication fails if user not found in RADIUS', + log_unlike => [qr/connection authenticated:/]); +test_access( + $node, 'test2', 2, + 'authentication fails with wrong password', + log_unlike => [qr/connection authenticated:/]); + +$ENV{"PGPASSWORD"} = 'password2'; +test_access($node, 'test2', 0, 'authentication succeeds with right password', + log_like => + [qr/connection authenticated: identity="test2" method=radius/],); + +done_testing(); -- 2.39.3 (Apple Git-146)