Skip to content

Commit 8051eba

Browse files
committed
Turn off PostgreSQL 'trust' authentication
When trust authentication [1] is specified, PostgreSQL assumes that anyone who can connect to the server is authorized to access the database with whatever database user name they specify (even superuser). Since this action is intended to be used on CI, this is unlikely a desired behaviour. First, all credentials are known and must be specified in order to avoid flakes. Second, most commonly folks around there want to test that secrets are gathered and passed down to the database server correctly. This patch turns off 'trust' authentication for the PostgreSQL server. [1] https://www.postgresql.org/docs/15/auth-trust.html Resolves #5
1 parent 3973215 commit 8051eba

File tree

2 files changed

+81
-11
lines changed

2 files changed

+81
-11
lines changed

action.yml

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,26 +42,53 @@ runs:
4242
- name: Setup and start PostgreSQL
4343
run: |
4444
export PGDATA="$RUNNER_TEMP/pgdata"
45-
pg_ctl init --options="--encoding=UTF-8 --locale=en_US.UTF-8"
45+
export PWFILE="$RUNNER_TEMP/pwfile"
4646
47-
# Forbid creating unix sockets since they are created by default in the
48-
# directory we don't have permissions to.
49-
echo "unix_socket_directories = ''" >> "$PGDATA/postgresql.conf"
50-
echo "port = ${{ inputs.port }}" >> "$PGDATA/postgresql.conf"
47+
find / -name "pg_hba.conf" -exec cat {} \;
48+
49+
# Save user password on disk so we can create a new PostgreSQL database
50+
# cluster. Password must be set because we're turning 'trust'
51+
# authentication off, and there's no other option since this script is
52+
# not executed interactively.
53+
echo '${{ inputs.password }}' > $PWFILE
54+
55+
# There are couple of reasons why we need to create a new PostgreSQL
56+
# database cluster:
57+
#
58+
# * TODO
59+
# --auth="scram-sha-256" \
60+
initdb \
61+
--username="${{ inputs.username }}" \
62+
--pwfile="$PWFILE" \
63+
--encoding="UTF-8" \
64+
--locale="en_US.UTF-8"
65+
66+
cat <<EOF > "$PGDATA/postgresql.conf"
67+
port = ${{ inputs.port }}
68+
EOF
69+
70+
# Client authentication is controlled by a host-based authentication
71+
# configuration file. By default local connections are trusted and do
72+
# not require password (in fact, passwords are ignored). It's undesired
73+
# behaviour in our case since in case of CI we want to make sure that
74+
# the password is configured and passed properly by the application
75+
# under test.
76+
# cat <<EOF > "$PGDATA/pg_hba.conf"
77+
# host all all all scram-sha-256
78+
# EOF
5179
pg_ctl start
5280
5381
# Both PGHOST and PGUSER are used by PostgreSQL tooling such as 'psql'
54-
# or 'createuser'. Since PostgreSQL data has been resetup, we cannot
82+
# or 'createuser'. Since PostgreSQL data has been recreated, we cannot
5583
# rely on defaults anymore.
5684
#
5785
# PGHOST is required for Linux and macOS since they default to unix
5886
# sockets, and we have turned them off.
5987
#
60-
# PGUSER is required for Windows since default the tooling's default
61-
# user is 'postgres', while 'pg_ctl init' creates one with the name of
62-
# the current user.
88+
# PGUSER is required for Windows since default tooling user is
89+
# 'postgres', while 'pg_ctl init' creates one with the name of the
90+
# current user.
6391
echo "PGHOST=localhost" >> $GITHUB_ENV
64-
echo "PGUSER=${USER:-$USERNAME}" >> $GITHUB_ENV
6592
echo "PGPORT=${{ inputs.port }}" >> $GITHUB_ENV
6693
shell: bash
6794

test_action.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import typing as t
21
import os
2+
import typing as t
3+
import urllib.parse
34

45
import psycopg
56
import pytest
@@ -91,3 +92,45 @@ def test_user_create_drop_database(connection: psycopg.Connection):
9192
database_name = "foobar42"
9293
connection.execute(f"CREATE DATABASE {database_name}")
9394
connection.execute(f"DROP DATABASE {database_name}")
95+
96+
97+
def test_auth_wrong_username(connection_factory: t.Callable[[str], psycopg.Connection]):
98+
"""Test that wrong username is rejected!"""
99+
100+
connection_uri = os.getenv("CONNECTION_URI")
101+
connection_parts = urllib.parse.urlparse(connection_uri)
102+
103+
# Despite of exposting username/password of network location, poor Python
104+
# doesn't allow their modification. Thus we have to have these dances in
105+
# order to change the password in the connection URI.
106+
userinfo, _, hostinfo = connection_parts.netloc.rpartition("@")
107+
_ , _, password = userinfo.rpartition(":")
108+
connection_parts = connection_parts._replace(netloc=f"bruteforce:{password}@{hostinfo}")
109+
connection_uri = urllib.parse.urlunparse(connection_parts)
110+
111+
with pytest.raises(psycopg.OperationalError) as excinfo:
112+
test_user_create_insert_select(connection_factory(connection_uri))
113+
assert str(excinfo.value) == (
114+
f'connection failed: FATAL: password authentication failed for user "bruteforce"'
115+
)
116+
117+
118+
def test_auth_wrong_password(connection_factory: t.Callable[[str], psycopg.Connection]):
119+
"""Test that wrong password is rejected!"""
120+
121+
connection_uri = os.getenv("CONNECTION_URI")
122+
connection_parts = urllib.parse.urlparse(connection_uri)
123+
124+
# Despite of exposting username/password of network location, poor Python
125+
# doesn't allow their modification. Thus we have to have these dances in
126+
# order to change the password in the connection URI.
127+
userinfo, _, hostinfo = connection_parts.netloc.rpartition("@")
128+
username, _, _ = userinfo.rpartition(":")
129+
connection_parts = connection_parts._replace(netloc=f"{username}:bruteforce@{hostinfo}")
130+
connection_uri = urllib.parse.urlunparse(connection_parts)
131+
132+
with pytest.raises(psycopg.OperationalError) as excinfo:
133+
test_user_create_insert_select(connection_factory(connection_uri))
134+
assert str(excinfo.value) == (
135+
f'connection failed: FATAL: password authentication failed for user "{username}"'
136+
)

0 commit comments

Comments
 (0)