26 lines
1.8 KiB
Python
26 lines
1.8 KiB
Python
import pathlib,json,hashlib,tarfile,io,subprocess,sys,tempfile
|
|
archive,fqdn=sys.argv[1:]
|
|
stack=pathlib.Path('/opt/skeletonworks/conductor')/fqdn
|
|
def fingerprint():return {p.name:hashlib.sha256(p.read_bytes()).hexdigest() for p in stack.iterdir() if p.is_file()}
|
|
before=fingerprint()
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
directory=pathlib.Path(directory)
|
|
with tarfile.open(archive) as source:
|
|
original=[(m,source.extractfile(m).read() if m.isfile() else b'') for m in source.getmembers()]
|
|
for kind in ['checksum','symlink','traversal','missing-applications']:
|
|
target=directory/(kind+'.tar.gz')
|
|
with tarfile.open(target,'w:gz') as output:
|
|
for member,data in original:
|
|
if kind=='missing-applications' and (member.name=='applications' or member.name.startswith('applications/')):continue
|
|
if kind=='checksum' and member.name=='manifest.json':data=data+b' '
|
|
member.size=len(data) if member.isfile() else 0
|
|
output.addfile(member,io.BytesIO(data) if member.isfile() else None)
|
|
if kind in ['symlink','traversal']:
|
|
member=tarfile.TarInfo('unsafe' if kind=='symlink' else '../unsafe')
|
|
if kind=='symlink':member.type=tarfile.SYMTYPE;member.linkname='/etc/passwd'
|
|
output.addfile(member)
|
|
result=subprocess.run(['/opt/skeletonworks/scripts/restore-conductor.sh','--fqdn',fqdn,'--backup-file',str(target),'--force','--skip-safety-backup'],capture_output=True,text=True)
|
|
assert result.returncode!=0,kind
|
|
assert fingerprint()==before,kind+' changed configuration before rejecting the archive'
|
|
print('PASS: corrupted, linked, traversing and incomplete-app backups rejected without configuration changes.')
|