Skip to content

Security Model

Pigsty trust boundaries and defense in depth, with the admin node as a high-trust control plane and a path from the default baseline to production hardening.
Pigsty trust boundaries and defense in depth, with the admin node as a high-trust control plane and a path from the default baseline to production hardening.

Identify the root of trust and defensive layers before selecting security features. The root of trust needs the strongest protection. Multiple layers preserve some protection if one fails.


Trust Boundaries

Pigsty uses Ansible for declarative deployment. Its administration node is the control plane and needs the strongest protection.

RoleAssets and Privileges
Admin nodeThe pigsty.yml inventory, which normally contains system and application credentials; the CA private key; SSH administration access to every node
INFRA nodesMonitoring and alerts, DNS, Nginx ingress, and software repositories
Database nodesDatabase instances, local dbsu, and restricted sudo
ClientsDatabase credentials or client certificates; access through service ports, HBA, and authentication

These roles have different capabilities, not a simple hierarchy. Protect these three assets in particular:

  1. The pigsty.yml inventory contains component passwords and credentials. Strictly restrict administration node access and access to any Git configuration repository.
  2. The CA private key, files/pki/ca/ca.key, anchors deployment trust. Its holder can issue any trusted certificate. The file uses mode 0600 inside a 0700 directory. Keep an offline backup.
  3. The administration user’s SSH private key provides passwordless sudo access to all enrolled nodes. It effectively grants root access throughout the managed deployment.

Pigsty’s security policy defines this trust boundary. Attacks requiring administration node access, or both pigsty.yml and the CA private key, are not product vulnerabilities. These control-plane assets have high trust by design. Protect them accordingly.


Seven Defensive Layers

Defense in depth combines controls so that one failure does not remove all protection. Pigsty provides seven security layers:

#LayerMechanismsDetails
1Network boundaryFirewall zones, constrained listen addresses, centralized ingressThis page
2Transport encryptionLocal CA and TLS between componentsEncrypted Communication
3AuthenticationHBA rules, SCRAM passwords, client certificatesAuthentication
4Access controlRole model, default privileges, database isolationAccess Control
5Host securitySELinux, restricted sudo, dedicated OS usersThis page
6Data securityChecksums, backup and encryption, PITR, deletion safeguardsData Security
7Audit trailDDL and connection logs, audit extensions, centralized logsData Security

Layers 2, 3, 4, 6, and 7 have dedicated chapters. The following sections cover the network and host layers.

Network Boundaries

Node provisioning enables firewalld or ufw, depending on the OS. node_firewall_mode defaults to zone. node_firewall_intranet places 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 in the trusted zone. Public networks can reach only node_firewall_public_port ports. Defaults are 22 for SSH and 80/443 for web traffic.

The demo pigsty.yml also exposes 5432 for local evaluation. Remove this exception in production. If direct database access is needed, restrict sources to explicit CIDRs through security groups, host firewalls, and HBA.

PostgreSQL listens on all addresses by default: pg_listen is 0.0.0.0. Listen addresses, firewall rules, and HBA jointly control access. For stricter access, constrain the listener:

pg_listen: '${ip},${vip},${lo}'   # Host IP, cluster VIP, and loopback only

The default firewall blocks direct public access to Grafana, VictoriaMetrics, and other web infrastructure. External web traffic normally uses the Nginx portal. Database traffic uses HAProxy service ports. Fewer entry points simplify hardening and auditing.

Host Security

Give each OS user only the privileges its job requires.

  • The database superuser postgres (pg_dbsu) has no password by default. It can enter the database only through local ident. pg_dbsu_sudo defaults to limit. This permits passwordless database service systemctl operations and log viewing, without unrestricted root access.
  • Operators and playbooks use node_admin_username, default dba. It receives passwordless sudo (nopass) by default. For stricter control, set node_admin_sudo to all to require a password, or limit to restrict commands.
  • node_selinux_mode defaults to permissive. SELinux records violations without blocking them, providing a baseline before enforcing.

Pigsty does not manage SSH server configuration. Put password-login restrictions, remote-root restrictions, and similar OS hardening in your host security baseline.


Hardening Levels

Harden the deployment in stages. Each level adds to the previous level:

Level 1: default baseline. Defaults provide SCRAM passwords, data checksums, a local CA, component certificates, layered HBA, four-tier roles, backups, and firewall zones. This suits development, testing, and evaluation on a trusted intranet. For production, review credentials and network boundaries, and configure client verification.

Level 2: randomized credentials. Change all public default passwords before any network-exposed deployment. Add -g to randomize recognized built-in parameters and example credentials:

./configure -g    # --generate: randomize recognized default credentials

This option does not replace pgBackRest cipher_pass, all Silo examples in ha/safe, or custom values. See the Default Credentials Checklist for coverage.

Level 3: policy hardening with ha/safe. conf/ha/safe.yml combines controls for further customization:

  • TLS and certificates: Main TCP HBA rules use ssl. Public administrator access requires a client certificate. PgBouncer uses require, and Patroni API uses HTTPS. Local ident and selected localhost password rules remain.
  • Password policy: The template preloads passwordcheck and sets expire_in for built-in users. Review and replace example passwords.
  • Reduced exposure: Listen addresses are limited to ${ip},${vip},${lo}. Public connection-pool access is explicitly denied for monitoring and administration accounts.
  • Backup encryption: pgBackRest uses the remote minio preset with AES-256-CBC. Replace the predictable example pgBR.${pg_cluster}.
  • Security extensions: The template installs passwordcheck, credcheck, pgaudit, pgsodium, anonymizer, and related extensions. Installation does not preload, create, or configure them.

Level 4: database hardening with crit.yml. The safe template selects CRIT for workloads that prioritize consistency. Compared with oltp, CRIT:

  • Requires checksums regardless of pg_checksum.
  • Enables strict synchronous replication (synchronous_mode_strict). Writes needing synchronous acknowledgment block if no synchronous replica is available.
  • Logs connections and disconnections. PostgreSQL 18 separates connection receipt, authentication, and authorization.
  • Sets watchdog to automatic. It activates only if a usable device exists.

Strict synchronous mode aims to preserve acknowledged transactions. It depends on synchronous_commit, synchronous replica state, and failover eligibility. Verify RPO with failure exercises on the target topology.

You can select individual controls instead of the whole template:

pg-meta:
  hosts:
    10.10.10.10: { pg_seq: 1 , pg_role: primary }
    10.10.10.11: { pg_seq: 2 , pg_role: replica }
    10.10.10.12: { pg_seq: 3 , pg_role: replica }
  vars:
    pg_cluster: pg-meta
    pg_conf: crit.yml                    # Use the CRIT database parameter template
    patroni_ssl_enabled: true            # Enable HTTPS for the Patroni API
    pgbouncer_sslmode: require           # Require TLS for PgBouncer
    pg_listen: '${ip},${vip},${lo}'      # Constrain listen addresses
    pg_libs: '$libdir/passwordcheck, pg_stat_statements, auto_explain'  # Password strength checks

Next