62 lines
2.4 KiB
Python
62 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Assemble an Alpine-derived, shell-free runtime from an explicit ELF allowlist."""
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
root = Path(sys.argv[1])
|
|
root.mkdir(parents=True, exist_ok=True)
|
|
|
|
copied = set()
|
|
|
|
def copy(source):
|
|
source = Path(os.path.normpath(source))
|
|
if source in copied:
|
|
return
|
|
copied.add(source)
|
|
target = root / str(source).lstrip('/')
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
if source.is_symlink():
|
|
link = os.readlink(source)
|
|
if not target.is_symlink():
|
|
target.symlink_to(link)
|
|
# Preserve every hop, not just the final file in a multi-link chain.
|
|
copy(link if os.path.isabs(link) else source.parent / link)
|
|
elif source.is_dir():
|
|
target.mkdir(exist_ok=True)
|
|
for child in source.iterdir():
|
|
copy(child)
|
|
else:
|
|
shutil.copy2(source, target)
|
|
|
|
for executable in sys.argv[2:]:
|
|
result = subprocess.run(['lddtreepax', '-l', executable], check=True, text=True, capture_output=True)
|
|
paths = result.stdout.splitlines()
|
|
if not paths or any(not p.startswith('/') or not Path(p).exists() for p in paths):
|
|
raise SystemExit(f'Unresolved runtime dependency for {executable}: {result.stdout}')
|
|
copy(executable)
|
|
for path in paths:
|
|
copy(path)
|
|
|
|
# musl uses this file for nonstandard library directories (for example Lua).
|
|
for search_path in Path('/etc').glob('ld-musl-*.path'):
|
|
copy(search_path)
|
|
|
|
for data in ['/etc/ssl/certs/ca-certificates.crt', '/etc/ssl/cert.pem',
|
|
'/etc/ssl/openssl.cnf', '/usr/lib/ossl-modules', '/usr/share/zoneinfo',
|
|
'/etc/terminfo', '/usr/share/terminfo', '/etc/services', '/etc/protocols',
|
|
'/etc/alpine-release', '/etc/os-release']:
|
|
if Path(data).exists():
|
|
copy(data)
|
|
for directory in ['tmp', 'root', 'myconfig', 'home/nfs_download/rsession', 'server/db', 'config', 'etc']:
|
|
(root / directory).mkdir(parents=True, exist_ok=True)
|
|
os.chmod(root / 'tmp', 0o1777)
|
|
(root / 'root/.rtorrent.rc').touch()
|
|
(root / 'etc/passwd').write_text('root:x:0:0:root:/root:/sbin/nologin\n')
|
|
(root / 'etc/group').write_text('root:x:0:\n')
|
|
for forbidden in ['bin/sh', 'bin/bash', 'bin/busybox', 'sbin/apk', 'usr/bin/apk',
|
|
'usr/bin/npm', 'usr/local/bin/npm', 'usr/bin/python3']:
|
|
assert not (root / forbidden).exists(), f'Unexpected runtime tool: {forbidden}'
|