64 lines
2.8 KiB
Python
64 lines
2.8 KiB
Python
#!/usr/bin/python3
|
|
"""Fresh native NTS observation with fixed-source, destination-limited egress."""
|
|
import ctypes
|
|
import hashlib
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import pwd
|
|
import socket
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
assert os.geteuid()==0 and Path('/run/.containerenv').exists()
|
|
assert socket.gethostname()=='c01-nts-composed'
|
|
root=Path('/opt/c01-nts')
|
|
sys.path.insert(0,str(root))
|
|
import c01_nts
|
|
|
|
subprocess.run(['/usr/sbin/nft','-f',str(root/'egress.nft')],check=True)
|
|
policy=c01_nts.Policy(frozenset({'time.cloudflare.com','nts.netnod.se'}))
|
|
c01_nts.CONFIG.parent.mkdir(parents=True,exist_ok=True)
|
|
c01_nts.CONFIG.write_bytes(c01_nts.configuration(policy))
|
|
c01_nts.CONFIG.chmod(0o644)
|
|
account=pwd.getpwnam(c01_nts.OBSERVER_USER)
|
|
c01_nts.SOCKET.parent.mkdir(mode=0o700,exist_ok=True)
|
|
os.chown(c01_nts.SOCKET.parent,account.pw_uid,account.pw_gid)
|
|
c01_nts.SOCKET.parent.chmod(0o700)
|
|
libc=ctypes.CDLL(None)
|
|
def no_privileges():
|
|
if libc.prctl(38,1,0,0,0)!=0: os._exit(111)
|
|
log=(root/'chronyd.log').open('wb')
|
|
daemon=subprocess.Popen([str(c01_nts.CHRONYD),'-x','-U','-n','-u',c01_nts.OBSERVER_USER,
|
|
'-f',str(c01_nts.CONFIG)],env={'PATH':'/usr/bin:/bin','LC_ALL':'C'},stdout=log,stderr=log,
|
|
preexec_fn=no_privileges)
|
|
failures=[]
|
|
try:
|
|
end=time.monotonic()+240
|
|
while time.monotonic()<end:
|
|
assert daemon.poll() is None, 'dedicated observer exited'
|
|
result=subprocess.run(['/usr/local/bin/archipelago','c01-observe-nts-v1'],capture_output=True,text=True,timeout=10)
|
|
if result.returncode==0:
|
|
observation=json.loads(result.stdout)
|
|
status=Path(f'/proc/{daemon.pid}/status').read_text()
|
|
fields=dict(line.split(':',1) for line in status.splitlines() if ':' in line)
|
|
assert fields['NoNewPrivs'].strip()=='1'
|
|
assert all(int(fields[k],16)&(1<<25)==0 for k in ('CapEff','CapPrm','CapAmb'))
|
|
record={'observation':observation,'native_sha256':hashlib.sha256(Path('/usr/local/bin/archipelago').read_bytes()).hexdigest(),
|
|
'prior_refusals':failures,'daemon_no_new_privileges':True,'daemon_has_sys_time':False,
|
|
'recovery_grant_issued':False,'platform_suspend_qualified':False,
|
|
'egress_rules':subprocess.check_output(['/usr/sbin/nft','list','ruleset'],text=True)}
|
|
(root/'result.json').write_text(json.dumps(record,indent=2)+'\n')
|
|
print(json.dumps(record),flush=True)
|
|
break
|
|
failures.append(result.stderr[-2000:])
|
|
time.sleep(4)
|
|
else:
|
|
(root/'result.json').write_text(json.dumps({'status':'unavailable','failures':failures},indent=2)+'\n')
|
|
raise RuntimeError('fresh native NTS observation unavailable within240s')
|
|
finally:
|
|
daemon.terminate()
|
|
daemon.wait(timeout=10)
|
|
log.close()
|