A complete, production-tested DISA STIG hardening guide for PostgreSQL 16 on Oracle Linux 8. Covers installation, SSL/TLS, pgaudit, systemd hardening, and SIEM forwarding — with every OL8-specific pitfall documented.
Air-gapped / disconnected network? See README-airgapped.md.
- DISA STIG baseline — PostgreSQL 9.x STIG V2R4 controls applied to PostgreSQL 16; OS STIG assumed at install time
- SSL/TLS enforced end-to-end — TLS 1.2 minimum, FIPS 140-2 approved cipher suites, client certificate CA configured
- pgaudit structured auditing — write, DDL, role, and function events logged with relation names and bound parameters
- Dual log destination — syslog → SIEM via TLS rsyslog, plus local CSV for independent audit trail
- pg_hba.conf zero-trust —
hostssl+ SCRAM-SHA-256 for all remote connections; plain TCP rejected; local socket peer-only - initdb data checksums — block-level corruption detection enabled at database initialisation
- systemd hardening drop-in — capabilities cleared, dangerous syscall groups blocked, core dumps disabled; OL8/systemd-239 compatible
- Post-install SQL hardening — public schema CREATE revoked, template database access restricted, default privileges locked down
- Documented pitfalls — every non-obvious OL8/PG16 failure mode explained and solved
Application Server(s)
│ TCP 5432 (TLS 1.2+, SCRAM-SHA-256)
▼
PostgreSQL 16
├── /var/lib/pgsql/data/ — PGDATA [lv_pgdata on vg_pgdata]
│ ├── postgresql.conf — main config (connections, SSL, pgaudit, logging)
│ └── pg_wal → /pg_wal_volume/ — WAL symlink [lv_pgwal on vg_pgdata]
├── /etc/postgresql/
│ ├── pg_hba.conf — authentication rules (hostssl + scram-sha-256)
│ └── pg_ident.conf — OS-to-DB user identity map
└── /etc/systemd/system/
└── postgresql.service.d/
└── hardening.conf — systemd drop-in (capabilities, syscall filter)
│
├── /pg_log_volume/pg_log/ — local CSV audit trail [lv_pglogs on vg_pglogs]
│
└── syslog (facility LOCAL0)
│ TLS 6514 (production/CUI) / UDP 514 (lab only)
▼
SIEM
repository/
├── config/
│ ├── postgresql.conf # Main daemon configuration
│ ├── pg_hba.conf # Host-based authentication rules
│ ├── postgresql-hardening.conf # systemd hardening drop-in
│ ├── setup.sql # Post-install SQL hardening script
│ └── rsyslog-postgresql.conf # rsyslog SIEM forwarding rule
├── scripts/
│ ├── New-SecurePassword.ps1 # Secure password generator (PowerShell)
│ ├── new-secure-password.sh # Secure password generator (Bash)
│ └── new-secure-password.py # Secure password generator (Python 3.6+)
├── README.md # This guide (internet-connected deployment)
└── README-airgapped.md # Air-gapped deployment variant
| File | Install path |
|---|---|
config/postgresql.conf |
/var/lib/pgsql/data/postgresql.conf |
config/pg_hba.conf |
/etc/postgresql/pg_hba.conf |
config/postgresql-hardening.conf |
/etc/systemd/system/postgresql.service.d/hardening.conf |
config/setup.sql |
run from any writable directory, not installed |
config/rsyslog-postgresql.conf |
/etc/rsyslog.d/postgresql.conf |
scripts/New-SecurePassword.ps1 |
helper script — not installed on server |
scripts/new-secure-password.sh |
helper script — not installed on server |
scripts/new-secure-password.py |
helper script — not installed on server |
Oracle Linux 8 is assumed to be DISA STIG hardened at install time using the STIG profile available in the OL8 installer. No additional OS hardening steps are required here.
rsysloginstalled and running- Outbound access to
yum.oracle.comduring installation - A TLS certificate and private key for the PostgreSQL server (RSA 2048+ or ECDSA P-256+; MD5 certificates will fail under OL8 FIPS mode)
- A log volume mounted at
/pg_log_volumewith adequate capacity for audit logs
Separate physical disks and LVM logical volumes prevent one workload from starving another. A WAL storm cannot fill PGDATA; a pgaudit log flood cannot affect database writes.
Physical Disks
├── /dev/sda ← OS disk (existing)
├── /dev/sdb ← PostgreSQL data + WAL (fast NVMe/SSD, e.g. 500 GB)
└── /dev/sdc ← Audit log disk (standard SSD, e.g. 200 GB)
Volume Groups / Logical Volumes
├── vg_pgdata on /dev/sdb
│ ├── lv_pgdata ≥ 2× expected DB size → /var/lib/pgsql
│ └── lv_pgwal ≥ 4× max_wal_size (32 GB) → /pg_wal_volume
└── vg_pglogs on /dev/sdc
└── lv_pglogs 100 GB to start → /pg_log_volume
Provision and mount before deployment:
# Data + WAL disk
pvcreate /dev/sdb
vgcreate vg_pgdata /dev/sdb
lvcreate -L 100G -n lv_pgdata vg_pgdata
lvcreate -L 32G -n lv_pgwal vg_pgdata
mkfs.xfs -f -L pgdata /dev/vg_pgdata/lv_pgdata
mkfs.xfs -f -L pgwal /dev/vg_pgdata/lv_pgwal
# Log disk
pvcreate /dev/sdc
vgcreate vg_pglogs /dev/sdc
lvcreate -L 100G -n lv_pglogs vg_pglogs
mkfs.xfs -f -L pglogs /dev/vg_pglogs/lv_pglogs
# Mount points
mkdir -p /var/lib/pgsql /pg_wal_volume /pg_log_volumeAdd to /etc/fstab:
/dev/vg_pgdata/lv_pgdata /var/lib/pgsql xfs defaults,noatime,nosuid,nodev 0 2
/dev/vg_pgdata/lv_pgwal /pg_wal_volume xfs defaults,noatime,nosuid,nodev 0 2
/dev/vg_pglogs/lv_pglogs /pg_log_volume xfs defaults,noatime,nosuid,nodev,noexec 0 2
mount -a # verify all three mounts before continuingDo not set
noexecon/var/lib/pgsql/dataor/pg_wal_volume— PostgreSQL executes binaries from those paths.noexecon/pg_log_volumeis safe; only CSV/text files are written there.
SELinux file contexts: new mount points will not carry the correct SELinux labels by default. PostgreSQL running as postgresql_t will be denied access to files labelled with the generic default_t. Apply the correct contexts before starting the service:
semanage fcontext -a -t postgresql_db_t "/var/lib/pgsql(/.*)?"
semanage fcontext -a -t postgresql_db_t "/pg_wal_volume(/.*)?"
semanage fcontext -a -t postgresql_log_t "/pg_log_volume(/.*)?"
restorecon -Rv /var/lib/pgsql /pg_wal_volume /pg_log_volume# Enable the Oracle Linux 8 EPEL repository (if not already enabled)
dnf install oracle-epel-release-el8
# Enable the postgresql:16 module stream
dnf module enable postgresql:16
# Install PostgreSQL 16 server, contrib modules, and pgaudit
dnf install postgresql-server postgresql-contrib
dnf install pgauditThe package creates the postgres OS account. Confirm it is locked:
id postgres
passwd -l postgres
passwd -S postgres # must show: postgres L ...Create the dedicated log directory (or ensure the mount is in place):
install -d -o postgres -g postgres -m 0700 /pg_log_volume
install -d -o postgres -g postgres -m 0700 /pg_log_volume/pg_logIf
/pg_log_volumeis a separate mount, ensure it is mounted before the PostgreSQL service starts. Add it to/etc/fstaband verify withmount -a.
PostgreSQL's hba_file is overridden in postgresql.conf to /etc/postgresql/ to separate authentication policy from the data directory.
install -d -o root -g postgres -m 0750 /etc/postgresql
install -o root -g postgres -m 0640 \
config/pg_hba.conf /etc/postgresql/pg_hba.conf
# Create a minimal pg_ident.conf (required by the config reference)
install -o root -g postgres -m 0640 \
/dev/null /etc/postgresql/pg_ident.confEdit pg_hba.conf and replace <<APP_SUBNET>> with your actual application server CIDR(s).
# Enable data checksums and force UTF-8 encoding at initdb time
PGSETUP_INITDB_OPTIONS="--data-checksums --encoding=UTF8 --locale=en_US.UTF-8" \
postgresql-setup --initdb
initdbmay warn that/var/lib/pgsqlis a direct mountpoint. This is safe to ignore — PGDATA (/var/lib/pgsql/data) is a regular subdirectory inside the mount, not the mountpoint itself. Silencing the warning would require an extra intermediate directory and deviating from the OL8 package default PGDATA path, which is not worth the trade-off.
Verify checksums were enabled:
sudo -u postgres pg_controldata /var/lib/pgsql/data \
| grep 'Data page checksum'
# Expected: Data page checksum version: 1Move the pg_wal directory created by initdb to the dedicated WAL volume and replace it with a symlink. This must be done before the service is started for the first time.
# Move pg_wal to the dedicated volume
mv /var/lib/pgsql/data/pg_wal /pg_wal_volume/pg_wal
# Symlink back to the expected path
ln -s /pg_wal_volume/pg_wal /var/lib/pgsql/data/pg_wal
# Confirm ownership (initdb sets postgres:postgres; mv preserves it)
chown -R postgres:postgres /pg_wal_volume/pg_wal
chmod 0700 /pg_wal_volume/pg_wal
# Verify
ls -la /var/lib/pgsql/data/pg_wal
# Expected: lrwxrwxrwx ... /var/lib/pgsql/data/pg_wal -> /pg_wal_volume/pg_walinstall -o postgres -g postgres -m 0600 \
config/postgresql.conf /var/lib/pgsql/data/postgresql.confEdit the site-specific values before starting the service:
| Setting | What to set |
|---|---|
listen_addresses |
Server IP(s) — never '*' in production |
ssl_cert_file |
Path to the server TLS certificate |
ssl_key_file |
Path to the server TLS private key |
ssl_ca_file |
Path to the CA used to verify client certificates |
syslog_facility |
Must match rsyslog-postgresql.conf |
shared_buffers |
25 % of installed RAM |
effective_cache_size |
Approximate total RAM available for caching |
# Server private key — must match ssl_key_file in postgresql.conf
install -o root -g postgres -m 0640 \
server.key /etc/pki/tls/private/postgresql.key
# Server certificate — must match ssl_cert_file in postgresql.conf
install -o root -g postgres -m 0644 \
server.crt /etc/pki/tls/certs/postgresql.crt
# CA certificate for client verification — must match ssl_ca_file in postgresql.conf
install -o root -g postgres -m 0644 \
ca.crt /etc/pki/tls/certs/postgresql-ca.crt
# CRL — initial file from your CA (see auto-refresh setup below)
install -o root -g root -m 0644 \
postgresql-crl.pem /etc/pki/tls/certs/postgresql-crl.pemCRL auto-refresh: PostgreSQL has no native OCSP or CDP support — ssl_crl_file holds a static local file. Without automated refresh, revoked certificates stay trusted until the file is manually updated. Automate refresh with a systemd timer.
Create /etc/systemd/system/pg-crl-refresh.service:
[Unit]
Description=Refresh PostgreSQL CRL from CDP
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=root
# Get the CDP URL from your CA certificate:
# openssl x509 -in /etc/pki/tls/certs/postgresql.crt -noout -text | grep -A4 "CRL Distribution"
# Most enterprise CAs publish CRLs in DER format. Remove -inform DER if your CA publishes PEM.
ExecStart=/bin/bash -c '\
curl -fsSL --max-time 30 -o /tmp/pg-crl.der "<<CDP_URL>>" && \
openssl crl -inform DER -outform PEM \
-in /tmp/pg-crl.der \
-out /etc/pki/tls/certs/postgresql-crl.pem && \
rm -f /tmp/pg-crl.der'
ExecStartPost=/bin/systemctl reload postgresql
[Install]
WantedBy=multi-user.targetCreate /etc/systemd/system/pg-crl-refresh.timer:
[Unit]
Description=Daily PostgreSQL CRL refresh
[Timer]
OnCalendar=daily
RandomizedDelaySec=3600
Persistent=true
[Install]
WantedBy=timers.targetsystemctl daemon-reload
systemctl enable --now pg-crl-refresh.timer
# Verify the timer is scheduled:
systemctl list-timers pg-crl-refresh.timerReplace
<<CDP_URL>>with the CRL Distribution Point URL from your CA certificate. Refresh frequency should be shorter than the CRL'sNext Updatefield — daily is typical for enterprise PKIs.
FIPS mode note: On OL8 with FIPS enabled, certificates must use RSA 2048+ or ECDSA P-256+. MD5-signed certificates and SHA-1 key identifiers will cause connection failures. Verify with
openssl x509 -in server.crt -text -noout | grep -E 'Signature Algorithm|Public-Key'.
install -d -o root -g root -m 0755 \
/etc/systemd/system/postgresql.service.d
install -o root -g root -m 0644 \
config/postgresql-hardening.conf \
/etc/systemd/system/postgresql.service.d/hardening.conf
systemctl daemon-reloadsystemctl enable --now postgresql
systemctl status postgresql# Copy to a location the postgres user can read (e.g. /tmp)
install -o postgres -g postgres -m 0400 config/setup.sql /tmp/setup.sql
sudo -u postgres psql -f /tmp/setup.sql
rm -f /tmp/setup.sqlReview the verification output at the end of the script and confirm:
pgaudit.logincludeswrite,ddl,role,functionsslison,ssl_min_protocol_versionisTLSv1.2password_encryptionisscram-sha-256log_connectionsandlog_disconnectionsareon- The
publicschema ACL does not include=C/postgres(CREATE for PUBLIC)
For each new application database, repeat the extension and privilege steps:
\c myappdb
CREATE EXTENSION IF NOT EXISTS pgaudit;
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
ALTER DEFAULT PRIVILEGES REVOKE ALL ON TABLES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES REVOKE ALL ON SEQUENCES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES REVOKE ALL ON FUNCTIONS FROM PUBLIC;Generate a strong password, then create the application user and database. Three equivalent password generators are included in this repository — use whichever matches your environment:
# Bash (Linux)
./scripts/new-secure-password.sh -p postgresql
# Python (any platform with Python 3.6+)
python3 scripts/new-secure-password.py -p postgresql
# PowerShell (Windows / cross-platform)
.\scripts\New-SecurePassword.ps1 -Profile PostgreSQLAll three use cryptographic randomness with rejection sampling, ensuring uniform character distribution and at least one character from each required class. Use --help / -h for full options (custom length, bulk generation, exclude ambiguous characters, etc.).
Create the user and database:
# Create a role with an interactive password prompt (paste the generated password)
sudo -u postgres createuser -P myappuser
# Create the database owned by the new role
sudo -u postgres createdb -E UNICODE -O myappuser myappdbThen apply the per-database hardening from Step 9:
sudo -u postgres psql -d myappdb -c "
CREATE EXTENSION IF NOT EXISTS pgaudit;
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
ALTER DEFAULT PRIVILEGES REVOKE ALL ON TABLES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES REVOKE ALL ON SEQUENCES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES REVOKE ALL ON FUNCTIONS FROM PUBLIC;
"Add a corresponding
hostsslline inpg_hba.conffor the new database and user, thensystemctl reload postgresql.
install -o root -g root -m 0644 \
config/rsyslog-postgresql.conf /etc/rsyslog.d/postgresql.confEdit /etc/rsyslog.d/postgresql.conf and replace siem.example.mil with your SIEM's hostname or IP.
systemctl restart rsyslog
# Verify delivery:
logger -p local0.info "PostgreSQL rsyslog test $(date)"Remove all default services and allow only what this host requires:
# Identify the active zone (commonly 'public' on a freshly installed host)
firewall-cmd --get-active-zones
# Remove every pre-configured service from the zone
# Replace 'public' if your active zone differs
for svc in $(firewall-cmd --zone=public --list-services); do
firewall-cmd --permanent --zone=public --remove-service="$svc"
done
# Allow SSH (adjust or remove if management is via jump host or out-of-band)
firewall-cmd --permanent --zone=public --add-service=ssh
# Allow PostgreSQL connections from application subnets only
# Replace with your actual subnet
firewall-cmd --permanent --zone=public \
--add-rich-rule='rule family="ipv4" source address="<<APP_SUBNET>>/24" port port="5432" protocol="tcp" accept'
# Reload and verify
firewall-cmd --reload
firewall-cmd --zone=public --list-allRun these immediately after first start and after every configuration change.
# 1. Confirm service is running and no errors in the journal
systemctl status postgresql
journalctl -u postgresql --no-pager | tail -20
# 2. Confirm data checksums are enabled
sudo -u postgres psql -c "SHOW data_checksums;"
# Expected: on
# 3. Confirm SSL is enabled in configuration
sudo -u postgres psql -c "SHOW ssl;"
# Expected: on
# Note: "SHOW ssl" confirms the server accepts SSL connections.
# Local socket connections (sudo -u postgres psql) will always show ssl=f
# in pg_stat_ssl — this is normal. SSL is verified over TCP in test 8 below.
# 4. Confirm pgaudit is loaded and logging
sudo -u postgres psql -c "SHOW shared_preload_libraries;"
# Expected: pgaudit,pg_stat_statements
sudo -u postgres psql -c "SELECT name, setting FROM pg_settings WHERE name LIKE 'pgaudit%';"
# 5. Confirm SCRAM-SHA-256 is enforced
sudo -u postgres psql -c "SHOW password_encryption;"
# Expected: scram-sha-256
# 6. Confirm idle-in-transaction timeout is active
sudo -u postgres psql -c "SHOW idle_in_transaction_session_timeout;"
# Expected: 5min (or your configured value)
# 7. Attempt a plain TCP connection (must be rejected)
psql -h <<SERVER_IP>> -U postgres postgres
# Expected: connection rejected (hostssl rules require SSL; plain host is rejected)
# 8. Verify SSL connection with client certificate
psql "host=<<SERVER_IP>> dbname=postgres user=<<USER>> \
sslmode=verify-full sslcert=client.crt sslkey=client.key sslrootcert=root.crt" \
-c "SELECT current_user, inet_client_addr(), ssl FROM pg_stat_ssl WHERE pid = pg_backend_pid();"
# 9. Confirm audit events appear in syslog
journalctl -u postgresql --no-pager | grep -i pgaudit | tail -10| Task | Frequency |
|---|---|
| Rotate TLS certificates before expiry | Per certificate validity period |
Verify pg-crl-refresh.timer is active; confirm CRL Next Update has not expired: openssl crl -in /etc/pki/tls/certs/postgresql-crl.pem -noout -text | grep -E 'Last Update|Next Update' |
Per CRL validity period |
Review pg_hba.conf against current network topology |
On every network change |
Check Oracle EPEL for updated postgresql-server and pgaudit packages |
Monthly |
| Review pgaudit logs for anomalous patterns | Weekly (or via SIEM alerting) |
Verify log rotation and available space on /pg_log_volume and /pg_wal_volume |
Monthly |
| Run smoke tests after any configuration change | After every change |
| Review and rotate application database passwords | Per site password policy |
| Verify data checksum status after major upgrades | After each upgrade |
These issues are documented here to prevent recurrence during deployment.
--data-checksums cannot be enabled after initdb. If the cluster is initialised without this flag, it must be rebuilt. Enforce it via PGSETUP_INITDB_OPTIONS as shown in Step 3. Verify immediately after initdb:
sudo -u postgres pg_controldata /var/lib/pgsql/data | grep checksumpostgresql.conf overrides hba_file to /etc/postgresql/pg_hba.conf. If that directory does not exist when PostgreSQL starts, it emits a misleading error about an invalid configuration parameter. Create the directory (Step 2) before the first service start.
host allows both SSL and non-SSL TCP connections. Only hostssl enforces SSL. A pg_hba.conf that uses host with scram-sha-256 still permits unencrypted connections, violating V-233517. Every remote rule must use hostssl.
ssl_ciphers in postgresql.conf controls only TLS 1.2 cipher suites. TLS 1.3 cipher selection is governed entirely by the system OpenSSL FIPS policy (/etc/crypto-policies/). On OL8 with FIPS enabled, OpenSSL automatically restricts to FIPS-approved TLS 1.3 suites regardless of ssl_ciphers.
pgaudit is not a runtime-loadable extension. Loading it via LOAD 'pgaudit' in a session or in session_preload_libraries provides only session-level audit and does not satisfy STIG V-233535. It must be listed in shared_preload_libraries and the service restarted.
shared_preload_libraries loads the pgaudit module globally, but CREATE EXTENSION pgaudit must be run in every database where object-level auditing (pgaudit.log_relation) is needed. Running setup.sql covers the default (postgres) database; repeat in each application database.
Setting log_destination = 'csvlog' without logging_collector = on silently produces no log files. Both settings are required together. syslog in log_destination works independently of logging_collector.
Failed at step NAMESPACE: No such file or directory is thrown when any path in ReadWritePaths= or ReadOnlyPaths= does not exist when the mount namespace is assembled. The error never names the offending path.
If you add a ReadWritePaths= directive to the drop-in, list all three PostgreSQL mount points or the service will refuse to start:
ReadWritePaths=/var/lib/pgsql /pg_wal_volume /pg_log_volume-
Never set
ProtectSystem=stricton OL8. Under systemd 239 with SELinux enforcing,strictprevents the kernel from executing binaries under/usr/bin/. The stock unit'sProtectSystem=fullis the safe maximum. -
Never set
MemoryDenyWriteExecute=truewith JIT enabled. PostgreSQL JIT allocatesmmap(PROT_EXEC)regions. If JIT is confirmed disabled (jit=offinpostgresql.conf), this can be set totrue. Default in the supplied drop-in:false. -
List-type keys accumulate across drop-ins. To replace
CapabilityBoundingSet=orSystemCallFilter=, issue an empty assignment first to clear the inherited value, then set the desired value. The drop-in follows this pattern. -
ProtectHostname=,ProtectClock=,RestrictSUIDSGID=,ProtectKernelLogs=require systemd 240+/245+/253+. Using them on OL8 causes silent parse failures. They are not included in the supplied drop-in. -
Many hardening directives implicitly force
NoNewPrivileges=true, which breaks SELinux domain transitions on OL8.SystemCallFilter=,RestrictNamespaces=,LockPersonality=,RestrictRealtime=,ProtectKernelTunables=,ProtectKernelModules=,MemoryDenyWriteExecute=, andPrivateDevices=all force NNP at kernel level — settingNoNewPrivileges=falsehas no effect. On systemd 239, this blocks theinit_t → postgresql_ttransition (AVC: denied { nnp_transition }), causing postgres to run asinit_tand be denied access to allpostgresql_db_tfiles. The supplied drop-in excludes all NNP-triggering directives. SELinux mandatory access control provides equivalent containment for most of what those directives protect against.
New LVM mount points (/pg_wal_volume, /pg_log_volume, or a non-default PGDATA mount) inherit the default_t SELinux type. PostgreSQL running as postgresql_t is denied access to default_t files. Run semanage fcontext and restorecon as shown in the storage layout section before the first service start. If the service fails with Permission denied on a file that has correct Unix ownership, check ausearch -m AVC -ts recent — the denial will name the source and target SELinux contexts.
On OL8 with FIPS enabled (fips-mode-setup --enable):
- Server certificates must use RSA 2048+ or ECDSA P-256+. RSA 1024 and MD5-signed certificates are rejected at connection time.
- SHA-1 is not permitted for certificate signatures.
gpgmay emitout of core handler ignored in FIPS mode— this is a harmless warning.- The
scram-sha-256authentication method is FIPS-compliant.md5password hashing is not; never use it.
ssl_crl_file is not deprecated — STIG V-233520 requires it. The constraint is that PostgreSQL has no built-in OCSP checking and does not auto-fetch CRLs from the CDP URL embedded in certificates. OpenSSL's server-side TLS verification does not follow CDP URLs either. The file is a static snapshot: a revoked certificate stays trusted until the file is manually replaced.
The pg-crl-refresh timer installed in Step 6 mitigates this. If the timer is disabled or the CDP URL changes, new connections will silently accept revoked certificates. Monitor:
# Timer status
systemctl status pg-crl-refresh.timer
# CRL expiry — Next Update must be in the future
openssl crl -in /etc/pki/tls/certs/postgresql-crl.pem -noout -text \
| grep -E 'Last Update|Next Update'MIT — free to use, modify, and distribute.
Przemysław Pradela
Built through real production deployment and iterative troubleshooting on Oracle Linux 8.
Contributions and issue reports welcome.