Package the service for setup on a new machine: - install.sh generates a token, writes share.config (mode 600, token stored as a SHA-256 hash) and installs the systemd user unit from share.service.template - share.config is now gitignored, with share.config.example as the tracked template — the config holds the secret and must not be in the repo - warn at startup when token is empty, since that means open access - SETUP.md documents install, verification and security posture
68 lines
2.2 KiB
Python
Executable File
68 lines
2.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Share — local file sharing server (HTTPS)."""
|
|
|
|
import hashlib
|
|
import json
|
|
import ssl
|
|
import sys
|
|
import threading
|
|
from pathlib import Path
|
|
|
|
from cert import ensure_cert, get_local_ip
|
|
from cleanup import cleanup_loop
|
|
from config import BASE_DIR, SECS_PER_DAY, client_cfg, load_config
|
|
from server import Handler, ShareServer
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) > 1 and sys.argv[1] == "hash":
|
|
token = sys.argv[2] if len(sys.argv) > 2 else input("Token: ")
|
|
print(hashlib.sha256(token.encode()).hexdigest())
|
|
return
|
|
|
|
conf = load_config()
|
|
|
|
port = int(conf["port"])
|
|
upload_dir = Path(conf["dir"]).expanduser().resolve()
|
|
max_age = int(conf["ttl"]) * SECS_PER_DAY
|
|
san_list = conf["san"].split() if conf["san"] else None
|
|
|
|
upload_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
threading.Thread(target=cleanup_loop, args=(upload_dir, max_age), daemon=True).start()
|
|
|
|
ip = get_local_ip()
|
|
cfg_tag = f"<script>const CFG={json.dumps(client_cfg(conf))}</script>".encode()
|
|
html = (BASE_DIR / "static" / "index.html").read_bytes()
|
|
html = html.replace(b"<!--CFG-->", cfg_tag)
|
|
|
|
if conf["cert"] and conf["key"]:
|
|
cert, key = Path(conf["cert"]), Path(conf["key"])
|
|
else:
|
|
cert_dir = BASE_DIR / ".certs"
|
|
cert, key = ensure_cert(cert_dir, extra_ips=san_list)
|
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
ctx.load_cert_chain(cert, key)
|
|
|
|
token = conf["token"]
|
|
if not token:
|
|
print("\n WARNING: no token set — anyone on the network has full access.")
|
|
print(" Set `token` in share.config (see SETUP.md) or pass --token.")
|
|
# If token looks like a SHA-256 hash, pass it as pre-hashed
|
|
is_hash = len(token) == 64 and all(c in "0123456789abcdef" for c in token)
|
|
server = ShareServer(("0.0.0.0", port), Handler, upload_dir=upload_dir, html=html, ssl_ctx=ctx, token=token, token_is_hash=is_hash)
|
|
|
|
print("\n Share running")
|
|
print(f" Local: https://localhost:{port}")
|
|
print(f" Network: https://{ip}:{port}")
|
|
print(f" Files: {upload_dir}\n")
|
|
try:
|
|
server.serve_forever()
|
|
except KeyboardInterrupt:
|
|
print("\n Stopped.")
|
|
server.server_close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|