Fail closed when ALLOWED_HOSTS is not set in production - #2308
Conversation
…wed-hosts-default # Conflicts: # gateway/tests/main/test_settings.py
…wed-hosts-default # Conflicts: # gateway/tests/main/test_settings.py
…wed-hosts-default # Conflicts: # charts/qiskit-serverless/charts/gateway/values.yaml # docker-compose.yaml # gateway/tests/main/test_settings.py # gateway/tox.ini
korgan00
left a comment
There was a problem hiding this comment.
LGTM, just a small question
| _allowed_hosts = os.environ.get("ALLOWED_HOSTS") | ||
| if not _allowed_hosts: | ||
| if DEBUG or IS_TEST: | ||
| _allowed_hosts = "*" | ||
| else: | ||
| raise ImproperlyConfigured("ALLOWED_HOSTS environment variable must be set when DEBUG is disabled.") | ||
| ALLOWED_HOSTS = _allowed_hosts.split(",") |
There was a problem hiding this comment.
Should we check if _allowed_hosts is *?
There was a problem hiding this comment.
I get your point, I guess you're suggesting something like this:
if DEBUG or IS_TEST:
if not _allowed_hosts:
_allowed_hosts = "*"
else:
if not _allowed_hosts:
raise ImproperlyConfigured("ALLOWED_HOSTS environment variable must be set when DEBUG is disabled.")
else:
if _allowed_hosts == "*":
raise ImproperlyConfigured("ALLOWED_HOSTS environment variable can't be * in production environment")Right? so it rejects an explicit * with DEBUG/IS_TEST off, which sounds ok... but this is something different of the main purpose of this PR, which it just fails when ALLOWED_HOSTS is missing in production, not checking the content...
If we want validate the content, forbidding * in production, it's more than just check if _allowed_host is *:
- docker-compose.yaml in this PR sets DEBUG=0 and ALLOWED_HOSTS=* on purpose for the gateway and scheduler services, so that would need to change first (a real host, or drop back to DEBUG=1 there), otherwise this check crashes both containers on startup. Do we want DEBUG=1 or force localhost?
- The staging/production deployments charts don't set allowedHosts explicitly and currently fallback to the chart's own wildcard default rather than an explicit value, so this check wouldn't catch them as-is.
So, I would keep this PR scoped to "fail closed on missing ALLOWED_HOSTS" as it is right now... and handle "reject a * value in production" PR as a follow-up, because it will require configuring the production/staging environment with the right value first before rejecting the *...
…wed-hosts-default # Conflicts: # gateway/tests/main/test_settings.py
Summary
Require an explicit ALLOWED_HOSTS in production instead of accepting any host by default.
Accepting any Host header ("*") opens the app to Host header attacks, such as cache poisoning and password reset links that point at an attacker domain. This makes the gateway fail closed: when DEBUG is off and ALLOWED_HOSTS is not set, it refuses to start, so an environment cannot silently run wide open. It matches how the missing secret key is already handled.
Details and comments
Changes in a few layers:
Impact for existing users: a deployment that relied on the old "*" default must now set the host list it serves (application.allowedHosts in the chart, or the ALLOWED_HOSTS env var), or the gateway will not start. This is the intended fail closed behavior.