Skip to content

Commit ae8cdaf

Browse files
staaldraadsamrose
andauthored
feat: limit /etc to readonly (#1451)
* feat: limit /etc to readonly https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#ReadWritePaths= Prevent postgres, or child-process of, from writing to /etc * bump version numbers * chore: add test for read-only mount * start / stop postgresql.service when doing osquery permission checks * chore: version suffix for local infra tests * chore: bump versions --------- Co-authored-by: Sam Rose <[email protected]>
1 parent 645db72 commit ae8cdaf

File tree

4 files changed

+39
-3
lines changed

4 files changed

+39
-3
lines changed

Diff for: ansible/files/permission_check.py

+31
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@
9696
],
9797
}
9898

99+
# postgresql.service is expected to mount /etc as read-only
100+
expected_mount = "/etc ro"
99101

100102
# This program depends on osquery being installed on the system
101103
# Function to run osquery
@@ -152,6 +154,33 @@ def check_nixbld_users():
152154

153155
print("All nixbld users are in the 'nixbld' group.")
154156

157+
def check_postgresql_mount():
158+
# processes table has the nix .postgres-wrapped path as the
159+
# binary path, rather than /usr/lib/postgresql/bin/postgres which
160+
# is a symlink to /var/lib/postgresql/.nix-profile/bin/postgres, a script
161+
# that ultimately calls /nix/store/...-postgresql-and-plugins-15.8/bin/.postgres-wrapped
162+
query = """
163+
SELECT pid
164+
FROM processes
165+
WHERE path LIKE '%.postgres-wrapped%'
166+
AND cmdline LIKE '%-D /etc/postgresql%';
167+
"""
168+
query_result = run_osquery(query)
169+
parsed_result = parse_json(query_result)
170+
171+
pid = parsed_result[0].get("pid")
172+
173+
# get the mounts for the process
174+
with open(f"/proc/{pid}/mounts", "r") as o:
175+
lines = [line for line in o if "/etc" in line and "ro," in line]
176+
if len(lines) == 0:
177+
print(f"Expected exactly 1 match, got 0")
178+
sys.exit(1)
179+
if len(lines) != 1:
180+
print(f"Expected exactly 1 match, got {len(lines)}: {';'.join(lines)}")
181+
sys.exit(1)
182+
183+
print("postgresql.service mounts /etc as read-only.")
155184

156185
def main():
157186
parser = argparse.ArgumentParser(
@@ -219,6 +248,8 @@ def main():
219248
# Check if all nixbld users are in the nixbld group
220249
check_nixbld_users()
221250

251+
# Check if postgresql.service is using a read-only mount for /etc
252+
check_postgresql_mount()
222253

223254
if __name__ == "__main__":
224255
main()

Diff for: ansible/files/postgresql_config/postgresql.service.j2

+3
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,8 @@ RestartSec=5
2121
OOMScoreAdjust=-1000
2222
EnvironmentFile=-/etc/environment.d/postgresql.env
2323
LimitNOFILE=16384
24+
{% if supabase_internal is defined %}
25+
ReadOnlyPaths=/etc
26+
{% endif %}
2427
[Install]
2528
WantedBy=multi-user.target

Diff for: ansible/playbook.yml

+2
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,9 @@
213213
- name: Run osquery permission checks
214214
become: yes
215215
shell: |
216+
systemctl start postgresql.service
216217
sudo -u ubuntu bash -c ". /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && /usr/bin/python3 /tmp/ansible-playbook/ansible/files/permission_check.py {{ '--qemu' if qemu_mode is defined else '' }}"
218+
systemctl stop postgresql.service
217219
when: stage2_nix
218220

219221
- name: Remove osquery

Diff for: ansible/vars.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ postgres_major:
99

1010
# Full version strings for each major version
1111
postgres_release:
12-
postgresorioledb-17: "17.0.1.063-orioledb"
13-
postgres17: "17.4.1.013"
14-
postgres15: "15.8.1.070"
12+
postgresorioledb-17: "17.0.1.064-orioledb"
13+
postgres17: "17.4.1.014"
14+
postgres15: "15.8.1.071"
1515

1616
# Non Postgres Extensions
1717
pgbouncer_release: "1.19.0"

0 commit comments

Comments
 (0)