53 lines
3.9 KiB
Python
53 lines
3.9 KiB
Python
"""Real SOPS regressions with generated keys, no host custody or network mounts."""
|
|
from pathlib import Path
|
|
import fcntl, hashlib, json, os, resource, subprocess
|
|
|
|
BASE=Path(__file__).resolve().parent
|
|
SOURCE=Path('/tmp/custodian-sops3133-fd/source')
|
|
BINARY=Path('/tmp/custodian-sops3133-test/bin/sops')
|
|
PIN='e5bec3346a873ae91d871550f3e698c1aad962aff462a080e40f25fde17fef6b'
|
|
metadata=json.loads((BASE/'sops-source-tool.json').read_text())
|
|
for relative,digest in metadata['public_source_files'].items():
|
|
assert 'sha256:'+hashlib.sha256((SOURCE/relative).read_bytes()).hexdigest()==digest
|
|
binary=BINARY.read_bytes();assert hashlib.sha256(binary).hexdigest()==PIN
|
|
fd=os.memfd_create('custodian-generated-sops-test-binary',os.MFD_ALLOW_SEALING)
|
|
os.write(fd,binary);os.lseek(fd,0,os.SEEK_SET)
|
|
fcntl.fcntl(fd,fcntl.F_ADD_SEALS,fcntl.F_SEAL_WRITE|fcntl.F_SEAL_GROW|fcntl.F_SEAL_SHRINK|fcntl.F_SEAL_SEAL)
|
|
script='''import json, os, pathlib, subprocess, sys
|
|
assert not pathlib.Path('/home/worsch').exists()
|
|
assert not pathlib.Path('/etc/rancher').exists()
|
|
assert not pathlib.Path('/home/tester/.config').exists()
|
|
assert not pathlib.Path('/proc/net/route').read_text().splitlines()[1:]
|
|
assert os.stat('/tmp').st_uid==os.getuid()
|
|
r=subprocess.run(['/usr/bin/python3','-m','pytest','-q','-c','/dev/null','-p','no:cacheprovider','--tb=no','--basetemp=/tmp/generated-sops-tests','/work/tests/test_sops_private.py','/work/tests/test_sops_write.py'],capture_output=True,text=True,timeout=150)
|
|
# Pytest tracebacks/captured tool output are never emitted. Retain its closed
|
|
# count summary only, including any failures rather than treating exit as proof.
|
|
lines=[line for line in r.stdout.splitlines() if __import__('re').fullmatch(r'[0-9]+ (?:passed|failed|skipped|error|errors)(?:, [0-9]+ (?:passed|failed|skipped|error|errors))* in [0-9.]+s',line)]
|
|
print(json.dumps({'status':'passed' if r.returncode==0 else 'failed','exit_code':r.returncode,'count_summary':lines,'private_diagnostics_suppressed':True,'generated_material_only':True,'host_custody_mounted':False,'network_routes_absent':True}))
|
|
sys.exit(r.returncode)
|
|
'''
|
|
command=['/usr/bin/bwrap','--unshare-all','--die-with-parent','--new-session','--clearenv','--cap-drop','ALL',
|
|
'--ro-bind','/usr','/usr','--symlink','usr/bin','/bin','--symlink','usr/lib','/lib',
|
|
'--ro-bind-try','/lib64','/lib64','--proc','/proc','--dev','/dev','--dir','/etc',
|
|
'--size','67108864','--tmpfs','/tmp','--tmpfs','/home','--dir','/home/tester',
|
|
'--ro-bind',str(SOURCE),'/work','--dir','/tools','--perms','0500','--ro-bind-data',str(fd),'/tools/sops',
|
|
'--setenv','PATH','/tools:/usr/bin:/bin','--setenv','HOME','/home/tester',
|
|
'--setenv','LANG','C.UTF-8','--setenv','PYTHONDONTWRITEBYTECODE','1',
|
|
'--setenv','PYTEST_DISABLE_PLUGIN_AUTOLOAD','1','--setenv','GOMAXPROCS','2',
|
|
'--chdir','/work','--','/usr/bin/python3','-c',script]
|
|
def limits():
|
|
resource.setrlimit(resource.RLIMIT_CPU,(120,120))
|
|
resource.setrlimit(resource.RLIMIT_AS,(8*1024**3,8*1024**3))
|
|
# bwrap copies the sealed 52-MB tool into an anonymous read-only bind file.
|
|
# The fixture's writable /tmp remains independently bounded to 64 MiB.
|
|
resource.setrlimit(resource.RLIMIT_FSIZE,(64*1024**2,64*1024**2))
|
|
try:
|
|
result=subprocess.run(command,pass_fds=(fd,),capture_output=True,text=True,timeout=180,preexec_fn=limits)
|
|
finally:os.close(fd)
|
|
try:receipt=json.loads(result.stdout)
|
|
except ValueError:receipt={'status':'unverified','exit_code':result.returncode,'fixed_reason':'namespace_preflight_or_runtime_failed','private_diagnostics_suppressed':True}
|
|
receipt.update(binary_sha256='sha256:'+PIN,driver_sha256='sha256:'+hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
|
|
namespace_teardown_complete=True,system_installation=False)
|
|
(BASE/'sops-real-regressions.json').write_text(json.dumps(receipt,indent=2)+'\n')
|
|
print(json.dumps(receipt))
|
|
raise SystemExit(result.returncode)
|