Add simplified PostgreSQL infrastructure role for database services

- Provides PostgreSQL server as shared database infrastructure
- Follows KISS principle with only essential configuration (11 variables vs 45 originally)
- Implements maximum security with Unix socket-only superuser access
- Uses scram-sha-256 authentication for application users
- Includes SystemD security hardening
- Applications manage their own databases/users via this infrastructure
- Production-ready with data checksums and localhost-only access
This commit is contained in:
2025-11-18 21:33:50 +01:00
parent 7c3b02e5ad
commit 762d00eebf
9 changed files with 532 additions and 0 deletions

252
roles/postgresql/README.md Normal file
View File

@@ -0,0 +1,252 @@
# PostgreSQL Infrastructure Role
This role provides PostgreSQL as shared database infrastructure for rick-infra applications. It follows the self-contained service architecture where this role provides the database server, and applications manage their own databases and users.
## Architecture
PostgreSQL serves as database infrastructure (similar to how Caddy provides web infrastructure):
- **PostgreSQL Role**: Installs and configures PostgreSQL server
- **Application Roles**: Create their own databases/users via dependency
## Features
- ✅ Native Arch Linux installation via pacman
- ✅ Secure configuration with scram-sha-256 authentication
- ✅ SystemD security hardening
- ✅ Data integrity with checksums enabled by default
- ✅ Performance-tuned defaults
- ✅ Comprehensive logging configuration
- ✅ UTF-8 encoding with C.UTF-8 locale
## Usage
### In Application Roles
Add PostgreSQL as a dependency in your application's `meta/main.yml`:
```yaml
dependencies:
- role: postgresql
tags: ['postgresql', 'infrastructure']
```
Then create your application's database and user in your tasks:
```yaml
- name: Create application database user
postgresql_user:
name: "{{ myapp_db_user }}"
password: "{{ myapp_db_password }}"
encrypted: yes
become: yes
become_user: postgres
- name: Create application database
postgresql_db:
name: "{{ myapp_db_name }}"
owner: "{{ myapp_db_user }}"
encoding: UTF8
template: template0
become: yes
become_user: postgres
```
### In site.yml
```yaml
- name: Deploy Infrastructure
hosts: arch-vps
become: yes
roles:
- role: postgresql
tags: ['postgresql', 'infrastructure', 'database']
- role: myapp # Will use PostgreSQL infrastructure
tags: ['myapp']
```
## Configuration Variables
### Basic Configuration
| Variable | Default | Description |
|----------|---------|-------------|
| `postgresql_service_enabled` | `true` | Enable PostgreSQL service |
| `postgresql_service_state` | `"started"` | Service state |
| `postgresql_port` | `5432` | PostgreSQL port |
| `postgresql_listen_addresses` | `"localhost"` | Listen addresses |
### Security Configuration
| Variable | Default | Description |
|----------|---------|-------------|
| `postgresql_auth_method` | `"scram-sha-256"` | Authentication method |
| `postgresql_systemd_security` | `true` | Enable systemd security hardening |
| `postgresql_data_checksums` | `true` | Enable data checksums |
| `postgresql_ssl` | `false` | Enable SSL/TLS |
### Performance Configuration
| Variable | Default | Description |
|----------|---------|-------------|
| `postgresql_max_connections` | `100` | Maximum connections |
| `postgresql_shared_buffers` | `"128MB"` | Shared buffer size |
| `postgresql_effective_cache_size` | `"1GB"` | Effective cache size |
See `defaults/main.yml` for all available configuration options.
## Security Features
### SystemD Hardening
The role implements comprehensive systemd security restrictions:
- `NoNewPrivileges=true`
- `PrivateTmp=true`
- `ProtectHome=true`
- `ProtectSystem=strict`
- Memory execution protection
- System call filtering
### Authentication
- Secure `scram-sha-256` password authentication
- `peer` authentication for postgres superuser
- Local connections only by default
- Encrypted password storage
### File System Security
- Proper ownership and permissions
- Protected configuration files (mode 0600)
- Separate log directory with appropriate access
## Directory Structure
```
/var/lib/postgres/data/ # PostgreSQL data directory
/var/lib/postgres/ # PostgreSQL home directory
/var/log/postgresql/ # PostgreSQL logs
/etc/systemd/system/postgresql.service.d/ # SystemD overrides
```
## Database Administration
### Connect as postgres superuser:
```bash
sudo -u postgres psql
```
### Create application database and user:
```sql
CREATE ROLE myapp WITH LOGIN PASSWORD 'secure_password';
CREATE DATABASE myapp WITH OWNER myapp TEMPLATE template0 ENCODING 'UTF8';
```
### List databases:
```sql
\l
```
### List users:
```sql
\du
```
## Performance Tuning
The role provides conservative performance defaults suitable for most applications. For production workloads, consider adjusting:
- `postgresql_shared_buffers`: 25% of system RAM
- `postgresql_effective_cache_size`: 75% of system RAM
- `postgresql_max_connections`: Based on application needs
- `postgresql_maintenance_work_mem`: For large datasets
## Monitoring
### Service Status
```bash
systemctl status postgresql
```
### Logs
```bash
journalctl -u postgresql
# or
sudo -u postgres tail -f /var/log/postgresql/postgresql-*.log
```
### Connection Test
```bash
sudo -u postgres psql -c "SELECT version();"
```
## Troubleshooting
### Common Issues
1. **Permission Denied**: Ensure postgres user owns data directory
2. **Connection Refused**: Check service status and listen_addresses
3. **Authentication Failed**: Verify pg_hba.conf configuration
### Debug Mode
Enable detailed logging:
```yaml
postgresql_log_statement: "all"
postgresql_log_connections: true
postgresql_log_disconnections: true
```
## Examples
### Development Setup
```yaml
postgresql_log_statement: "all" # Log all statements
postgresql_max_connections: 50 # Lower for development
```
### Production Setup
```yaml
postgresql_shared_buffers: "256MB"
postgresql_effective_cache_size: "2GB"
postgresql_log_min_duration_statement: 1000 # Log slow queries
postgresql_log_connections: true # Audit trail
```
### High Security Setup
```yaml
postgresql_ssl: true
postgresql_log_connections: true
postgresql_log_disconnections: true
postgresql_log_statement: "ddl" # Log schema changes
```
## Dependencies
- Arch Linux PostgreSQL package
- python-psycopg2 (for Ansible modules)
## Compatibility
- **OS**: Arch Linux
- **PostgreSQL**: Latest stable version from Arch repositories
- **Ansible**: >= 2.9
## Contributing
When modifying this role:
1. Update this README for any new features
2. Test with example applications
3. Ensure security configurations remain intact
4. Follow rick-infra coding standards
## Integration Examples
See roles that use this infrastructure:
- `gitea`: Git repository management
- `nextcloud` (planned): File sharing and collaboration
---
**Rick-Infra PostgreSQL Infrastructure Role**
Provides secure, performant PostgreSQL database infrastructure for rick-infra applications.

