diff --git a/app.py b/app.py index c50cede..df36658 100644 --- a/app.py +++ b/app.py @@ -2023,6 +2023,7 @@ def _ssh(host, cmd, timeout=600): return 124, "", f"SSH command timed out after {timeout} seconds" +PAPERLESS_GATEWAY = AUTOMATION1 PAPERLESS_SSH = "sascha@10.5.1.120" PAPERLESS_CONSUME_DIR = "/app-config/paperless/consume" PAPERLESS_MAX_IMPORT_BYTES = 50 * 1024 * 1024 @@ -2049,6 +2050,16 @@ def _paperless_import_filename(filename: str, digest: str) -> str: return f"{stem[:100]}-{digest[:12]}.pdf" +def _paperless_gateway_command(command: str) -> str: + """Route through automation1, whose deployment key is authorized on Homelab VMs.""" + if "'" in command: + raise ValueError("Paperless remote command contains an unsafe quote") + return ( + "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new " + f"-o UserKnownHostsFile=/tmp/paperless_known_hosts {PAPERLESS_SSH} '{command}'" + ) + + @app.post("/paperless/import") async def paperless_import(request: Request, filename: str = Query(..., min_length=1, max_length=180), _=Depends(_verify)): """Queue a PDF in Paperless without exposing Paperless or SSH to the caller.""" @@ -2065,7 +2076,9 @@ async def paperless_import(request: Request, filename: str = Query(..., min_leng f"tmp=$(mktemp /tmp/paperless-import.XXXXXX) && " f"cat > \"$tmp\" && sudo -n install -o sascha -g sascha -m 0644 \"$tmp\" {remote_path} && rm -f \"$tmp\"" ) - rc, out, err = await asyncio.to_thread(_ssh_bytes, PAPERLESS_SSH, command, payload, 180) + rc, out, err = await asyncio.to_thread( + _ssh_bytes, PAPERLESS_GATEWAY, _paperless_gateway_command(command), payload, 180 + ) if rc != 0: raise HTTPException(502, (err or out).strip()[-500:] or "Paperless import transfer failed") _audit("/paperless/import", "POST", 202, f"sha256={digest}; bytes={len(payload)}") @@ -2081,7 +2094,9 @@ async def paperless_import_status(filename: str = Query(..., min_length=1, max_l f"if sudo -n test -f {remote_path}; then echo QUEUED; else echo CONSUMED; fi; " f"sudo -n docker logs --since 15m paperless-ngx 2>&1 | grep -F -- {filename} | tail -20 || true" ) - rc, out, err = await asyncio.to_thread(_ssh, PAPERLESS_SSH, command, 45) + rc, out, err = await asyncio.to_thread( + _ssh, PAPERLESS_GATEWAY, _paperless_gateway_command(command), 45 + ) if rc != 0: raise HTTPException(502, (err or out).strip()[-500:] or "Paperless status check failed") lines = out.splitlines() diff --git a/tests/test_app.py b/tests/test_app.py index 67c0621..161ded6 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -65,7 +65,8 @@ def test_paperless_import_queues_pdf_through_butler(monkeypatch): body = response.json() assert body["status"] == "queued" assert body["filename"].startswith("Hausordnung_Stand_09.05.2022-") - assert captured["host"] == app.PAPERLESS_SSH + assert captured["host"] == app.PAPERLESS_GATEWAY + assert app.PAPERLESS_SSH in captured["command"] assert captured["payload"] == pdf assert app.PAPERLESS_CONSUME_DIR in captured["command"]