Open micahflee opened 1 month ago
I have this snipped in another project and it would obviously need modifying, but it could be set up to a single app like this. It's a few years old and from the Flask 2.x release series, so idk if there's a better way to do it.
class DomainDispatcher:
def __init__(self, domains):
self.domains = domains
def __call__(self, environ, start_response):
request_host = environ['HTTP_HOST']
for (domain, app) in self.domains:
if request_host.endswith(domain):
return app(environ, start_response)
# TODO instead of aborting 503, replace with call to simple app that renders a
# simple page "service not available" static page.
# nginx should redirect to this too when the service is down
abort(503)
def create_app(config: Config = None) -> DomainDispatcher:
if config is None:
config = Config()
domains = []
for domain in config.domains:
# make a copy so we can fiddle with some of the options for this sub-app
domains.append((domain, make_app(copy.deepcopy(config), domain)))
return DomainDispatcher(domains)
def make_app(config: Config, domain: str = None) -> Flask:
# normal app.route, app.config, etc. stuff here
The disadvantage of this is I had some dangerous mangling of the Config
in make_app
where I was doing things like if domain.endswith(".onion"): ...
and then tweaking things.
I actually think the best way to deploy this would be using two containers each with fully separate (but in some cases copy/pasted) configs because for example in #626 there almost certainly would be a difference in the proxy config for clearnet vs. Tor which would require a totally different Config
(and/or set of env vars), and no tricky mangling within make_app
would be able to account for this without being highly brittle and tied to our particular setup.
I started working on this and immediately ran into some problems. There are some config values that need to be different between the clearnet and Tor versions of the app if we use the above model, e.g., PREFERRED_URL_SCHEME
. This one config could be mostly reliably detected by matching server_name.endswith('.onion')
but also onion services are allowed to be HTTPS if they pay for verification. Granted, we don't support that yet and that's okay maybe, except in the next release we're already doing to have to make a breaking config change, so we might as well think carefully for a moment.
If we want to have the app be able to serve multiple domains, we need to do one of the following:
To support multiple domains, we first require an env var like DOMAIN_MAP
structured like this:
DOMAIN_MAP="PREFIX_1:example.com , PREFIX_2:example.onion , etc."
We then parse this first, then per domain, parse configs like:
# prefix being e.g., `PREFIX_1` from the above example
def load_config(prefix: str) -> dict[str, Any]:
cfg_value = os.environ.get(prefix + "_" + "ACTUAL_CONFIG_NAME", None)
... # etc.
Many of these values are related as in there's no reason multiple domains would need to set a different value for whether or not premium/stripe is set. So we have shared + overrides like:
# using TOML because it's easier to type, but we might use YAML idk
shared_config_1 = "foo"
shared_config_2 = 123
[domains."example.com"]
# override
shared_config_1 = "bar"
[domains."example.onion"]
some_other_config = false
For the Debian package (hardware) or containers (DO/hosted), we just make two apps. Two systemd units, two containers, whatever. All the app code stays the same (modulo any bugs related to clearnet / onion, etc.).
Describe the bug The onion service for https://tips.hushline.app/ is http://hyewn4dvbedq7ooe3oxrhpceljd7ncfyeyts2c7nwsjp34i46smbzwid.onion/, however it seems that if you load the site using the onion service hostname it auto-redirects to
SERVER_NAME
, which istips.hushline.app
.To Reproduce Steps to reproduce the behavior:
Open Tor Browser, load https://tips.hushline.app/, and click ".onion available". It loads the onion service, which redirects back to https://tips.hushline.app/.
Expected behavior You should be able to use the onion service.