- SSRF: disable automatic redirects, manually follow up to 5 hops with IP re-validation at each step to prevent redirect-to-localhost bypass - Identity file: enforce 0600 permissions on tinyweb_identity at load and creation to prevent other users from reading the private key - Error messages: replace raw exception strings with generic messages to avoid leaking internal paths/hostnames to the UI - DB connections: wrap all get_db() usage in try/finally to guarantee close() even when handlers throw mid-operation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
75 lines
2 KiB
Python
75 lines
2 KiB
Python
import os
|
|
import time
|
|
import threading
|
|
import RNS
|
|
from http.server import HTTPServer
|
|
|
|
from db import init_db, set_setting
|
|
from handlers import dispatch_request
|
|
from gateway import GatewayState, GatewayHandler, GATEWAY_PORT
|
|
|
|
APP_NAME = "tinyweb"
|
|
ASPECTS = ["server"]
|
|
IDENTITY_FILE = "tinyweb_identity"
|
|
|
|
|
|
def load_or_create_identity():
|
|
if os.path.isfile(IDENTITY_FILE):
|
|
# Ensure identity file is only readable by owner
|
|
current = os.stat(IDENTITY_FILE).st_mode & 0o777
|
|
if current != 0o600:
|
|
os.chmod(IDENTITY_FILE, 0o600)
|
|
return RNS.Identity.from_file(IDENTITY_FILE)
|
|
identity = RNS.Identity()
|
|
identity.to_file(IDENTITY_FILE)
|
|
os.chmod(IDENTITY_FILE, 0o600)
|
|
return identity
|
|
|
|
|
|
def rns_request_handler(path, data, request_id, link_id, remote_identity, requested_at):
|
|
if data is None:
|
|
data = {"method": "GET", "path": "/", "query": {}, "body": {}, "gateway_host": ""}
|
|
return dispatch_request(data)
|
|
|
|
|
|
def start_gateway(reticulum):
|
|
GatewayState.reticulum = reticulum
|
|
GatewayState.local_dispatch = dispatch_request
|
|
server = HTTPServer(("0.0.0.0", GATEWAY_PORT), GatewayHandler)
|
|
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
thread.start()
|
|
|
|
|
|
def main():
|
|
init_db()
|
|
reticulum = RNS.Reticulum()
|
|
identity = load_or_create_identity()
|
|
|
|
destination = RNS.Destination(
|
|
identity,
|
|
RNS.Destination.IN,
|
|
RNS.Destination.SINGLE,
|
|
APP_NAME,
|
|
*ASPECTS,
|
|
)
|
|
|
|
destination.register_request_handler(
|
|
"/tinyweb",
|
|
response_generator=rns_request_handler,
|
|
allow=RNS.Destination.ALLOW_ALL,
|
|
)
|
|
|
|
destination.announce()
|
|
set_setting("dest_hash", destination.hash.hex())
|
|
start_gateway(reticulum)
|
|
|
|
print(f"TinyWeb running!")
|
|
print(f"Open http://localhost:{GATEWAY_PORT} in your browser")
|
|
print(f"Destination hash: {RNS.prettyhexrep(destination.hash)} (share this so friends can subscribe)")
|
|
|
|
while True:
|
|
time.sleep(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|