"""Linux local socket/UID integration. Synthetic core inventory, no remote hosts.""" from dataclasses import replace from pathlib import Path import grp import json import os import pwd import shutil import signal import socket import struct import subprocess import sys import tempfile import time import pytest from aim_webgui.config import Settings from aim_webgui.core.client import CoreClient from aim_webgui.credentials import wire def test_nonroot_executor_public_core_and_peer_rejection(core_root): if os.geteuid()!=0 or not hasattr(socket,'SO_PEERCRED'): pytest.skip('Needs Linux root in the isolated test container to exercise different local UIDs.') account=pwd.getpwnam('nobody') base=Path(tempfile.mkdtemp(prefix='aim-exec-'));base.chmod(0o755) process=None try: core=base/'core';shutil.copytree(core_root,core) for p in [core,*core.rglob('*')]: if p.is_dir():p.chmod(0o755) else:p.chmod(0o644) config=core/'scripts/aim.yml' config.write_text(f'root_dir: {core}\nservice_user: synthetic\nrequired_group: {grp.getgrgid(account.pw_gid).gr_name}\n') runtime=base/'socket';runtime.mkdir(mode=0o711);os.chown(runtime,account.pw_uid,account.pw_gid) home=base/'home';home.mkdir(mode=0o700);os.chown(home,account.pw_uid,account.pw_gid) cfg=base/'web.toml' cfg.write_text('[core]\ntransport="unix"\n'+f'command=[{json.dumps(sys.executable)},{json.dumps(str(core/"scripts/aimctl.py"))}]\n'+ f'config={json.dumps(str(config))}\nhome={json.dumps(str(home))}\nsocket={json.dumps(str(runtime/"core.sock"))}\nexecutor_user="nobody"\nclient_user="root"\n') cfg.chmod(0o644) settings=Settings.load(cfg) source=Path(__file__).resolve().parents[1]/'src' def drop():os.setgroups([0,account.pw_gid]);os.setgid(account.pw_gid);os.setuid(account.pw_uid) process=subprocess.Popen([sys.executable,'-B','-m','aim_webgui','--config',str(cfg),'executor'], env={**os.environ,'PYTHONPATH':str(source)},stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,preexec_fn=drop) for _ in range(200): if settings.core_socket.exists():break if process.poll()is not None:pytest.fail('Executor fixture exited before opening socket') time.sleep(.05) assert settings.core_socket.exists() assert settings.core_socket.stat().st_uid==account.pw_uid assert runtime.stat().st_gid==account.pw_gid assert settings.core_socket.stat().st_gid==0 assert settings.core_socket.stat().st_mode&0o777==0o660 client=CoreClient(settings) assert client.request('capabilities')['core_version']=='3.3.0rc8' assert client.request('list_customers')==['example'] # A direct local client cannot smuggle an execution executable/path field. with socket.socket(socket.AF_UNIX)as conn: conn.connect(str(settings.core_socket));wire.send(conn,{'request':{'api_version':'1.0','operation':'list_customers','command':'id'},'credential_frame':False,'start_seconds':None}) assert wire.receive(conn)['type']=='transport_error' # Make only this synthetic endpoint reachable to a different UID, so the # SO_PEERCRED check, rather than DAC alone, is exercised. runtime.chmod(0o755);settings.core_socket.chmod(0o666) script='''import socket,sys s=socket.socket(socket.AF_UNIX);s.connect(sys.argv[1]);s.settimeout(3) assert s.recv(4)==b'' ''' other=pwd.getpwnam('daemon') def other_drop():os.setgroups([]);os.setgid(other.pw_gid);os.setuid(other.pw_uid) result=subprocess.run([sys.executable,'-c',script,str(settings.core_socket)],preexec_fn=other_drop,capture_output=True,timeout=5) assert result.returncode==0 finally: if process and process.poll()is None:process.send_signal(signal.SIGTERM);process.wait(timeout=20) shutil.rmtree(base)