{"id":7730,"library":"snitun","title":"SNI Proxy with TCP Multiplexer","description":"Snitun is a Python library that provides a Server Name Indication (SNI) proxy with TCP multiplexing capabilities, useful for routing traffic based on the SNI header. It is actively maintained by NabuCasa (the developers behind Home Assistant), currently at version 0.45.2, with a steady release cadence.","status":"active","version":"0.45.2","language":"en","source_language":"en","source_url":"https://github.com/NabuCasa/snitun.git","tags":["proxy","tls","sni","asyncio","network","multiplexer"],"install":[{"cmd":"pip install snitun","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Requires Python 3.12 or higher as per project metadata.","package":"python","optional":false},{"reason":"Core dependency for asynchronous HTTP client/server functionality.","package":"aiohttp","optional":false},{"reason":"Used for cryptographic operations, particularly TLS/SSL.","package":"cryptography","optional":false},{"reason":"Used for generating test certificates, not a runtime dependency for deployment but useful for local testing setups.","package":"trustme","optional":true},{"reason":"Optional dependency for a faster `asyncio` event loop implementation.","package":"uvloop","optional":true}],"imports":[{"symbol":"Config","correct":"from snitun.config import Config"},{"symbol":"SnitunServer","correct":"from snitun.server import SnitunServer"}],"quickstart":{"code":"import asyncio\nimport os\nfrom snitun.config import Config\nfrom snitun.server import SnitunServer\n\n# In a real-world scenario, you would provide paths to your\n# actual TLS server certificate and key files.\n# For this quickstart, we use placeholder paths. \n# Running this code as-is will likely fail unless these files exist\n# and contain valid cert/key pairs for a TLS server.\n# You can generate dummy ones or provide real paths via environment variables.\n# Example: SNITUN_SERVER_CERT=./server.pem SNITUN_SERVER_KEY=./server.key python your_script.py\nDUMMY_CERT = os.environ.get(\"SNITUN_SERVER_CERT\", \"/path/to/server.pem\")\nDUMMY_KEY = os.environ.get(\"SNITUN_SERVER_KEY\", \"/path/to/server.key\")\n\nasync def main():\n    # Define the Snitun configuration\n    config = Config(\n        listen_host=\"127.0.0.1\",\n        listen_port=8443,\n        server_certs=DUMMY_CERT, # Required for TLS\n        server_key=DUMMY_KEY,    # Required for TLS\n        routes={\n            \"example.com\": {  # SNI hostname to route\n                \"host\": \"192.168.1.100\", # Target host\n                \"port\": 443,             # Target port\n                \"no_verify_ssl\": False   # Verify upstream SSL certs\n            },\n            \"another.example.org\": {\n                \"host\": \"127.0.0.1\",\n                \"port\": 8080,\n                \"no_verify_ssl\": True\n            }\n        }\n    )\n\n    # Create an instance of the Snitun server\n    server = SnitunServer(config)\n\n    print(f\"Snitun server configured to listen on {config.listen_host}:{config.listen_port}\")\n    print(f\"Routes defined: {list(config.routes.keys())}\")\n    print(\"\\nNOTE: To actually run and test this server, you must ensure valid TLS certificate and key files \")\n    print(\"       are accessible at the configured `server_certs` and `server_key` paths.\")\n    print(\"       See Snitun documentation for proper setup.\")\n    print(\"\\nTo start the server (after ensuring valid certs/keys):\")\n    print(\"  await server.start()\")\n    print(\"  await asyncio.Future() # Keep running indefinitely\")\n    print(\"  await server.stop()\")\n\nif __name__ == \"__main__\":\n    asyncio.run(main())","lang":"python","description":"This quickstart demonstrates how to configure and instantiate a `SnitunServer`. It outlines the required `Config` parameters, especially for TLS certificates and routing rules. Note that running a functional TLS proxy requires actual certificate and key files, which are indicated by placeholder paths in this example."},"warnings":[{"fix":"Refactor your configuration to create a `snitun.config.Config` instance and pass it to `SnitunServer`. E.g., `config = Config(...); server = SnitunServer(config)`.","message":"Prior to version 0.37.0, configuration parameters were often passed directly to `SnitunServer` or related functions. As of 0.37.0, all configuration must be provided via a `snitun.config.Config` object, which is then passed to `SnitunServer`.","severity":"breaking","affected_versions":"<0.37.0"},{"fix":"Ensure you provide paths to valid TLS certificate (`.pem`) and private key (`.key`) files via `config.server_certs` and `config.server_key`. For production, use certificates issued by a trusted CA and secure their storage.","message":"Snitun operates as a TLS proxy, requiring valid `server_certs` and `server_key` paths in the `Config` object. Without correctly configured certificates, the server will fail to start or operate securely, leading to connection errors for clients.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure your application environment correctly manages the `asyncio` event loop. Always `await` asynchronous calls to `SnitunServer` methods like `start()` and `stop()`. Use `asyncio.run()` for top-level execution.","message":"Snitun is built entirely on `asyncio`. Users must be familiar with `async`/`await` syntax and the `asyncio` event loop for proper integration and management of the server, including starting, stopping, and handling long-running operations.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure clients are configured to send SNI. Verify client-side logs or use network sniffers to confirm SNI presence for troubleshooting routing issues.","message":"Snitun routes traffic based on the Server Name Indication (SNI) header in client TLS handshakes. If client traffic does not include an SNI header (e.g., older clients, direct IP connections), routing based on `config.routes` will not occur, and the connection might be dropped or fallback to a default if configured.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Provide valid absolute or relative paths to your TLS server certificate and private key files in the `Config` object. Ensure the files exist and `snitun` has read permissions to them.","cause":"The `server_certs` or `server_key` paths specified in the `Config` object do not point to existing, readable files containing valid TLS certificates.","error":"FileNotFoundError: [Errno 2] No such file or directory: '/path/to/server.pem' (or similar SSL error)"},{"fix":"All configuration parameters must now be encapsulated within a `snitun.config.Config` object, which is then passed as the sole argument to `SnitunServer`. For example: `config = Config(listen_host='...', ...); server = SnitunServer(config)`.","cause":"Attempting to pass configuration parameters directly to `SnitunServer` when using version 0.37.0 or newer, which expects a `Config` object.","error":"TypeError: SnitunServer.__init__() got an unexpected keyword argument 'listen_host' (or similar for other config params)"},{"fix":"Always `await` calls to `SnitunServer.start()` and `SnitunServer.stop()`. Ensure the `asyncio` event loop is running and use `await asyncio.Future()` or `asyncio.sleep()` in an infinite loop to keep the server running if it's the main task.","cause":"`SnitunServer.start()` or `SnitunServer.stop()` were called without `await`, or the `asyncio` event loop was not properly managed to keep the server task alive.","error":"RuntimeWarning: Enable tracemalloc to get the object allocation traceback (often followed by server not starting) OR Task was destroyed but it was pending!"}]}