Skip to content

Commit

Permalink
Add SSL support
Browse files Browse the repository at this point in the history
There are two cases when PostgreSQL with SSL enabled may come handy. The
first one is when PostgreSQL driver developers want to test SSL support
in their drivers. The second one is when you come to depend on some
self-signed certificates and you want to test your application
end-to-end, i.e. with certificates being used.

Co-authored-by: Ihor Kalnytskyi <[email protected]>
Signed-off-by: Ihor Kalnytskyi <[email protected]>
Signed-off-by: chandr-andr (Kiselev Aleksandr) <[email protected]>
  • Loading branch information
chandr-andr and ikalnytskyi committed Aug 18, 2024
1 parent 50da8e8 commit 5462685
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 16 deletions.
46 changes: 30 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ jobs:
matrix:
os:
- ubuntu-20.04
- ubuntu-22.04
- ubuntu-24.04
- macos-12
- macos-13
- macos-14
- windows-2019
- windows-2022
# - ubuntu-22.04
# - ubuntu-24.04
# - macos-12
# - macos-13
# - macos-14
# - windows-2019
# - windows-2022
steps:
- uses: actions/checkout@v4

Expand All @@ -45,29 +45,35 @@ jobs:
env:
CONNECTION_URI: ${{ steps.postgres.outputs.connection-uri }}
SERVICE_NAME: ${{ steps.postgres.outputs.service-name }}
CERTIFICATE_PATH: ${{ steps.postgres.outputs.certificate-path }}
EXPECTED_CONNECTION_URI: postgresql://postgres:postgres@localhost:5432/postgres
EXPECTED_SERVICE_NAME: postgres
EXPECTED_SERVER_VERSION: "16"
EXPECTED_SSL: false

parametrized:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-20.04
- ubuntu-22.04
- ubuntu-24.04
- macos-12
- macos-13
- macos-14
- windows-2019
# - ubuntu-20.04
# - ubuntu-22.04
# - ubuntu-24.04
# - macos-12
# - macos-13
# - macos-14
# - windows-2019
- windows-2022
postgres-version:
- "14"
# - "14"
- "15"
steps:
- uses: actions/checkout@v4

- uses: mxschmitt/action-tmate@v3
with:
limit-access-to-actor: false

- name: Run setup-postgres
uses: ./
with:
Expand All @@ -76,20 +82,28 @@ jobs:
database: jedi_order
port: 34837
postgres-version: ${{ matrix.postgres-version }}
ssl: true
id: postgres

- name: Run setup-python
uses: actions/setup-python@v5
with:
python-version: "3.10"

- run: |
echo "${{ steps.postgres.outputs.connection-uri }}"
echo "${{ steps.postgres.outputs.service-name }}"
echo "${{ steps.postgres.outputs.certificate-path }}"
- name: Run tests
run: |
python3 -m pip install --upgrade pip pytest psycopg furl
python3 -m pytest -vv test_action.py
env:
CONNECTION_URI: ${{ steps.postgres.outputs.connection-uri }}
SERVICE_NAME: ${{ steps.postgres.outputs.service-name }}
EXPECTED_CONNECTION_URI: postgresql://yoda:GrandMaster@localhost:34837/jedi_order
CERTIFICATE_PATH: ${{ steps.postgres.outputs.certificate-path }}
EXPECTED_CONNECTION_URI: postgresql://yoda:GrandMaster@localhost:34837/jedi_order?sslmode=verify-ca&sslrootcert=${{ steps.postgres.outputs.certificate-path }}
EXPECTED_SERVICE_NAME: yoda
EXPECTED_SERVER_VERSION: ${{ matrix.postgres-version }}
EXPECTED_SSL: true
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ steps:
database: test
port: 34837
postgres-version: "14"
ssl: "on"
id: postgres

- run: pytest -vv tests/
Expand Down
25 changes: 25 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ inputs:
postgres-version:
description: The PostgreSQL major version to install. Either "14", "15", or "16".
default: "16"
ssl:
description: When "true", encrypt connections using SSL (TLS).
default: "false"
required: false
outputs:
connection-uri:
Expand All @@ -32,6 +35,9 @@ outputs:
service-name:
description: The service name with connection parameters.
value: ${{ steps.set-outputs.outputs.service-name }}
certificate-path:
description: The path to the server certificate if SSL is on.
value: ${{ steps.set-outputs.outputs.certificate-path }}
runs:
using: composite
steps:
Expand Down Expand Up @@ -132,6 +138,14 @@ runs:
# directory we have no permissions to (owned by system postgres user).
echo "unix_socket_directories = ''" >> "$PGDATA/postgresql.conf"
echo "port = ${{ inputs.port }}" >> "$PGDATA/postgresql.conf"
if [ "${{ inputs.ssl }}" = "true" ]; then
openssl req -new -x509 -days 365 -nodes -text -out "$PGDATA/server.crt" \
-keyout "$PGDATA/server.key" -subj '/CN=localhost'
chmod og-rwx "$PGDATA/server.key" "$PGDATA/server.crt"
echo "ssl = on" >> "$PGDATA/postgresql.conf"
fi
pg_ctl start --pgdata="$PGDATA"
# Save required connection parameters for created superuser to the
Expand Down Expand Up @@ -173,6 +187,17 @@ runs:
- name: Set action outputs
run: |
CONNECTION_URI="postgresql://${{ inputs.username }}:${{ inputs.password }}@localhost:${{ inputs.port }}/${{ inputs.database }}"
CERTIFICATE_PATH="$RUNNER_TEMP/pgdata/server.crt"
if [ "${{ inputs.ssl }}" = "true" ]; then
# Although SSLMODE and SSLROOTCERT are specific to libpq options,
# most third-party drivers also support them. By default libpq
# prefers SSL but doesn't require it, thus it's important to set
# these options to ensure SSL is used and the certificate is
# verified.
CONNECTION_URI="$CONNECTION_URI?sslmode=verify-ca&sslrootcert=$CERTIFICATE_PATH"
echo "certificate-path=$CERTIFICATE_PATH" >> $GITHUB_OUTPUT
fi
echo "connection-uri=$CONNECTION_URI" >> $GITHUB_OUTPUT
echo "service-name=${{ inputs.username }}" >> $GITHUB_OUTPUT
Expand Down
21 changes: 21 additions & 0 deletions test_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,20 @@ def test_service_name(service_name: str):
assert service_name == os.getenv("EXPECTED_SERVICE_NAME")


def test_certificate_path():
"""Test that CERTIFICATE_PATH points to the certificate."""

certificate_path = os.getenv("CERTIFICATE_PATH")

if os.getenv("EXPECTED_SSL") == "true":
assert certificate_path
certificate_text = pathlib.Path(certificate_path).read_text()
assert "Subject: CN = localhost" in certificate_text
assert "-----BEGIN CERTIFICATE-----" in certificate_text
else:
assert not certificate_path


def test_server_encoding(connection: psycopg.Connection):
"""Test that PostgreSQL's encoding matches the one we passed to initdb."""

Expand Down Expand Up @@ -147,6 +161,13 @@ def test_server_version(connection: psycopg.Connection):
assert server_version.split(".")[0] == os.getenv("EXPECTED_SERVER_VERSION")


def test_server_ssl(connection: psycopg.Connection):
"""Test that connection is SSL encrypted."""

expected = os.getenv("EXPECTED_SSL") == "true"
assert connection.info.pgconn.ssl_in_use is expected


def test_user_permissions(connection: psycopg.Connection):
"""Test that a user has super/createdb permissions."""

Expand Down

0 comments on commit 5462685

Please sign in to comment.