"""Finite native loopback interfaces. A URL override is never rewritten here. The table is an interface inventory, not evidence a service is installed/ready. App-port policies additionally require a root-qualified manifest projection. """ NATIVE = { 'management': ('host', 'managed', 'tcp', 5678), 'kiosk-http': ('runtime', 'host', 'tcp', 80), 'kiosk-https': ('runtime', 'host', 'tcp', 443), 'bitcoin-rpc': ('managed', 'runtime', 'tcp', 8332), 'lnd-rest': ('managed', 'runtime', 'tcp', 18080), 'lnd-grpc': ('managed', 'runtime', 'tcp', 10009), 'dwn': ('managed', 'runtime', 'tcp', 3100), 'tor-socks': ('managed', 'host', 'tcp', 9050), 'fips-dns': ('managed', 'host', 'udp', 5354), 'fips-tcp6': ('host', 'host', 'tcp', 8443), 'indeehub': ('managed', 'runtime', 'tcp', 7778), 'ollama': ('managed', 'runtime', 'tcp', 11434), 'routstr': ('managed', 'runtime', 'tcp', 8200), 'fedimint': ('managed', 'runtime', 'tcp', 8175), 'searxng': ('managed', 'runtime', 'tcp', 8888), } NATIVE_APPS={'bitcoin-rpc':{'bitcoin-core','bitcoin-knots'},'lnd-rest':{'lnd'},'lnd-grpc':{'lnd'}, 'dwn':{'dwn'},'indeehub':{'indeedhub-api'},'ollama':{'ollama'},'routstr':{'routstr'}, 'fedimint':{'fedimint'},'searxng':{'searxng'}} def source_addresses(route): return route.get('source_addresses',['127.0.0.1']) def target_address(route): return route.get('target_address','127.0.0.1') def target_port(route): return route.get('target_port',route['port']) def source_binding_conflict(left,right): a,b=left[3],right[3] return left[:3]==right[:3] and (a==b or ((':' in a)==(':' in b) and (a in ('0.0.0.0','::') or b in ('0.0.0.0','::')))) def validate_route(route, manifests, runtime=None, address_version=1): import ipaddress import re fields={'name','source','target','protocol','port','app_id','projection_sha256','target_cgroup','target_uid'} if address_version in (3,4):fields.update(('source_addresses','target_address')) elif address_version!=1:raise ValueError('endpoint address schema version') if address_version==4:fields.add('target_port') if type(route) is not dict or set(route)!=fields: raise ValueError('endpoint exact fields') name=route['name'] if type(name) is not str:raise ValueError('endpoint name type') gate=address_version==4 and name.startswith('gate/') native_relay=address_version==4 and name.startswith('native/') publication=address_version==4 and route['target']=='runtime' if type(target_port(route)) is not int or not 1<=target_port(route)<=65535:raise ValueError('target port bound') if address_version in (3,4): addresses=route['source_addresses'] allowed=('127.0.0.1','127.0.0.2') target_allowed=(*allowed,'::1') if native_relay else allowed ingress=gate or native_relay or publication or name=='fips-tcp6' if (type(addresses) is not list or not 1<=len(addresses)<=(16 if ingress else 2) or any(type(a) is not str or (not ingress and a not in allowed) for a in addresses) or len(set(addresses))!=len(addresses) or type(route['target_address']) is not str or (not publication and route['target_address'] not in target_allowed)): raise ValueError('only explicitly appointed local loopback addresses are supported') if ingress: for address in addresses: ip=ipaddress.ip_address(address) wildcard=publication or name.startswith('native/mdns') or name=='fips-tcp6' if str(ip)!=address or (ip.is_unspecified and not wildcard) or ip.is_multicast or ip.is_link_local or '%' in address: raise ValueError('ingress requires exact unicast host addresses') if publication: ip=ipaddress.ip_address(route['target_address']) if str(ip)!=route['target_address'] or ip.is_unspecified or ip.is_multicast or ip.is_link_local or '%' in route['target_address']: raise ValueError('runtime target requires an exact address') if name in NATIVE and addresses!=(['::'] if name=='fips-tcp6' else ['127.0.0.1']): raise ValueError('native client interface address changed') if route['target']!='runtime' and not (gate or native_relay) and route['target_address']!='127.0.0.1': raise ValueError('host/management native endpoint address changed') if name in NATIVE: if tuple(route[k] for k in ('source','target','protocol','port'))!=NATIVE[name]: raise ValueError('native endpoint interface changed') if name in NATIVE_APPS and route['app_id'] not in NATIVE_APPS[name]:raise ValueError('native endpoint application differs') if target_port(route)!=route['port']:raise ValueError('native endpoint port changed') elif native_relay: match=re.fullmatch(r'native/(peer|website-fips|website-tor|mdns4|mdns6)/(tcp|udp)/([0-9]{1,5})/host',name) if not match or not 1<=int(match[3])<=65535:raise ValueError('fixed native relay name') protocol='udp' if match[1].startswith('mdns') else 'tcp' if match[2]!=protocol:raise ValueError('native relay protocol') if match[1].startswith('website-'): port=int(match[3]);start=32000 if match[1]=='website-fips' else 32100 if not start<=port={'source_bindings','target_addresses','producer'} external=any(a not in ('127.0.0.1','127.0.0.2') for a in addresses) or route['target_address'] not in ('127.0.0.1','127.0.0.2') if external and not extended:raise ValueError('runtime external publication needs qualified bindings') if extended: if any([route['source'],route['protocol'],route['port'],a] not in doc['source_bindings'] for a in addresses) or route['target_address'] not in doc['target_addresses']: raise ValueError('runtime route exceeds qualified address bindings') elif route['app_id'] is not None or route['projection_sha256'] is not None: raise ValueError('non-runtime route has application projection') elif route['name']=='management' or native_relay: if route['target_cgroup']!='/system.slice/archipelago.service/c01-application' or route['target_uid']==0: raise ValueError('management listener appointment') if native_relay and (runtime is None or route['target_uid']!=runtime['manager_uid']): raise ValueError('native relay application identity') elif not route['target_cgroup'].startswith('/system.slice/'): raise ValueError('host service hierarchy') if name=='tor-socks' and route['target_cgroup'].split('/')[-1] not in ('tor.service','tor@default.service'): raise ValueError('Tor service identity') if name=='fips-dns' and route['target_cgroup']!='/system.slice/archipelago-fips.service':raise ValueError('FIPS service identity') if name=='fips-tcp6' and (address_version!=4 or route['target_cgroup']!='/system.slice/archipelago-fips.service' or route['target_uid']!=0):raise ValueError('FIPS transport producer identity') if not re.fullmatch(r'/[A-Za-z0-9_.@/-]{1,240}',route['target_cgroup']) or '..' in route['target_cgroup'].split('/'): raise ValueError('endpoint cgroup path') return route