Dropbox & Proxy Services
The Dropbox subsystem provides a self-hosted, stealthy TLS proxy that sits in front of a filebrowser instance, handling authentication, TLS termination, and HTTP-to-HTTPS redirection. It operates as a single-port listener that inspects incoming connections to distinguish between TLS handshakes and plain HTTP, routing them to appropriate handlers. The service is designed to be stateless regarding client connections but relies on local file-based state for secrets and credentials, coordinated with the broader autosre configuration system.
Configuration and State Coordination
Section titled “Configuration and State Coordination”Configuration is managed through the DropboxConfig dataclass, which serves as the single source of truth for paths, ports, and tunables 1. The configuration resolution follows a strict precedence order: built-in defaults, followed by a TOML file (defaulting to $XDG_CONFIG_HOME/autosre/dropbox.toml or an explicit --config-file path), and finally environment variables prefixed with AUTOSRE_DROPBOX_. This loader is consumed by the proxy, installer, CLI, and tests to ensure uniform behavior.
The proxy relies on several local files for state and credentials, all derived from a base data_dir (default /data/dropbox). These include:
- TLS Material:
cert.pemandkey.pemlocated in thetls_dir. - Secrets: An HMAC signing secret (
proxy-secret) in theconfig_dir, generated if missing with mode0600to prevent local reading 2. - Credentials: The admin password (
admin-password) in theconfig_dir, read on each login attempt. - Filebrowser State: The database file (
filebrowser.db) in theconfig_dir1.
The state_dir is also defined in the configuration but is not explicitly used by the proxy code itself in the provided sources, though it is part of the resolved configuration structure.
TLS Handling and Stealth Proxy Logic
Section titled “TLS Handling and Stealth Proxy Logic”The proxy listens on a single port (default 8443) and performs “stealth” TLS handling by peeking at the first byte of every accepted TCP connection 3. If the first byte is 0x16 (TLS ClientHello), the connection is wrapped in an SSL context and processed as TLS. If the first byte is anything else, the connection is treated as plain HTTP, and the proxy responds with a 301 Moved Permanently redirect to the HTTPS version of the requested URL.
The proxy is designed to be “stealthy”: it sends no Server header, omits body content on errors, and presents a minimal login page consisting only of a password input field with no labels, CSS, or title.
Authentication is handled via an HMAC-signed cookie. Upon a successful POST to /__auth with the correct password, the proxy issues a cookie named dbx containing an expiration timestamp and an HMAC-SHA256 signature 2. Subsequent requests are validated by verifying this cookie against the local secret file.
Upstream Proxying and Health Checks
Section titled “Upstream Proxying and Health Checks”Authenticated requests are reverse-proxied to the upstream filebrowser instance running on 127.0.0.1 at upstream_port (default 18443) 1. The proxy streams request bodies verbatim to the upstream, preserving Transfer-Encoding headers to support large uploads without buffering entire files in memory 2. It strips hop-by-hop headers (e.g., Connection, Keep-Alive) before forwarding.
An unauthenticated health probe endpoint is available at /_health. This endpoint performs an internal HEAD request to the upstream filebrowser. It returns 204 No Content if the upstream responds, or 503 Service Unavailable if the upstream is unreachable or times out. This endpoint is used by kubelet exec probes for readiness and liveness checks.
Systemd Integration and k3s Coordination
Section titled “Systemd Integration and k3s Coordination”The proxy is designed to be run as a systemd service, with the configuration loader supporting an explicit --config-file argument or the AUTOSRE_DROPBOX_CONFIG_FILE environment variable, allowing systemd units to point to a runbook-installed TOML file 1. The service binds to 0.0.0.0 by default, making it accessible from outside the host.
In a k3s environment, the proxy runs on the node and exposes the filebrowser service. The health probe endpoint (/_health) is critical for k3s to monitor the proxy’s health, as it validates both the proxy’s TLS listener and the upstream filebrowser’s availability 2. The proxy’s state (secrets, passwords, TLS certs) is stored on the node’s filesystem, meaning it is not distributed across the cluster but is local to the node running the proxy instance.
"""Dropbox subsystem configuration.
Single source of truth for every dropbox path, port, and tunable.
Loaded via ``DropboxConfig.load()`` and consumed by ``proxy.py``,
``installer.py``, ``filebrowser.py``, the CLI, and the tests.
Resolution order (low → high precedence):
1. Built-in defaults
2. TOML file at ``$XDG_CONFIG_HOME/autosre/dropbox.toml`` (or an explicit
``--config-file`` path); missing files are ignored silently
3. Environment variables (``AUTOSRE_DROPBOX_*``)
There is intentionally no ``from_env()`` shortcut or any other constructor.
Every entry point - proxy ``__main__``, installer, CLI commands, tests -
goes through ``DropboxConfig.load`` so the precedence rules apply uniformly.
Passwords never appear on this dataclass. Credential handling lives in
``autosre.dropbox.credentials``.
"""
from __future__ import annotations
import os
import tomllib
from dataclasses import dataclass, field, replace
from pathlib import Path
from typing import Any
from autosre.paths import config_dir as autosre_config_dir
DEFAULT_DATA_DIR = Path("/data/dropbox")
DEFAULT_LISTEN_PORT = 8443
DEFAULT_UPSTREAM_PORT = 18443
DEFAULT_BIND_ADDR = "0.0.0.0" # noqa: S104 - public bind is the entire point
DEFAULT_COOKIE_TTL_SECONDS = 7 * 24 * 3600
DEFAULT_FILEBROWSER_BIN = "filebrowser"
def _default_config_file() -> Path:
"""Default TOML location: ``<XDG config>/autosre/dropbox.toml``."""
def _build_response(
status: str,
body: bytes = b"",
extra_headers: list[tuple[str, str]] | None = None,
content_type: str = "text/html; charset=utf-8",
) -> bytes:
headers = [
f"HTTP/1.1 {status}",
f"Content-Length: {len(body)}",
f"Content-Type: {content_type}",
"Connection: close",
"Cache-Control: no-store",
"X-Content-Type-Options: nosniff",
]
for key, value in extra_headers or []:
headers.append(f"{key}: {value}")
return ("\r\n".join(headers) + "\r\n\r\n").encode() + body
async def _read_http_head(reader: asyncio.StreamReader) -> tuple[bytes, bytes] | None:
"""Read the request head only; return ``(head, surplus)``.
``surplus`` is whatever body bytes arrived in the same reads as the
head. The body itself is never buffered here: uploads are the whole
point of the dropbox and can be multi-GB, so the proxy path streams
them to the upstream instead (:func:`_proxy_to_upstream`).
"""
buf = bytearray()
while b"\r\n\r\n" not in buf:
chunk = await reader.read(4096)
if not chunk:
return None
buf.extend(chunk)
if len(buf) > MAX_REQUEST_BYTES:
return None
head, _, rest = buf.partition(b"\r\n\r\n")
return bytes(head), bytes(rest)
def _content_length(head: bytes) -> int:
"""Dropbox stealth TLS / HTTP-redirect proxy.
Listens on a single port (default ``8443``). Peeks the first byte of every
accepted TCP connection:
- TLS (``0x16``): terminates TLS and reverse-proxies authenticated HTTP to
the upstream filebrowser. Unauthenticated requests get a bare-bones
``<input type=password>`` login page; a successful POST to ``/__auth``
sets an HMAC-signed cookie and redirects to ``/``.
- Plain HTTP (any other byte): responds with ``301 Moved Permanently`` to
``https://<host>:<listen_port>/<path>`` so users hitting the wrong
scheme on the right port get redirected instead of a TLS handshake error.
Stealth choices: no ``Server`` header, no body on errors, login page is a
single password input with no labels, no CSS, no title.
Configuration is loaded via :class:`autosre.dropbox.config.DropboxConfig`;
no module-level constants other than the wire-protocol literals.
"""
from __future__ import annotations
import asyncio
import contextlib
import hashlib
import hmac
import secrets
import socket
import ssl
import time
import urllib.parse
from autosre.dropbox.config import DropboxConfig
# Wire-protocol literals (intentionally not configurable)
COOKIE_NAME = "dbx"
LOGIN_PATH = "/__auth"
HEALTH_PATH = "/_health"
HEALTH_PROBE_TIMEOUT = 1.0
LOGIN_HTML: bytes = (