Skip to content

Commit

Permalink
doc(postgres): revamp with current best-known practices, separate ser…
Browse files Browse the repository at this point in the history
…ver and client info
  • Loading branch information
coolaj86 committed Oct 14, 2024
1 parent 6924bac commit d8fffe0
Showing 1 changed file with 214 additions and 64 deletions.
278 changes: 214 additions & 64 deletions postgres/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ install:
~/.config/envman/PATH.env
~/.local/opt/postgres/
~/.local/share/postgres/var/postgresql.conf
# for psql (postgres client)
~/.pgpass
~/.psqlrc
~/.config/psql/
```

## Cheat Sheet
Expand All @@ -25,116 +30,261 @@ install:
> handles SQL, 'NoSQL', JSON, HSTORE, Full-Text Search, Messages Queues and
> more. Best bang for buck.
### Start the postgres server
To enable Postgres as a Linux Service with [serviceman](../serviceman/): \
(see macOS below)

Run just once (for development):
```sh
sudo env PATH="$PATH" \
serviceman add --system --username "$(whoami)" --name 'postgres' -- \
postgres -D ~/.local/share/postgres/var -p 5432

sudo systemctl restart systemd-journald
```

To login: \
(see creating remote app users below)

```sh
postgres -D $HOME/.local/share/postgres/var -p 5432
# as Postgres admin
psql "postgres://postgres:postgres@localhost:5432/postgres"

# as remote user
psql "postgres://[email protected]:5432/db-xxxx?sslmode=require&sslnegotiation=direct"
```

Run as a system service on Linux:
## Table of Contents

- Server vs Client & PG Essentials
- Initialize a database with a password
- Start the Postgres Server
- development
- systemd (most Linuxes)
- OpenRC (container Linuxes)
- macOS
- Enable Secure Remote Access
- Create Secure Remote App (User/DB)
- Change an User's (App's) password

### Where to Find the Postgres Client Cheat Sheet

This is exclusively a server-side cheat sheet.

For the client-side cheat sheet, see [The PSQL (Client) Cheat Sheet](../psql/).

For a collection of other helpful scripts, see
[PG Essentials](https://github.com/bnnanet/pg-essentials):

- pg-register-service
- pg-addgroup
- pg-adduser
- pg-passwd

### How to Initialize the Postgres Database (with password)

1. Create a database directory, paired to the Postgres version
```sh
mkdir -p ~/.local/share/postgres-17/var/
```
2. Use a password file and `initdb` to create a new DB directory
```sh
echo 'postgres' > /tmp/pwfile && \
initdb -D ~/.local/share/postgres/var/ \
--username 'postgres' --pwfile /tmp/pwfile \
--auth-local=password --auth-host=password \ &&
rm /tmp/pwfile
```
3. Test that the server starts
```sh
postgres -D ~/.local/share/postgres/var -p 5432
```
(kill with `ctrl+c`)

### How to run the Postgres server (daemon or foreground)

- foreground (development)
- systemd (Debian, Ubuntu, Redhat, etc)
- OpenRC (Alpine, Arch, Gentoo, etc)
- macOS (launchd)

#### To run in the foreground (for development):

```sh
postgres -D ~/.local/share/postgres/var -p 5432
```

#### Debian / Systemd

```sh
curl https://webi.sh/serviceman | sh
```

```sh
sudo env PATH="$PATH" \
serviceman add --system --username "$(whoami)" --name postgres -- \
postgres -D "$HOME/.local/share/postgres/var" -p 5432
serviceman add --system --username "$(whoami)" --name 'postgres' -- \
postgres -D ~/.local/share/postgres/var -p 5432

# Restart the logging service
sudo systemctl restart systemd-journald
sudo journalctl -xefu postgres
```

### Connect with the psql client
#### Alpine / OpenRC

`/etc/init.d/postgres`:

```sh
psql 'postgres://postgres:postgres@localhost:5432/postgres'
#!/sbin/openrc-run

name="postgres"
description="postgres daemon"
command="/home/app/.local/opt/postgres/bin/postgres"
command_args="-D /home/app/.local/share/postgres/var -p 5432"
command_user="app:app"

supervisor="supervise-daemon"
output_log="/var/log/postgres"
error_log="/var/log/postgres"

depend() {
need net
}

start_pre() {
checkpath --directory --owner root /var/log/
checkpath --file --owner ${command_user} ${output_log} ${error_log}
}

start() {
ebegin "Starting ${name}"
supervise-daemon ${name} --start \
--stdout ${output_log} \
--stderr ${error_log} \
--pidfile /run/${RC_SVCNAME}.pid \
--respawn-delay 5 \
--respawn-max 10 \
-- \
${command} \
${command_args} \
eend $?
}

stop() {
ebegin "Stopping ${name}"
supervise-daemon ${name} --stop \
--pidfile /run/${RC_SVCNAME}.pid
eend $?
}
```

### Initialize a database with a password

```sh
echo "postgres" > /tmp/pwfile
mkdir -p $HOME/.local/share/postgres/var/
sudo rc-update add postgres
sudo rc-service postgres restart

sudo tail -f /var/log/postgres
```

initdb -D $HOME/.local/share/postgres/var/ \
--username postgres --pwfile "/tmp/pwfile" \
--auth-local=password --auth-host=password
#### macOS

```sh
serviceman add --name 'postgres' -- \
postgres -D ~/.local/share/postgres/var -p 5432

rm /tmp/pwfile
tail -f ~/.local/share/postgres/var/log/postgres.log
```

### Add and secure remote users
### How to Enable Secure Remote Postgres Access

1. Create the `my_remote_users` group:

1. Set your server name or IP address
```sh
PG_HOST=pg-1.example.com
echo 'CREATE ROLE "my_remote_users" NOLOGIN;' |
psql "postgres://postgres:postgres@localhost:5432/postgres" -f -
```
2. Generate a 10-year self-signed TLS certificate

```sh
openssl req -new -x509 -days 3650 -nodes -text \
-out server.crt \
-keyout server.key \
-subj "/CN=$PG_HOST"
2. Update the hba permissions to enable App users (`sameuser`): \
`~/.local/share/postgres/var/pg_hba.conf`:

chmod og-rwx server.key server.crt
mv server.key server.crt ~/.local/share/postgres/var/
```ini
# Allow 'my_remote_users' to connect remotely directly over the internet
hostssl sameuser +my_remote_users 0.0.0.0/0 scram-sha-256
hostssl sameuser +my_remote_users ::0/0 scram-sha-256

# Allow 'my_remote_users' group to connect through a local TLS-terminating proxy
hostnossl sameuser +my_remote_users 127.0.0.1/8 scram-sha-256
hostnossl sameuser +my_remote_users ::1/128 scram-sha-256
host sameuser +my_remote_users 10.0.0.0/8 scram-sha-256
host sameuser +my_remote_users 172.16.0.0/12 scram-sha-256
host sameuser +my_remote_users 192.168.0.0/16 scram-sha-256
host sameuser +my_remote_users fc00::/7 scram-sha-256
```

3. Enable SSL (TLS)
3. Create a 10-year Self-signed cert for your server name or IP

```sh
vim ~/.local/share/postgres/var/postgresql.conf
my_host=pg-1.example.com

openssl req -new -x509 -days 3650 -nodes -text \
-out ./server.crt -keyout ./server.key -subj "/CN=$my_host"

chmod 0600 ./server.key ./server.crt
mv ./server.key ./server.crt ~/.local/share/postgres/var/
```

4. Update the main postgres config to allow secure connections: \
`~/.local/share/postgres/var/postgresql.conf`:

```ini
ssl = on
password_encryption = scram-sha-256
listen_addresses = '*'
```
4. Generate a user with a random token password

```sh
MY_USER='my_user'
MY_PASSWORD="$(xxd -l16 -ps /dev/urandom)"
5. Restart Postgres for the change to take effect

echo "CREATE ROLE \"$MY_USER\" LOGIN ENCRYPTED PASSWORD '$MY_PASSWORD';" |
psql 'postgres://postgres:postgres@localhost:5432/postgres' -f -
```

5. Show the token password and save it somewhere
```sh
echo "$MY_PASSWORD"
```
6. Allow the user to connect via IPv4 and IPv6
```sh
echo "# Allow $MY_USER to connect remotely over the internet
hostssl all $MY_USER 0.0.0.0/0 scram-sha-256
hostssl all $MY_USER ::0/0 scram-sha-256" \
>> ~/.local/share/postgres/var/pg_hba.conf
```
7. Restart postgres
```sh
# all unixes
killall postgres

# systemd (Debian, Ubunut, RedHat, Suse, etc)
sudo systemctl restart postgres
```
8. Test the connection from a remote system

```sh
PG_HOST="pg-1.example.com"
PG_USER="my_user"
# openrc (Alpine, Gentoo, Arch)
rc-service postgres restart

psql "postgres://$PG_USER@$PG_HOST/postgres?sslmode=require" << EOF
SELECT CURRENT_USER;
EOF
# macos
launchctl unload ~/Library/LaunchAgents/postgres.plist
launchctl load ~/Library/LaunchAgents/postgres.plist
```

(you will be prompted for your password / token)
### How to Create Secure Remote Users

The `sameuser` database directive REQUIRES the user and db name to be the same.

```sh
my_app="foobar_xxxx"

# e.g. generate password / token with 'xxd -l16 -ps /dev/urandom'
my_token="db-token-for-the-app"

echo "
CREATE DATABASE \"$my_app\";
CREATE ROLE \"$my_app\" LOGIN INHERIT
IN ROLE \"my_remote_users\" ENCRYPTED PASSWORD '$my_token';
GRANT ALL PRIVILEGES ON DATABASE \"$my_app\" to \"$my_app\";
" | psql "postgres://postgres:postgres@localhost:5432/postgres" -f -
```

Notes:

- quote style matters:
- double quotes `"` for identifiers (users, tables, groups, roles)
- single quotes `'` for values (password)

### Add or update a user's password
### How to Change a DB's Password

```sh
MY_USER='my_user'
MY_NEW_PASSWORD="$(xxd -l16 -ps /dev/urandom)"
my_app='foobar_xxxx'
my_token="$(xxd -l16 -ps /dev/urandom)"

# Update existing user with new password using new hash
echo "ALTER USER \"$MY_USER\" PASSWORD '$MY_NEW_PASSWORD';" |
echo "ALTER USER \"$my_app\" PASSWORD '$my_token';" |
psql 'postgres://postgres:postgres@localhost:5432/postgres' -f -
```

0 comments on commit d8fffe0

Please sign in to comment.