55 lines
3.7 KiB
Python
55 lines
3.7 KiB
Python
import datetime,hashlib,json,os,re,stat,subprocess,socket
|
|
from pathlib import Path
|
|
|
|
def attrs(path, content=False):
|
|
p=Path(path)
|
|
try:
|
|
s=p.lstat()
|
|
out={'exists':True,'uid':s.st_uid,'gid':s.st_gid,'mode':oct(stat.S_IMODE(s.st_mode)),
|
|
'regular':stat.S_ISREG(s.st_mode),'symlink':stat.S_ISLNK(s.st_mode),'device':s.st_dev,'inode':s.st_ino}
|
|
if content and stat.S_ISREG(s.st_mode):
|
|
try:
|
|
raw=p.read_bytes();out['sha256']=hashlib.sha256(raw).hexdigest()
|
|
text=raw.decode('utf-8',errors='replace')
|
|
out['mode_flags']=re.findall(r'--write-kubeconfig-mode(?:=|\s+)[\x22\x27]?(0?[0-7]{3})\b',text)
|
|
out['mode_config_values']=re.findall(r'^\s*write-kubeconfig-mode\s*:\s*[\x22\x27]?(0?[0-7]{3})\b',text,re.M)
|
|
out['execstart_count']=len(re.findall(r'^ExecStart=',text,re.M))
|
|
out['has_mode_env_assignment']=bool(re.search(r'K3S_KUBECONFIG_MODE\s*=',text))
|
|
out['readable']=True
|
|
except (OSError,ValueError):out['readable']=False
|
|
return out
|
|
except FileNotFoundError:return {'exists':False}
|
|
except OSError:return {'metadata_accessible':False}
|
|
|
|
network=subprocess.run(['ip','-j','address','show','scope','global'],capture_output=True,text=True,timeout=10)
|
|
try:
|
|
addresses=[a.get('local') for interface in json.loads(network.stdout) for a in interface.get('addr_info',[])]
|
|
expected_ip_present=network.returncode==0 and '92.205.62.239' in addresses
|
|
except (ValueError,TypeError):expected_ip_present=False
|
|
host_identity={'ssh_alias':'railiance01','expected_ipv4':'92.205.62.239',
|
|
'expected_ipv4_present':expected_ip_present,'hostname':socket.gethostname(),
|
|
'machine_id_sha256':hashlib.sha256(Path('/etc/machine-id').read_bytes()).hexdigest(),
|
|
'remote_captured_at':datetime.datetime.now(datetime.timezone.utc).isoformat()}
|
|
result={'host_identity':host_identity,'read_only':True,'sudo_used':False,'credential_file_content_read':False,'uid':os.getuid(),'service':{}}
|
|
for prop in ('LoadState','ActiveState','SubState','MainPID','FragmentPath','DropInPaths','NeedDaemonReload'):
|
|
p=subprocess.run(['systemctl','show','k3s','--property='+prop,'--value'],capture_output=True,text=True,timeout=10)
|
|
value=p.stdout.strip()
|
|
if prop in ('FragmentPath','DropInPaths'):
|
|
paths=value.split()
|
|
safe=all(re.fullmatch(r'/[A-Za-z0-9_./@+-]+',x) for x in paths)
|
|
result['service'][prop]=paths if p.returncode==0 and safe else 'unavailable'
|
|
else:
|
|
result['service'][prop]=value if p.returncode==0 and re.fullmatch(r'[A-Za-z0-9_-]*',value) else 'unavailable'
|
|
p=subprocess.run(['systemctl','show','k3s','--property=ExecStart','--value'],capture_output=True,text=True,timeout=10)
|
|
result['service']['effective_mode_flags']=re.findall(r'--write-kubeconfig-mode(?:=|\s+)[\x22\x27]?(0?[0-7]{3})\b',p.stdout) if p.returncode==0 else None
|
|
result['files']={name:attrs(name,content) for name,content in [('/etc/systemd/system/k3s.service',True),('/etc/rancher/k3s/config.yaml',True),('/etc/rancher/k3s/k3s.yaml',False),('/etc/systemd/system/k3s.service.env',False)]}
|
|
for directory in ('/etc/systemd/system/k3s.service.d','/etc/rancher/k3s/config.yaml.d'):
|
|
d=Path(directory)
|
|
try:
|
|
entries=list(d.iterdir()) if d.exists() else []
|
|
result['directories' if 'directories' in result else 'directories']=result.get('directories',{})
|
|
result['directories'][directory]={'exists':d.exists(),'entry_count':len(entries),'metadata_accessible':True}
|
|
for child in entries:
|
|
if re.fullmatch(r'[A-Za-z0-9_.+-]+',child.name):result['files'][str(child)]=attrs(child,True)
|
|
except OSError:result.setdefault('directories',{})[directory]={'metadata_accessible':False}
|
|
print(json.dumps(result,sort_keys=True))
|