1- import subprocess
2- import json
31import sys
42import argparse
53import os
64import stat
5+ import pwd
6+ import grp
77
88
99# Expected groups for each user
159159}
160160
161161
162- # This program depends on osquery being installed on the system
163- # Function to run osquery
164- def run_osquery (query ):
165- process = subprocess .Popen (
166- ["osqueryi" , "--json" , query ], stdout = subprocess .PIPE , stderr = subprocess .PIPE
167- )
168- output , error = process .communicate ()
169- return output .decode ("utf-8" )
170-
171-
172- def parse_json (json_str ):
162+ def get_user_groups (username ):
163+ """Get all groups that a user belongs to using Python's pwd and grp modules."""
173164 try :
174- return json .loads (json_str )
175- except json .JSONDecodeError as e :
176- print ("Error decoding JSON:" , e )
165+ user_info = pwd .getpwnam (username )
166+ user_uid = user_info .pw_uid
167+ user_gid = user_info .pw_gid
168+
169+ # Get all groups
170+ groups = []
171+ for group in grp .getgrall ():
172+ # Check if user is in the group (either as primary group or in member list)
173+ if user_gid == group .gr_gid or username in group .gr_mem :
174+ groups .append ({
175+ "username" : username ,
176+ "groupname" : group .gr_name
177+ })
178+
179+ # Sort by groupname to match expected behavior
180+ groups .sort (key = lambda x : x ["groupname" ])
181+ return groups
182+ except KeyError :
183+ print (f"User '{ username } ' not found" )
177184 sys .exit (1 )
178185
179186
@@ -195,43 +202,58 @@ def compare_results(username, query_result):
195202
196203
197204def check_nixbld_users ():
198- query = """
199- SELECT u.username, g.groupname
200- FROM users u
201- JOIN user_groups ug ON u.uid = ug.uid
202- JOIN groups g ON ug.gid = g.gid
203- WHERE u.username LIKE 'nixbld%';
204- """
205- query_result = run_osquery (query )
206- parsed_result = parse_json (query_result )
207-
208- for user in parsed_result :
209- if user ["groupname" ] != "nixbld" :
210- print (
211- f"User '{ user ['username' ]} ' is in group '{ user ['groupname' ]} ' instead of 'nixbld'."
212- )
213- sys .exit (1 )
205+ """Check that all nixbld users are only in the nixbld group."""
206+ # Get all users that match the pattern nixbld*
207+ nixbld_users = []
208+ for user in pwd .getpwall ():
209+ if user .pw_name .startswith ("nixbld" ):
210+ nixbld_users .append (user .pw_name )
211+
212+ if not nixbld_users :
213+ print ("No nixbld users found" )
214+ return
215+
216+ # Check each nixbld user's groups
217+ for username in nixbld_users :
218+ groups = get_user_groups (username )
219+ for user_group in groups :
220+ if user_group ["groupname" ] != "nixbld" :
221+ print (
222+ f"User '{ username } ' is in group '{ user_group ['groupname' ]} ' instead of 'nixbld'."
223+ )
224+ sys .exit (1 )
214225
215226 print ("All nixbld users are in the 'nixbld' group." )
216227
217228
218229def check_postgresql_mount ():
219- # processes table has the nix .postgres-wrapped path as the
220- # binary path, rather than /usr/lib/postgresql/bin/postgres which
221- # is a symlink to /var/lib/postgresql/.nix-profile/bin/postgres, a script
222- # that ultimately calls /nix/store/...-postgresql-and-plugins-15.8/bin/.postgres-wrapped
223- query = """
224- SELECT pid
225- FROM processes
226- WHERE path LIKE '%.postgres-wrapped%'
227- AND cmdline LIKE '%-D /etc/postgresql%';
228- """
229- query_result = run_osquery (query )
230- parsed_result = parse_json (query_result )
231-
232- pid = parsed_result [0 ].get ("pid" )
233-
234- # get the mounts for the process
230+ """Check that postgresql.service mounts /etc as read-only."""
231+ # Find the postgres process by reading /proc
232+ # We're looking for a process with .postgres-wrapped in the path
233+ # and -D /etc/postgresql in the command line
234+ pid = None
235+
236+ for proc_dir in os .listdir ("/proc" ):
237+ if not proc_dir .isdigit ():
238+ continue
239+
240+ try :
241+ # Read the command line
242+ with open (f"/proc/{ proc_dir } /cmdline" , "r" ) as f :
243+ cmdline = f .read ()
244+ # Check if this is a postgres process with the right data directory
245+ if ".postgres-wrapped" in cmdline and "-D /etc/postgresql" in cmdline :
246+ pid = proc_dir
247+ break
248+ except (FileNotFoundError , PermissionError ):
249+ # Process might have disappeared or we don't have permission
250+ continue
251+
252+ if pid is None :
253+ print ("Could not find postgres process with .postgres-wrapped and -D /etc/postgresql" )
254+ sys .exit (1 )
255+
256+ # Get the mounts for the process
235257 with open (f"/proc/{ pid } /mounts" , "r" ) as o :
236258 lines = [line for line in o if "/etc" in line and "ro," in line ]
237259 if len (lines ) == 0 :
@@ -265,9 +287,6 @@ def check_directory_permissions():
265287 actual_mode = oct (stat .S_IMODE (stat_info .st_mode ))[2 :] # Remove '0o' prefix
266288
267289 # Get owner and group names
268- import pwd
269- import grp
270-
271290 actual_owner = pwd .getpwuid (stat_info .st_uid ).pw_name
272291 actual_group = grp .getgrgid (stat_info .st_gid ).gr_name
273292
@@ -369,12 +388,10 @@ def main():
369388 if not qemu_artifact :
370389 usernames .append ("ec2-instance-connect" )
371390
372- # Iterate over usernames, run the query , and compare results
391+ # Iterate over usernames, get their groups , and compare results
373392 for username in usernames :
374- query = f"SELECT u.username, g.groupname FROM users u JOIN user_groups ug ON u.uid = ug.uid JOIN groups g ON ug.gid = g.gid WHERE u.username = '{ username } ' ORDER BY g.groupname;"
375- query_result = run_osquery (query )
376- parsed_result = parse_json (query_result )
377- compare_results (username , parsed_result )
393+ user_groups = get_user_groups (username )
394+ compare_results (username , user_groups )
378395
379396 # Check if all nixbld users are in the nixbld group
380397 check_nixbld_users ()
0 commit comments