P
dbosoft/postgresql/1.1
server
Architecture: any
Architecture: any
name: server
variables:
- name: db_name
value: db
- name: db_user
value: dbuser
- name: db_password
required: true
secret: true
- name: allowed_host
value: 0.0.0.0/0
fodder:
- name: server
type: cloud-config
secret: true
content: |
package_update: true
write_files:
- path: /opt/eryph-postgresql-setup.sh
permissions: '0755'
content: |
#!/bin/bash
set -euo pipefail
DB_NAME='{{ db_name }}'
DB_USER='{{ db_user }}'
DB_PASS='{{ db_password }}'
ALLOWED_HOST='{{ allowed_host }}'
. /etc/os-release
FAMILY=rhel
case " ${ID:-} ${ID_LIKE:-} " in
*" debian "*|*" ubuntu "*) FAMILY=debian ;;
esac
STATE_LABEL=postgresql
# Install PostgreSQL (package names differ per family)
if [ "$FAMILY" = debian ]; then
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq postgresql postgresql-contrib openssl
systemctl stop postgresql # reconfigure onto the state drive below
else
dnf install -y -q postgresql-server postgresql-contrib openssl
fi
# --- Persistent state drive: the PostgreSQL data directory lives here so
# the databases survive a redeploy. A named data drive on the catlet is
# required; it is picked by its label (redeploy) or as the empty non-root
# disk (first deploy). Guest device names are not stable, so never assume
# /dev/sdb. ---
if [ "$FAMILY" = debian ]; then
PG_VER=$(ls /var/lib/postgresql | grep -E '^[0-9]+$' | sort -n | tail -1)
STATE=/var/lib/postgresql/eryph
else
STATE=/var/lib/pgsql/data
fi
if ! mountpoint -q "$STATE"; then
mkdir -p "$STATE"
dev=$(blkid -L "$STATE_LABEL" 2>/dev/null || true)
if [ -z "$dev" ]; then
root_src=$(findmnt -no SOURCE / || true)
root_disk=$(lsblk -no PKNAME "$root_src" 2>/dev/null || true)
[ -z "$root_disk" ] && root_disk=$(basename "$root_src")
for d in $(lsblk -dn -o NAME,TYPE | awk '$2=="disk"{print $1}'); do
[ "$d" = "$root_disk" ] && continue
if [ -z "$(lsblk -no FSTYPE "/dev/$d" | tr -d '[:space:]')" ]; then
mkfs.ext4 -q -L "$STATE_LABEL" "/dev/$d"; dev="/dev/$d"; break
fi
done
fi
if [ -z "$dev" ]; then
echo "ERROR: no empty data drive found for persistent state." >&2
echo "Add a data drive to the catlet, e.g. drives: - name: sdb / size: 10" >&2
exit 1
fi
grep -q "LABEL=$STATE_LABEL[[:space:]]" /etc/fstab || \
echo "LABEL=$STATE_LABEL $STATE ext4 defaults,nofail 0 2" >> /etc/fstab
mount "$STATE"
fi
rm -rf "$STATE/lost+found"
if [ "$FAMILY" = debian ]; then
PGDATA_DIR="$STATE/pgdata"
chown postgres:postgres "$STATE"
if [ ! -f "$PGDATA_DIR/PG_VERSION" ]; then
sudo -u postgres "/usr/lib/postgresql/$PG_VER/bin/initdb" -D "$PGDATA_DIR" >/dev/null 2>&1
fi
sed -i "s|^data_directory.*|data_directory = '$PGDATA_DIR'|" \
"/etc/postgresql/$PG_VER/main/postgresql.conf"
chown -R postgres:postgres "$STATE"
systemctl enable postgresql >/dev/null 2>&1 || true
systemctl start postgresql
else
chown postgres:postgres "$STATE"; chmod 700 "$STATE"
command -v restorecon >/dev/null 2>&1 && restorecon -R /var/lib/pgsql || true
if [ ! -f "$STATE/PG_VERSION" ]; then postgresql-setup --initdb >/dev/null 2>&1; fi
command -v restorecon >/dev/null 2>&1 && restorecon -R /var/lib/pgsql || true
systemctl enable --now postgresql >/dev/null 2>&1
fi
PSQL="sudo -u postgres psql"
PGDATA=$($PSQL -tAc "SHOW data_directory;")
HBA=$($PSQL -tAc "SHOW hba_file;")
# Self-signed server certificate (PostgreSQL looks for server.crt/key in the datadir).
# Debian ships one and enables ssl; RHEL does not - generate for both for consistency.
if [ ! -f "$PGDATA/server.crt" ]; then
openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \
-keyout "$PGDATA/server.key" -out "$PGDATA/server.crt" -subj "/CN=$(hostname)" >/dev/null 2>&1
chown postgres:postgres "$PGDATA/server.key" "$PGDATA/server.crt"
chmod 600 "$PGDATA/server.key"; chmod 644 "$PGDATA/server.crt"
fi
$PSQL -c "ALTER SYSTEM SET listen_addresses = '*';" >/dev/null
$PSQL -c "ALTER SYSTEM SET ssl = 'on';" >/dev/null
# Role + database (idempotent)
$PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='$DB_USER'" | grep -q 1 \
|| $PSQL -c "CREATE ROLE \"$DB_USER\" LOGIN PASSWORD '$DB_PASS';" >/dev/null
$PSQL -tAc "SELECT 1 FROM pg_database WHERE datname='$DB_NAME'" | grep -q 1 \
|| $PSQL -c "CREATE DATABASE \"$DB_NAME\" OWNER \"$DB_USER\";" >/dev/null
# Allow the user to connect over TCP. The rule must PRECEDE the distro
# defaults (RHEL ships a 127.0.0.1 ident rule that would match first).
LINE="host $DB_NAME $DB_USER $ALLOWED_HOST scram-sha-256"
tmp=$(mktemp); { echo "$LINE"; grep -vxF "$LINE" "$HBA"; } > "$tmp"; cat "$tmp" > "$HBA"; rm -f "$tmp"
systemctl restart postgresql
# Open the firewall only when one is active
if command -v firewall-cmd >/dev/null 2>&1 && firewall-cmd --state >/dev/null 2>&1; then
firewall-cmd --permanent --add-port=5432/tcp >/dev/null; firewall-cmd --reload >/dev/null
fi
if command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q "Status: active"; then
ufw allow 5432/tcp >/dev/null
fi
runcmd:
- bash /opt/eryph-postgresql-setup.sh