Skip to content

Commit 9531b9d

Browse files
authored
fix: restore defaults to /var/lib/postgresql (#1901)
* fix: back to to root:root (standard for system binaries) * fix: restore permissions on /var/lib/postgresql/ for link traversal * chore: suffix to test * chore: bump to release
1 parent d383df0 commit 9531b9d

File tree

4 files changed

+142
-7
lines changed

4 files changed

+142
-7
lines changed

ansible/files/permission_check.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import json
33
import sys
44
import argparse
5+
import os
6+
import stat
57

68

79
# Expected groups for each user
@@ -103,6 +105,59 @@
103105
# postgresql.service is expected to mount /etc as read-only
104106
expected_mount = "/etc ro"
105107

108+
# Expected directory permissions for security-critical paths
109+
# Format: path -> (expected_mode, expected_owner, expected_group, description)
110+
expected_directory_permissions = {
111+
"/var/lib/postgresql": (
112+
"0755",
113+
"postgres",
114+
"postgres",
115+
"PostgreSQL home - must be traversable for nix-profile symlinks",
116+
),
117+
"/var/lib/postgresql/data": (
118+
"0750",
119+
"postgres",
120+
"postgres",
121+
"PostgreSQL data directory symlink - secure, postgres only",
122+
),
123+
"/data/pgdata": (
124+
"0750",
125+
"postgres",
126+
"postgres",
127+
"Actual PostgreSQL data directory - secure, postgres only",
128+
),
129+
"/etc/postgresql": (
130+
"0775",
131+
"postgres",
132+
"postgres",
133+
"PostgreSQL configuration directory - adminapi writable",
134+
),
135+
"/etc/postgresql-custom": (
136+
"0775",
137+
"postgres",
138+
"postgres",
139+
"PostgreSQL custom configuration - adminapi writable",
140+
),
141+
"/etc/ssl/private": (
142+
"0750",
143+
"root",
144+
"ssl-cert",
145+
"SSL private keys directory - secure, ssl-cert group only",
146+
),
147+
"/home/postgres": (
148+
"0750",
149+
"postgres",
150+
"postgres",
151+
"postgres user home directory - secure, postgres only",
152+
),
153+
"/var/log/postgresql": (
154+
"0750",
155+
"postgres",
156+
"postgres",
157+
"PostgreSQL logs directory - secure, postgres only",
158+
),
159+
}
160+
106161

107162
# This program depends on osquery being installed on the system
108163
# Function to run osquery
@@ -189,6 +244,75 @@ def check_postgresql_mount():
189244
print("postgresql.service mounts /etc as read-only.")
190245

191246

247+
def check_directory_permissions():
248+
"""Check that security-critical directories have the correct permissions."""
249+
errors = []
250+
251+
for path, (
252+
expected_mode,
253+
expected_owner,
254+
expected_group,
255+
description,
256+
) in expected_directory_permissions.items():
257+
# Skip if path doesn't exist (might be a symlink or not created yet)
258+
if not os.path.exists(path):
259+
print(f"Warning: {path} does not exist, skipping permission check")
260+
continue
261+
262+
# Get actual permissions
263+
try:
264+
stat_info = os.stat(path)
265+
actual_mode = oct(stat.S_IMODE(stat_info.st_mode))[2:] # Remove '0o' prefix
266+
267+
# Get owner and group names
268+
import pwd
269+
import grp
270+
271+
actual_owner = pwd.getpwuid(stat_info.st_uid).pw_name
272+
actual_group = grp.getgrgid(stat_info.st_gid).gr_name
273+
274+
# Check permissions
275+
if actual_mode != expected_mode:
276+
errors.append(
277+
f"ERROR: {path} has mode {actual_mode}, expected {expected_mode}\n"
278+
f" Description: {description}\n"
279+
f" Fix: sudo chmod {expected_mode} {path}"
280+
)
281+
282+
# Check ownership
283+
if actual_owner != expected_owner:
284+
errors.append(
285+
f"ERROR: {path} has owner {actual_owner}, expected {expected_owner}\n"
286+
f" Description: {description}\n"
287+
f" Fix: sudo chown {expected_owner}:{actual_group} {path}"
288+
)
289+
290+
# Check group
291+
if actual_group != expected_group:
292+
errors.append(
293+
f"ERROR: {path} has group {actual_group}, expected {expected_group}\n"
294+
f" Description: {description}\n"
295+
f" Fix: sudo chown {actual_owner}:{expected_group} {path}"
296+
)
297+
298+
if not errors or not any(path in err for err in errors):
299+
print(f"✓ {path}: {actual_mode} {actual_owner}:{actual_group} - OK")
300+
301+
except Exception as e:
302+
errors.append(f"ERROR: Failed to check {path}: {str(e)}")
303+
304+
if errors:
305+
print("\n" + "=" * 80)
306+
print("DIRECTORY PERMISSION ERRORS DETECTED:")
307+
print("=" * 80)
308+
for error in errors:
309+
print(error)
310+
print("=" * 80)
311+
sys.exit(1)
312+
313+
print("\nAll directory permissions are correct.")
314+
315+
192316
def main():
193317
parser = argparse.ArgumentParser(
194318
prog="Supabase Postgres Artifact Permissions Checker",
@@ -258,6 +382,9 @@ def main():
258382
# Check if postgresql.service is using a read-only mount for /etc
259383
check_postgresql_mount()
260384

385+
# Check directory permissions for security-critical paths
386+
check_directory_permissions()
387+
261388

262389
if __name__ == "__main__":
263390
main()

ansible/tasks/setup-postgres.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@
118118
loop_control:
119119
loop_var: 'pg_dir_item'
120120

121+
- name: Set /var/lib/postgresql to 0755 for nix-profile symlink traversal
122+
ansible.builtin.file:
123+
group: 'postgres'
124+
mode: '0755'
125+
owner: 'postgres'
126+
path: '/var/lib/postgresql'
127+
state: 'directory'
128+
121129
- name: Allow adminapi to write custom config
122130
ansible.builtin.file:
123131
group: 'postgres'

ansible/tasks/stage2-setup-postgres.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@
128128

129129
- name: Create symlinks for Nix files into /usr/bin
130130
ansible.builtin.file:
131-
group: 'postgres'
132-
owner: 'postgres'
131+
group: 'root'
132+
owner: 'root'
133133
path: "/usr/bin/{{ file_item['path'] | basename }}"
134134
src: "{{ file_item['path'] }}"
135135
state: 'link'
@@ -150,8 +150,8 @@
150150

151151
- name: Create symlinks for Nix files into /usr/bin
152152
ansible.builtin.file:
153-
group: 'postgres'
154-
owner: 'postgres'
153+
group: 'root'
154+
owner: 'root'
155155
path: "/usr/bin/{{ link_item['path'] | basename }}"
156156
src: "{{ link_item['path'] }}"
157157
state: 'link'

ansible/vars.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ postgres_major:
1010

1111
# Full version strings for each major version
1212
postgres_release:
13-
postgresorioledb-17: "17.5.1.064-orioledb"
14-
postgres17: "17.6.1.043"
15-
postgres15: "15.14.1.043"
13+
postgresorioledb-17: "17.5.1.065-orioledb"
14+
postgres17: "17.6.1.044"
15+
postgres15: "15.14.1.044"
1616

1717
# Non Postgres Extensions
1818
pgbouncer_release: 1.19.0

0 commit comments

Comments
 (0)