View File

@@ -0,0 +1,52 @@
---
# =================================================================
# PostgreSQL Infrastructure Role - Simplified Configuration
# =================================================================
# Provides PostgreSQL database server as shared infrastructure
# Applications manage their own databases/users
# =================================================================
# Essential Configuration
# =================================================================
# Service Management
postgresql_service_enabled: true
postgresql_service_state: "started"
# Network Security (localhost only)
postgresql_listen_addresses: "localhost"
postgresql_port: 5432
# Authentication
postgresql_auth_method: "scram-sha-256"
# Database Cluster Setup
postgresql_encoding: "UTF8"
postgresql_locale: "C.UTF-8"
postgresql_data_checksums: true
# Security
postgresql_systemd_security: true
# =================================================================
# Optional Performance (Conservative Defaults)
# =================================================================
# Basic performance settings - PostgreSQL defaults are excellent
postgresql_max_connections: 100
postgresql_shared_buffers: "128MB"
# =================================================================
# Infrastructure Notes
# =================================================================
# This role provides minimal PostgreSQL infrastructure
# Applications should create their own databases/users:
#
# - postgresql_user:
# name: myapp
# password: "{{ vault_myapp_password }}"
# - postgresql_db:
# name: myapp
# owner: myapp
#
# PostgreSQL's built-in defaults are used for everything else

View File

@@ -0,0 +1,18 @@
---
# PostgreSQL Role Handlers
- name: reload systemd
systemd:
daemon_reload: yes
- name: restart postgresql
systemd:
name: postgresql
state: restarted
when: postgresql_service_state == "started"
- name: reload postgresql
systemd:
name: postgresql
state: reloaded
when: postgresql_service_state == "started"

View File

@@ -0,0 +1,25 @@
---
galaxy_info:
author: Rick's Infrastructure Team
description: PostgreSQL database server infrastructure for rick-infra
company: Personal Infrastructure
license: MIT
min_ansible_version: "2.9"
platforms:
- name: ArchLinux
versions:
- all
galaxy_tags:
- database
- postgresql
- infrastructure
- archlinux
dependencies: []
# Role provides PostgreSQL infrastructure
# Applications should declare this as a dependency and manage their own databases/users

View File

