Skip to content

Fail closed when ALLOWED_HOSTS is not set in production - #2308

Open
avilches wants to merge 16 commits into
mainfrom
security/gateway-allowed-hosts-default
Open

Fail closed when ALLOWED_HOSTS is not set in production#2308
avilches wants to merge 16 commits into
mainfrom
security/gateway-allowed-hosts-default

Conversation

@avilches

@avilches avilches commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

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:

  • App settings: when DEBUG is off, ALLOWED_HOSTS must be set. If it is missing the app raises ImproperlyConfigured at startup instead of falling back to a value. When DEBUG is on (local development and tests) it still defaults to "*".
  • Helm chart: the gateway chart default for allowedHosts is now empty instead of "*", so a chart install that does not set application.allowedHosts fails closed. The chart always passes ALLOWED_HOSTS to the app, so this default is what real deployments get.
  • gateway/tox.ini: the shared test and lint environment sets ALLOWED_HOSTS, because pylint and the import checks import the settings module, which now fails closed without it.
  • docker-compose: the services set ALLOWED_HOSTS=* so local runs keep working with DEBUG off.

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.

@avilches
avilches requested a review from a team as a code owner July 2, 2026 14:21
@avilches avilches self-assigned this Jul 2, 2026
@avilches avilches changed the title Default ALLOWED_HOSTS to localhost in production Default ALLOWED_HOSTS to a safe value instead of accepting any host Jul 2, 2026
@avilches avilches changed the title Default ALLOWED_HOSTS to a safe value instead of accepting any host Fail closed when ALLOWED_HOSTS is not set in production Jul 2, 2026
avilches and others added 7 commits July 2, 2026 17:15
…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 korgan00 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just a small question

Comment thread gateway/main/settings.py
Comment on lines +65 to +71
_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(",")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check if _allowed_hosts is *?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, probably yes 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 *...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants