Add PostgreSQL user management playbook and documentation
Created reusable Ansible playbook for creating PostgreSQL users with flexible privilege options (superuser, createdb, createrole). Features include auto-generated secure passwords, credential file export, and comprehensive documentation with examples. Files added: - playbooks/create-postgres-user.yml - Automated user creation - docs/POSTGRES-USER-MANAGEMENT.md - Usage guide and examples Initial use case: Created hiveops superuser for HiveOps application. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0281f7d806
commit
015c708644
|
|
@ -0,0 +1,244 @@
|
||||||
|
# PostgreSQL User Management
|
||||||
|
|
||||||
|
This guide covers creating and managing PostgreSQL users on the postgres server (192.168.200.103).
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### Create Superuser with Random Password
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/create-postgres-user.yml -e "pg_username=hiveops pg_superuser=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create User with Specific Password
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/create-postgres-user.yml -e "pg_username=myapp pg_password=SecurePass123"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create Database Creator User
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/create-postgres-user.yml -e "pg_username=dbadmin pg_createdb=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create Basic User (No Special Privileges)
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/create-postgres-user.yml -e "pg_username=readonly"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Playbook Variables
|
||||||
|
|
||||||
|
| Variable | Required | Default | Description |
|
||||||
|
|----------|----------|---------|-------------|
|
||||||
|
| `pg_username` | Yes | - | PostgreSQL username to create |
|
||||||
|
| `pg_password` | No | Auto-generated | Password (random 32-char base64 if not provided) |
|
||||||
|
| `pg_superuser` | No | `false` | Grant SUPERUSER privilege |
|
||||||
|
| `pg_createdb` | No | `false` | Grant CREATEDB privilege |
|
||||||
|
| `pg_createrole` | No | `false` | Grant CREATEROLE privilege |
|
||||||
|
| `pg_login` | No | `true` | Allow user to login |
|
||||||
|
| `pg_save_credentials` | No | `false` | Save credentials to `/tmp/postgres-user-*.txt` |
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: HiveOps Application User (Superuser)
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/create-postgres-user.yml \
|
||||||
|
-e "pg_username=hiveops" \
|
||||||
|
-e "pg_superuser=true" \
|
||||||
|
-e "pg_save_credentials=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- Random password generated
|
||||||
|
- Superuser privileges
|
||||||
|
- Credentials saved to `/tmp/postgres-user-hiveops-*.txt`
|
||||||
|
|
||||||
|
### Example 2: Application User with Database Creation
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/create-postgres-user.yml \
|
||||||
|
-e "pg_username=smartjournal" \
|
||||||
|
-e "pg_createdb=true" \
|
||||||
|
-e "pg_password=MySecurePassword123"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- Specific password used
|
||||||
|
- Can create databases
|
||||||
|
- Cannot create other users
|
||||||
|
|
||||||
|
### Example 3: Read-Only Application User
|
||||||
|
```bash
|
||||||
|
# First create the user
|
||||||
|
ansible-playbook playbooks/create-postgres-user.yml \
|
||||||
|
-e "pg_username=reporting"
|
||||||
|
|
||||||
|
# Then grant SELECT permissions manually
|
||||||
|
ansible postgres -m shell \
|
||||||
|
-a "psql -d mydb -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO reporting;'" \
|
||||||
|
--become-user=postgres -b
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 4: Multiple Users at Once
|
||||||
|
```bash
|
||||||
|
# Create a variables file
|
||||||
|
cat > /tmp/users.yml <<EOF
|
||||||
|
---
|
||||||
|
users:
|
||||||
|
- username: hiveops
|
||||||
|
superuser: true
|
||||||
|
- username: smartjournal
|
||||||
|
createdb: true
|
||||||
|
- username: readonly
|
||||||
|
superuser: false
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Run for each user
|
||||||
|
for user in hiveops smartjournal readonly; do
|
||||||
|
ansible-playbook playbooks/create-postgres-user.yml \
|
||||||
|
-e "pg_username=$user" \
|
||||||
|
-e "@/tmp/users.yml"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ad-Hoc User Management
|
||||||
|
|
||||||
|
### Change User Password
|
||||||
|
```bash
|
||||||
|
ansible postgres -m shell \
|
||||||
|
-a "psql -c \"ALTER USER hiveops WITH PASSWORD 'new_password';\"" \
|
||||||
|
--become-user=postgres -b
|
||||||
|
```
|
||||||
|
|
||||||
|
### Grant Superuser to Existing User
|
||||||
|
```bash
|
||||||
|
ansible postgres -m shell \
|
||||||
|
-a "psql -c 'ALTER USER myuser WITH SUPERUSER;'" \
|
||||||
|
--become-user=postgres -b
|
||||||
|
```
|
||||||
|
|
||||||
|
### Revoke Superuser from User
|
||||||
|
```bash
|
||||||
|
ansible postgres -m shell \
|
||||||
|
-a "psql -c 'ALTER USER myuser WITH NOSUPERUSER;'" \
|
||||||
|
--become-user=postgres -b
|
||||||
|
```
|
||||||
|
|
||||||
|
### List All Users
|
||||||
|
```bash
|
||||||
|
ansible postgres -m shell \
|
||||||
|
-a "psql -c '\du'" \
|
||||||
|
--become-user=postgres -b
|
||||||
|
```
|
||||||
|
|
||||||
|
### Drop User
|
||||||
|
```bash
|
||||||
|
ansible postgres -m shell \
|
||||||
|
-a "psql -c 'DROP USER myuser;'" \
|
||||||
|
--become-user=postgres -b
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database Permissions
|
||||||
|
|
||||||
|
### Grant All Privileges on Database
|
||||||
|
```bash
|
||||||
|
ansible postgres -m shell \
|
||||||
|
-a "psql -c 'GRANT ALL PRIVILEGES ON DATABASE mydb TO hiveops;'" \
|
||||||
|
--become-user=postgres -b
|
||||||
|
```
|
||||||
|
|
||||||
|
### Grant SELECT on All Tables
|
||||||
|
```bash
|
||||||
|
ansible postgres -m shell \
|
||||||
|
-a "psql -d mydb -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;'" \
|
||||||
|
--become-user=postgres -b
|
||||||
|
```
|
||||||
|
|
||||||
|
### Grant CREATE on Schema
|
||||||
|
```bash
|
||||||
|
ansible postgres -m shell \
|
||||||
|
-a "psql -d mydb -c 'GRANT CREATE ON SCHEMA public TO myuser;'" \
|
||||||
|
--become-user=postgres -b
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Best Practices
|
||||||
|
|
||||||
|
1. **Use Strong Passwords**: Always use the auto-generated passwords or strong passwords (32+ characters)
|
||||||
|
2. **Principle of Least Privilege**: Only grant necessary permissions
|
||||||
|
3. **Superuser Sparingly**: Only create superusers when absolutely necessary
|
||||||
|
4. **Save Credentials Securely**: Use `pg_save_credentials=true` and move to vault
|
||||||
|
5. **Rotate Passwords**: Change passwords periodically for sensitive accounts
|
||||||
|
|
||||||
|
## Connection Examples
|
||||||
|
|
||||||
|
### psql Command Line
|
||||||
|
```bash
|
||||||
|
psql -h 192.168.200.103 -U hiveops -d mydatabase
|
||||||
|
```
|
||||||
|
|
||||||
|
### Spring Boot (application.properties)
|
||||||
|
```properties
|
||||||
|
spring.datasource.url=jdbc:postgresql://192.168.200.103:5432/hiveops
|
||||||
|
spring.datasource.username=hiveops
|
||||||
|
spring.datasource.password=j2ONAsFC6xPHk/VhktBE1qDKwUFsZQwjZvxf/rpViaE=
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python (psycopg2)
|
||||||
|
```python
|
||||||
|
import psycopg2
|
||||||
|
|
||||||
|
conn = psycopg2.connect(
|
||||||
|
host="192.168.200.103",
|
||||||
|
port=5432,
|
||||||
|
database="hiveops",
|
||||||
|
user="hiveops",
|
||||||
|
password="j2ONAsFC6xPHk/VhktBE1qDKwUFsZQwjZvxf/rpViaE="
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Node.js (pg)
|
||||||
|
```javascript
|
||||||
|
const { Pool } = require('pg');
|
||||||
|
|
||||||
|
const pool = new Pool({
|
||||||
|
host: '192.168.200.103',
|
||||||
|
port: 5432,
|
||||||
|
database: 'hiveops',
|
||||||
|
user: 'hiveops',
|
||||||
|
password: 'j2ONAsFC6xPHk/VhktBE1qDKwUFsZQwjZvxf/rpViaE='
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### User Already Exists
|
||||||
|
The playbook will update the existing user with new privileges/password if it already exists.
|
||||||
|
|
||||||
|
### Permission Denied
|
||||||
|
Ensure you're using `-b` (become) flag and the postgres user exists on the server.
|
||||||
|
|
||||||
|
### Connection Refused
|
||||||
|
Check that PostgreSQL is listening on the network interface:
|
||||||
|
```bash
|
||||||
|
ansible postgres -m shell \
|
||||||
|
-a "grep listen_addresses /etc/postgresql/*/main/postgresql.conf" -b
|
||||||
|
```
|
||||||
|
|
||||||
|
Should be: `listen_addresses = '*'`
|
||||||
|
|
||||||
|
### Authentication Failed
|
||||||
|
Check `pg_hba.conf` for connection rules:
|
||||||
|
```bash
|
||||||
|
ansible postgres -m shell \
|
||||||
|
-a "cat /etc/postgresql/*/main/pg_hba.conf" -b
|
||||||
|
```
|
||||||
|
|
||||||
|
## History
|
||||||
|
|
||||||
|
### 2026-02-14
|
||||||
|
- Created playbook for automated PostgreSQL user creation
|
||||||
|
- Initial user created: `hiveops` (superuser)
|
||||||
|
- Password: `j2ONAsFC6xPHk/VhktBE1qDKwUFsZQwjZvxf/rpViaE=`
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
- [PostgreSQL Official Documentation](https://www.postgresql.org/docs/)
|
||||||
|
- [Security Best Practices](https://www.postgresql.org/docs/current/auth-methods.html)
|
||||||
|
- Ansible Project: `/source/dlx-src/dlx-ansible/`
|
||||||
|
- Playbook: `playbooks/create-postgres-user.yml`
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
---
|
||||||
|
# Playbook: Create PostgreSQL User
|
||||||
|
# Description: Creates a PostgreSQL user with specified privileges
|
||||||
|
# Usage:
|
||||||
|
# Basic user:
|
||||||
|
# ansible-playbook playbooks/create-postgres-user.yml -e "pg_username=myuser"
|
||||||
|
#
|
||||||
|
# With specific password:
|
||||||
|
# ansible-playbook playbooks/create-postgres-user.yml -e "pg_username=myuser pg_password=mypassword"
|
||||||
|
#
|
||||||
|
# With specific privileges:
|
||||||
|
# ansible-playbook playbooks/create-postgres-user.yml -e "pg_username=myuser pg_superuser=true"
|
||||||
|
# ansible-playbook playbooks/create-postgres-user.yml -e "pg_username=myuser pg_createdb=true"
|
||||||
|
#
|
||||||
|
# Variables:
|
||||||
|
# pg_username: Username to create (required)
|
||||||
|
# pg_password: Password for user (optional, will generate random if not provided)
|
||||||
|
# pg_superuser: Grant SUPERUSER privilege (default: false)
|
||||||
|
# pg_createdb: Grant CREATEDB privilege (default: false)
|
||||||
|
# pg_createrole: Grant CREATEROLE privilege (default: false)
|
||||||
|
# pg_login: Allow user to login (default: true)
|
||||||
|
|
||||||
|
- name: Create PostgreSQL User
|
||||||
|
hosts: postgres
|
||||||
|
become: true
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
vars:
|
||||||
|
pg_superuser: false
|
||||||
|
pg_createdb: false
|
||||||
|
pg_createrole: false
|
||||||
|
pg_login: true
|
||||||
|
pg_generate_password: "{{ pg_password is not defined }}"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Validate required variables
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- pg_username is defined
|
||||||
|
- pg_username | length > 0
|
||||||
|
fail_msg: "pg_username is required. Usage: ansible-playbook playbooks/create-postgres-user.yml -e 'pg_username=myuser'"
|
||||||
|
|
||||||
|
- name: Generate random password if not provided
|
||||||
|
ansible.builtin.shell: openssl rand -base64 32
|
||||||
|
register: generated_password
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
when: pg_generate_password | bool
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Set password variable
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
pg_password: "{{ generated_password.stdout if pg_generate_password | bool else pg_password }}"
|
||||||
|
|
||||||
|
- name: Build role attributes
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
role_attrs: >-
|
||||||
|
{{
|
||||||
|
(pg_superuser | bool) | ternary('SUPERUSER', 'NOSUPERUSER')
|
||||||
|
}}
|
||||||
|
{{
|
||||||
|
(pg_createdb | bool) | ternary('CREATEDB', 'NOCREATEDB')
|
||||||
|
}}
|
||||||
|
{{
|
||||||
|
(pg_createrole | bool) | ternary('CREATEROLE', 'NOCREATEROLE')
|
||||||
|
}}
|
||||||
|
{{
|
||||||
|
(pg_login | bool) | ternary('LOGIN', 'NOLOGIN')
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Check if user already exists
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='{{ pg_username }}'"
|
||||||
|
become_user: postgres
|
||||||
|
register: user_exists
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Create PostgreSQL user
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
psql -c "CREATE USER {{ pg_username }} WITH {{ role_attrs }} PASSWORD '{{ pg_password }}';"
|
||||||
|
become_user: postgres
|
||||||
|
when: user_exists.stdout != "1"
|
||||||
|
register: user_created
|
||||||
|
|
||||||
|
- name: Update existing user
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
psql -c "ALTER USER {{ pg_username }} WITH {{ role_attrs }} PASSWORD '{{ pg_password }}';"
|
||||||
|
become_user: postgres
|
||||||
|
when: user_exists.stdout == "1"
|
||||||
|
register: user_updated
|
||||||
|
|
||||||
|
- name: Verify user creation
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
psql -c '\du {{ pg_username }}'
|
||||||
|
become_user: postgres
|
||||||
|
register: user_details
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Display user information
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "=========================================="
|
||||||
|
- "PostgreSQL User Created Successfully"
|
||||||
|
- "=========================================="
|
||||||
|
- "Username: {{ pg_username }}"
|
||||||
|
- "Password: {{ pg_password }}"
|
||||||
|
- "Server: {{ ansible_host }}"
|
||||||
|
- "Port: 5432"
|
||||||
|
- ""
|
||||||
|
- "Connection String:"
|
||||||
|
- "postgresql://{{ pg_username }}:{{ pg_password }}@{{ ansible_host }}:5432/database_name"
|
||||||
|
- ""
|
||||||
|
- "Privileges:"
|
||||||
|
- " Superuser: {{ pg_superuser }}"
|
||||||
|
- " Create DB: {{ pg_createdb }}"
|
||||||
|
- " Create Role: {{ pg_createrole }}"
|
||||||
|
- " Login: {{ pg_login }}"
|
||||||
|
- ""
|
||||||
|
- "User Details:"
|
||||||
|
- "{{ user_details.stdout_lines }}"
|
||||||
|
- ""
|
||||||
|
- "⚠️ IMPORTANT: Save this password securely!"
|
||||||
|
- "=========================================="
|
||||||
|
|
||||||
|
- name: Save credentials to file (optional)
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: |
|
||||||
|
# PostgreSQL User Credentials
|
||||||
|
# Created: {{ ansible_date_time.iso8601 }}
|
||||||
|
|
||||||
|
Username: {{ pg_username }}
|
||||||
|
Password: {{ pg_password }}
|
||||||
|
Server: {{ ansible_host }}
|
||||||
|
Port: 5432
|
||||||
|
|
||||||
|
Connection String:
|
||||||
|
postgresql://{{ pg_username }}:{{ pg_password }}@{{ ansible_host }}:5432/database_name
|
||||||
|
|
||||||
|
Privileges:
|
||||||
|
- Superuser: {{ pg_superuser }}
|
||||||
|
- Create DB: {{ pg_createdb }}
|
||||||
|
- Create Role: {{ pg_createrole }}
|
||||||
|
- Login: {{ pg_login }}
|
||||||
|
dest: "/tmp/postgres-user-{{ pg_username }}-{{ ansible_date_time.epoch }}.txt"
|
||||||
|
mode: '0600'
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
when: pg_save_credentials | default(false) | bool
|
||||||
Loading…
Reference in New Issue