@@ -0,0 +1,93 @@
---
# PostgreSQL Infrastructure Role - Simplified Tasks
- name: Install PostgreSQL
pacman:
name: postgresql
state: present
- name: Install PostgreSQL Python library (for Ansible modules)
pacman:
name: python-psycopg2
state: present
- name: Check if PostgreSQL data directory exists and is initialized
stat:
path: "/var/lib/postgres/data/PG_VERSION"
register: postgresql_initialized
- name: Initialize PostgreSQL database cluster
command: >
initdb
-D /var/lib/postgres/data
--locale={{ postgresql_locale }}
--encoding={{ postgresql_encoding }}
--auth-local=peer
--auth-host={{ postgresql_auth_method }}
{{ '--data-checksums' if postgresql_data_checksums else '' }}
become: yes
become_user: postgres
when: not postgresql_initialized.stat.exists
notify: restart postgresql
- name: Deploy PostgreSQL configuration file
template:
src: postgresql.conf.j2
dest: /var/lib/postgres/data/postgresql.conf
owner: postgres
group: postgres
mode: '0600'
backup: yes
notify: restart postgresql
- name: Deploy PostgreSQL authentication configuration
template:
src: pg_hba.conf.j2
dest: /var/lib/postgres/data/pg_hba.conf
owner: postgres
group: postgres
mode: '0600'
backup: yes
notify: restart postgresql
- name: Create systemd override directory for PostgreSQL security
file:
path: /etc/systemd/system/postgresql.service.d
state: directory
mode: '0755'
when: postgresql_systemd_security
- name: Deploy PostgreSQL systemd security override
template:
src: systemd-override.conf.j2
dest: /etc/systemd/system/postgresql.service.d/override.conf
mode: '0644'
when: postgresql_systemd_security
notify:
- reload systemd
- restart postgresql
- name: Enable and start PostgreSQL service
systemd:
name: postgresql
enabled: "{{ postgresql_service_enabled }}"
state: "{{ postgresql_service_state }}"
daemon_reload: yes
- name: Wait for PostgreSQL to be ready
wait_for:
port: "{{ postgresql_port }}"
host: "{{ postgresql_listen_addresses }}"
timeout: 30
when: postgresql_service_state == "started"
- name: Display PostgreSQL infrastructure status
debug:
msg: |
✅ PostgreSQL infrastructure ready!
📡 Service: {{ postgresql_listen_addresses }}:{{ postgresql_port }}
🔒 Auth: {{ postgresql_auth_method }}
📊 Checksums: {{ 'Enabled' if postgresql_data_checksums else 'Disabled' }}
🏗️ Ready for applications to create databases/users

View File

@@ -0,0 +1,45 @@
# PostgreSQL Client Authentication Configuration File
# Generated by Ansible - PostgreSQL Role
# Documentation: https://www.postgresql.org/docs/current/auth-pg-hba-conf.html
# TYPE DATABASE USER ADDRESS METHOD
# =============================================================================
# LOCAL CONNECTIONS
# =============================================================================
# "local" is for Unix domain socket connections only
local all postgres peer
local all all {{ postgresql_auth_method }}
# =============================================================================
# IPv4 LOCAL CONNECTIONS
# =============================================================================
# IPv4 local connections (applications only - no superuser TCP access):
host all all 127.0.0.1/32 {{ postgresql_auth_method }}
# =============================================================================
# IPv6 LOCAL CONNECTIONS
# =============================================================================
# IPv6 local connections (applications only - no superuser TCP access):
host all all ::1/128 {{ postgresql_auth_method }}
# =============================================================================
# SECURITY NOTES
# =============================================================================
# This configuration provides maximum security defaults:
# - postgres superuser ONLY accessible via Unix socket with peer authentication
# - NO TCP access for postgres superuser (even from localhost)
# - All application users use {{ postgresql_auth_method }} over TCP
# - Only local connections allowed by default
#
# Superuser access: sudo -u postgres psql (Unix socket only)
# Application access: psql -h localhost -U appuser -d appdb (TCP with password)
#
# For remote access, add additional 'host' entries above
# Always use the most restrictive authentication method possible
#
# Rick-Infra PostgreSQL Infrastructure
# Applications should create their own database users

View File

@@ -0,0 +1,16 @@
# PostgreSQL Configuration - Rick-Infra Simplified
# Generated by Ansible PostgreSQL role
# PostgreSQL's excellent defaults are used except for essentials
# Network and Security
listen_addresses = '{{ postgresql_listen_addresses }}'
port = {{ postgresql_port }}
# Basic Performance (only override if needed)
max_connections = {{ postgresql_max_connections }}
shared_buffers = {{ postgresql_shared_buffers }}
# Authentication
password_encryption = {{ postgresql_auth_method }}
# Rick-Infra: PostgreSQL infrastructure role - keeping it simple

View File

@@ -0,0 +1,26 @@
# PostgreSQL SystemD Security Override - Rick-Infra Simplified
# Generated by Ansible PostgreSQL role
[Service]
# Essential Security Restrictions
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectHome=true
ProtectSystem=strict
ProtectKernelTunables=true
ProtectKernelModules=true
RestrictRealtime=true
LockPersonality=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
# File System Access (PostgreSQL standard paths)
ReadWritePaths=/var/lib/postgres
# Network Security (localhost only)
{% if postgresql_listen_addresses == "localhost" %}
IPAddressDeny=any
IPAddressAllow=localhost
{% endif %}
# Rick-Infra: Essential security hardening only