Or, “Getting Your PostgreSQL Ready to Wield the Power of Custom Code!”
Welcome back, fellow code-slingers and database defenders! In Part 1: The Guardians of the Gate of The Epic Quest for Secure Passwords: A PostgreSQL & PCI DSS Saga, we unmasked the formidable PCI DSS v4.0.1 password rules. We learned why our digital fortresses need more than just a flimsy “password123,” and why pgtle is our weapon of choice for this grand endeavor.
(Just need the quick cheatsheet for setting up your pgtle environment? Your Quick-Reference: Part 2 – Environment Setup awaits!)
If you’re here to conquer the entire saga, a true hero’s journey, then make sure to check out our Series Overview Page for all the other exciting chapters and quick-reference guides.
Now, before we dive into writing the clever code that will enforce those pesky (but crucial!) PCI DSS rules, we need to do some serious groundwork. Think of it like preparing your starship for warp speed, or gathering your party before venturing forth into a dangerous dungeon. We need to get pgtle installed and ensure our PostgreSQL database is ready to accept its new superpowers. This PostgreSQL pgtle setup guide will walk you through the entire pgtle installation process.
pgtle: Your New Database Sidekick (A Quick Recap)
Just a quick reminder: pgtle (PostgreSQL Trusted Language Extensions) isn’t some obscure, unsupported alien tech. It’s a first-party extension from AWS (yes, those cloud folks, but this one’s for vanilla PostgreSQL, which is why we love it!), allowing you to write custom PostgreSQL extensions using common languages like PL/pgSQL, JavaScript (via PL/v8), and even Python. This means we can integrate powerful, customized logic directly into our database, avoiding external services or unsupported shenanigans. It’s like having a miniature, programmable R2-D2 living inside your database server!
The Installation Ritual: Summoning pgtle
Getting pgtle installed is surprisingly straightforward, especially compared to some of the more arcane rituals you might perform as a DBA. The official documentation over on GitHub (https://github.com/aws/pg_tle/blob/main/docs/01_install.md) and AWS (https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/PostgreSQL_trusted_language_extension.html) provides the precise spellbook. Here’s the simplified, blog-friendly version with all the essential commands for your vanilla PostgreSQL environment and pgtle installation on Ubuntu Server 24.04 LTS.
Note: The following pgtle installation steps are demonstrated on Ubuntu Server 24.04 LTS. Commands may vary slightly for other Linux distributions.
Prerequisites: Gathering Your Tools
Before we begin the pgtle installation, ensure your system has the following prerequisites. Think of these as the essential tools in your digital toolkit for a successful PostgreSQL pgtle setup:
- PostgreSQL 11 or later:
pgtlerequires a PostgreSQL instance version 11 or newer to function. (We’ll cover PostgreSQL installation in a later post, but ensure it’s present for these steps). git: Essential for cloning thepg_tlesource code repository from GitHub.- Installation (Ubuntu 24.04):
sudo apt update sudo apt install git
- Installation (Ubuntu 24.04):
- PostgreSQL Development Files: These files (headers and libraries) are crucial for compiling extensions against your specific PostgreSQL version. They provide
pg_configand other necessary components.- Installation (Ubuntu 24.04): The version number in
postgresql-server-dev-16must match your installed PostgreSQL major version. For this demonstration, I am using PostgreSQL 16.sudo apt update sudo apt install postgresql-server-dev-16
- Installation (Ubuntu 24.04): The version number in
pg_config: This utility is installed as part of the PostgreSQL development files and is used by thepg_tlebuild system to locate PostgreSQL’s installation directories.
Step 1: Get the pgtle Bits (Download & Compile)
For most self-hosted PostgreSQL installations, you’ll compile pgtle from source. Don’t panic! It’s usually a standard make && sudo make install dance after you’ve got the necessary PostgreSQL development headers installed. (It’s less complicated than fixing the hyperdrive motivator, promise!) This step is crucial for a successful pgtle installation.
First, you’ll need to clone the pg_tle repository from GitHub and navigate into its directory. Make sure you have git installed.
git clone https://github.com/aws/pg_tle.git
cd pg_tle
Next, you need to compile pgtle. For details on installing the necessary PostgreSQL development packages, please refer to the Prerequisites section above.
# Compile the extension
make
# Install the extension into your PostgreSQL installation directory
sudo make install
- A Note for Managed Services: If you’re running PostgreSQL on a managed service that supports
pgtle(like AWS RDS or Aurora PostgreSQL, which conveniently already haspgtlepre-installed for you), this entire “Get thepgtleBits” step is often skipped. You just move to the next step.
Step 2: The shared_preload_libraries Incantation (Configure PostgreSQL)
This is the critical step to ensure PostgreSQL actually loads pgtle when it starts up. Think of shared_preload_libraries as PostgreSQL’s “boot-up checklist” for essential modules. This is a key part of your PostgreSQL pgtle setup.
You’ll need to modify your postgresql.conf file (the main configuration file for your PostgreSQL instance). The location of this file can vary depending on your operating system and PostgreSQL installation method (e.g., /etc/postgresql/XX/main/postgresql.conf on Debian/Ubuntu, or in your PostgreSQL data directory).
Open postgresql.conf with your favorite text editor (e.g., nano, vim, or gedit):
vi /etc/postgresql/16/main/postgresql.conf
Find the shared_preload_libraries parameter and add pgtle to it. If there are already other libraries listed, simply add pgtle, before or after them, separated by commas.
# Before:
# shared_preload_libraries = 'pg_stat_statements,pgaudit'
# After
shared_preload_libraries = 'pg_stat_statements,pgaudit,pg_tle'
Crucial Step: Restart Your PostgreSQL Server! After modifying postgresql.conf, you must restart your PostgreSQL server. This isn’t like hitting refresh on your browser; it’s more like rebooting the Millennium Falcon to apply a system update.
#For Debian based systems
sudo pg_ctlcluster 16 main restart
# Example for systemd-based systems (most Linux distributions)
sudo systemctl restart postgresql
- Verify the status after restart to ensure it came up cleanly:
sudo systemctl status postgresql
Step 3: Create the Extension (Within Your Database)
Once PostgreSQL is back online and has preloaded pgtle, the final step is to create the extension within your specific database. This finalizes the pgtle installation within your chosen database.
Connect to your database as a superuser:
psql
Once connected to the psql prompt, run this simple SQL command:
CREATE EXTENSION pgtle;
If successful, you should see a confirmation message. You can verify it’s installed by running:
postgres=# \dx
List of installed extensions
Name | Version | Schema | Description
---------+---------+------------|--------------------------------------------
pg_tle | 1.5.0 | pgtle | Trusted Language Extensions for PostgreSQL
plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
(2 rows)
You should see pgtle listed among the installed extensions.
And voilà! pgtle is now installed and ready to roll in your PostgreSQL environment. This completes the core PostgreSQL pgtle setup. It’s standing by, waiting for its marching orders to help us enforce those stringent PCI DSS rules.
Preparing for Hooks: Where the Magic Begins
Now that pgtle is installed, let’s talk about pgtle hooks and how they function as the interception points for our custom security logic. The pgtle documentation on hooks (https://github.com/aws/pg_tle/blob/main/docs/04_hooks.md) shows us the true power of this extension.
pgtle hooks are essentially specific points in PostgreSQL’s internal processes where pgtle allows us to “hook into” and execute our custom code. For our PCI DSS password quest, we’ll be particularly interested in pgtle hooks that trigger during user creation or password changes. It’s like setting up tripwires in your digital perimeter: when a specific event happens (like a password being set), our custom code gets to inspect it.
Deep Dive into pgtle Hooks and Their Configuration
Let’s break down the two main pgtle hook types that pgtle provides, along with the crucial postgresql.conf pgtle parameters that control their behavior. This pgtle configuration is vital for tailoring pgtle to your security needs.
1. Password Check Hook (passcheck)
This hook is your primary line of defense for enforcing password policies.
- What it does: Allows you to intercept attempts to set or change a user’s password. Your custom
pgtlefunction (written in PL/pgSQL, PL/v8, etc.) will then validate that password against your defined criteria (e.g., length, complexity, history). - When it’s triggered: Whenever a user executes
ALTER USER ... PASSWORDorCREATE USER ... PASSWORD. - How it works: Your
pgtlefunction will receive the username and the proposed new password (and other details). It performs its checks and either allows the password (by returning successfully) or raises an error (which prevents the password change). The specificpgtlefunction that gets called is registered in the database usingpgtle.register_feature()(which we’ll cover in a later part).
Function Definition for passcheck hook:
Your custom passcheck function must accept the following arguments:
passcheck_hook(
username TEXT, -- The name of the role setting the password.
password TEXT, -- The password itself (plaintext or hashed).
password_type pgtle.password_types, -- The format of the 'password' argument. Can be:
-- - PASSWORD_TYPE_PLAINTEXT: The password is in plain text.
-- - PASSWORD_TYPE_MD5: The password is MD5 hashed.
-- - PASSWORD_TYPE_SCRAM_SHA_256: The password is SCRAM-SHA-256 hashed.
valid_until TIMESTAMPTZ, -- If set, the time until the password on the account no longer works.
valid_null BOOLEAN -- If true, 'valid_until' is set to NULL.
) RETURNS VOID -- Your function should return VOID. Raise an error to reject the password.
Configuration Parameters for passcheck Hook (in postgresql.conf):
pgtle.enable_password_check- Default:
off - What it controls: Determines if the
passcheckhook mechanism is active.off: Disables thepasscheckhook entirely. This is the default setting.on: Enables thepasscheckhook, but only calls your custom function if one is registered (viapgtle.register_featurewith the feature name'pgtle.password_check_functions').require: Requires apasscheckhook function to be defined and registered. If no function is found, password changes will be rejected. Use with caution!
- Context:
SIGHUP(meaning you can change this value and apply it without a full database restart by reloading the configuration, e.g.,pg_ctl reloadorsudo systemctl reload postgresql).
- Default:
pgtle.passcheck_db_name(Available inpgtleversions 1.3.0 and higher)- Default:
""(empty string) - What it controls: If set to a specific database name,
pgtlewill query that database for the registeredpasscheckfunction, regardless of which database the client is connected to. This is useful for centralizing your password validation logic. - Warning: If this parameter is set,
passcheckfunctions are executed as a superuser! Define your functions very carefully and be aware of potential security risks from malicious code. - Context:
SIGHUP
- Default:
2. Client Authentication Hook (clientauth)
This hook provides powerful control over incoming connection attempts.
- What it does: Allows you to intercept client connection attempts before PostgreSQL’s standard authentication methods (like
pg_hba.confrules) are evaluated. This gives you a very early gatekeeper role for custom authentication, rate limiting, or connection filtering. - When it’s triggered: Every time a client tries to connect to the PostgreSQL server.
- How it works: Your
pgtlefunction receives details about the incoming connection. It can then return aTEXTmessage (which will be sent back to the client as an error, rejecting the connection) orVOID(which allows the connection to proceed to standard authentication). The specificpgtlefunction that gets called is registered in the database usingpgtle.register_feature()(which we’ll cover in a later part).
Function Definition for clientauth hook:
Your custom clientauth function must accept the following arguments and return either TEXT</code class="pgsql"> or VOID:
clientauth_hook(
port pgtle.clientauth_port_subset, -- An object containing a subset of Port details:
-- - noblock (BOOLEAN)
-- - remote_host (TEXT)
-- - remote_hostname (TEXT)
-- - remote_hostname_resolv (INTEGER)
-- - remote_hostname_errcode (INTEGER)
-- - database_name (TEXT)
-- - user_name (TEXT)
-- - application_name (TEXT)
status INTEGER -- Connection status code:
-- - 0: Successful connection (so far)
-- - -1: Connection error
) RETURNS TEXT OR VOID -- Return TEXT to reject with a message, or VOID to allow.
Configuration Parameters for clientauth Hook (in postgresql.conf):
pgtle.enable_clientauth- Default:
off - What it controls: Determines if the
clientauthhook mechanism is active.off: Disables theclientauthhook. This is the default.on: Enables theclientauthhook, but only calls your custom function if one is registered (viapgtle.register_featurewith the feature name'pgtle.client_authentication_functions').require: Requires aclientauthhook function to be defined and registered. Warning: connections will be rejected if no functions are registered to this hook!
- Context:
SIGHUP. Important Note: A full database restart is typically needed to enable theclientauthfeature (switching fromofftoonorrequire), as it involves registering background workers. Disabling it (e.g.,ontooff) doesn’t require a restart, but restarting is still recommended to free up worker resources.
- Default:
pgtle.clientauth_db_name- Default:
postgres - What it controls: Specifies which database to query for the registered
clientauthfunctions. Allclientauthfunctions should be created and registered in this central database. When a client connects to any database in the cluster, the functions in this specified database will be executed. - Context:
Postmaster(requires a full database restart to apply changes).
- Default:
pgtle.clientauth_num_parallel_workers- Default:
1 - Minimum:
1 - Maximum:
min(max_connections, 256) - What it controls: The number of background workers
pgtlewill use to handleclient_authenticationrequests in parallel. Increase this value to handle large connection bursts or if yourclientauthfunctions are expected to run for longer. Ensure this value is less than PostgreSQL’smax_worker_processesto leave room for other processes. - Context:
Postmaster(requires a full database restart to apply changes).
- Default:
pgtle.clientauth_users_to_skip- Default:
""(empty string) - What it controls: A comma-separated list of usernames that will bypass the
clientauthhook. If a connecting user is on this list,clientauthfunctions will not be executed for their connection. Useful for administrative users or specific applications. - Context:
SIGHUP
- Default:
pgtle.clientauth_databases_to_skip- Default:
""(empty string) - What it controls: A comma-separated list of database names that will bypass the
clientauthhook. If a client connects to a database on this list,clientauthfunctions will not be executed. - Context:
SIGHUP
- Default:
Understanding these detailed parameters gives you comprehensive control over your pgtle environment, allowing you to secure it further and precisely dictate how its powerful hooks behave. For our password validation journey, the primary focus will be on the password_check hook and how we register our custom functions to it.
Are you ready to truly make your PostgreSQL database a fortress? The next part will be a deep dive into the code that will validate your passwords with the vigilance of a Jedi Master! Stay tuned!




