【问题标题】:Block invalid HTTP_HOST headers in Apache in AWS ElasticBeanstalk在 AWS ElasticBeanstalk 中阻止 Apache 中的无效 HTTP_HOST 标头
【发布时间】:2020-12-21 13:11:17
【问题描述】:

我有几个运行 Django/Apache 的网站部署在 AWS ElasticBeanstalk 中。

我唯一的问题是我每天收到数百封关于这个主题的电子邮件:

[Django] ERROR (EXTERNAL IP): Invalid HTTP_HOST header: WHATEVER. You may need to add WHATEVER to ALLOWED_HOSTS.

那是因为我已经在我的 Django 配置中正确配置了 ALLOWED_HOSTS 变量,但我需要 Apache 阻止所有无效的 HTTP_HOST 标头,这样它们甚至不会到达 Django 并每天处理掉这数百封电子邮件。

我知道,这里和那里有很多关于如何在 Apache 中执行此操作的示例,但我还没有找到一个在 AWS ElasticBeanstalk 中部署时如何执行此操作的示例。

如果您不熟悉 AWS ElasticBeanstalk,请记住,该系统会自动创建一个 /etc/httpd/conf.d/wsgi.conf 文件,其中包含一些由 Amazon 进行的配置,并且 Amazon 可以(并且将)将来在我们的控制之外修改它们。

因此,当我们想向 Apache 添加一些配置时,例如提供重定向,首选方式是在我们的项目中提供一个 YAML 文件,该文件定义一个新的独立 Apache 配置文件,除了 @亚马逊自动创建的 987654326@ 文件,如下所示:

files:
    "/etc/httpd/conf.d/ssl_rewrite.conf":
        mode: "000644"
        owner: root
        group: root
        content: |
            RewriteEngine On
            <If "-n '%{HTTP:X-Forwarded-Proto}' && %{HTTP:X-Forwarded-Proto} != 'https'">
            RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
            </If>

这样做,一旦我们部署了我们的代码,一个/etc/httpd/conf.d/ssl_rewrite.conf文件也将被Apache创建和使用(我们没有编辑原始的wsgi.conf文件,我们只是在一个新文件中提供了更多的配置)。

这里有对这种模式的惊人解释:https://stackoverflow.com/a/38751648/1062587

这些 YAML 配置文件可以创建 Apache 配置文件或将某些内容附加到现有文件中,但不能进行替换或修改。

但是,关于我的问题,原始的wsgi.conf 文件已经提供了这样的部分:

<VirtualHost *:80>
  [...]
  <Directory /opt/python/current/app/>
    Require all granted
  </Directory>
  [...]
</VirtualHost>

由于我想阻止所有无效的主机请求,我假设我需要将其更改为类似的内容(根据此答案:https://stackoverflow.com/a/43322857/1062587):

<VirtualHost *:80>
  [...]
  <Directory /opt/python/current/app/>
    Require expr "%{HTTP_HOST} in {'whatever.com', 'www.whatever.com'}"
    Options
  </Directory>
  [...]
</VirtualHost>

我的问题是:YAML 文件无法进行编辑。我只能为他们提供新的配置。

所以,我不知道我是否可以在独立的 Apache 配置文件中提供一些配置来覆盖现有的 &lt;VirtualHost&gt; -&gt; &lt;Directory&gt; 部分,所以我可以像 ElasticBeanstalk 理解的那样在 YAML 文件中定义所需的代码。

否则,我将不得不提供一个脚本来即时进行替换。我知道该怎么做,但我觉得它很难看。我只是问是否有更优雅的解决方案。

【问题讨论】:

  • 为什么不在 ebextension 中创建 VirtualHost,ServerName 为 HTTP_HOST
  • 我对 Apache 了解不多,但是,我不需要覆盖现有的 &lt;VirtualHost *:80&gt; 指令以便阻止来自任何主机的所有内容吗?可以举个例子吗?
  • 通常情况下,您托管的每个域都有一个 VirtualHost 条目,因此这不是阻塞的情况,但包括主机/服务器。看看这个问题; stackoverflow.com/questions/21878024/…
  • 请阅读我的问题。实际上,我首先在它的中间链接了同一个线程,我什至从那里复制/粘贴了一些代码。无论如何,所有线程都在谈论可以添加的配置,所以 YAML 文件是一个简单的解决方案。然而,在我的问题中,我需要编辑亚马逊创建的东西,所以它是完全不相关的,我的问题的重点在这里。
  • 我在 AWS WAF 中使用自定义规则来解决这个确切的问题。

标签: django apache amazon-elastic-beanstalk


【解决方案1】:

Apache 在决定基于名称的路由时会查看 Host HTTP 请求标头:https://httpd.apache.org/docs/2.4/vhosts/name-based.html

只有一个默认的虚拟主机服务于静态 404 页面,并且只有在指定了正确的Host HTTP 请求标头时才允许路由到 Django。

在 Django 中也有记录:https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/#allowed-hosts

【讨论】:

  • Tnx 用于回答,但您的链接对我的问题没有用(实际上我已经知道它们了)。 Django 文档中的唯一示例是针对 nginx,而不是针对 Apache。 Apache 文档中的所有示例都使用VirtualHost 标签,不可能在独立的 Apache 配置文件中使用,这是 AWS ElasticBeanstalk 中首选的 Apache 配置文件类型。我的问题不是如何在 Apache 中进行,我的问题是如何以正确的方式在 AWS ElasticBeanstalk 中进行部署。
  • 你能解释一下为什么VirtualHost不能在“独立的Apache配置文件”中使用吗?甚至他们的示例也包含VirtualHost 子句docs.aws.amazon.com/elasticbeanstalk/latest/dg/…
  • 再次感谢您的帮助。我会改进我的问题以避免任何进一步的误解,因为我们正在讨论不同的主题。
【解决方案2】:

由于我没有找到任何使用 YAML 文件的更好方法,我正在使用 Python 脚本以我能想到的最不可知的方式修改原始 Apache wsgi.conf 文件(不管亚马逊将来是否修改它)。

我在这里分享它以防任何人发现它有用。使用这种方法,您不必在代码中的任何位置硬编码主机白名单。

  1. 首先在.ebextensions/deploy.config中添加新命令
container_commands:
    01__apache_block_invalid_hosts:
        command: python .ebextensions/apache_block_invalid_hosts.py

请注意,如果您不打算随时更改 DJANGO_ALLOWED_HOSTS 环境变量(稍后解释)并且您了解其含义,则可以考虑添加 leader_only: true

  1. .ebextensions/apache_block_invalid_hosts.py中创建python脚本
from enum import Enum, auto
import os


NEW_AUTH_DIRECTIVE = """
  Require expr "%{{HTTP_HOST}} in {{{hosts}}}"
  Options
"""

class Step(Enum):
    BEFORE_AUTH = auto()
    INSIDE_AUTH = auto()
    AFTER_AUTH = auto()

step = Step.BEFORE_AUTH
with open('/etc/httpd/conf.d/wsgi.conf', 'r') as file_in, open('../wsgi.conf', 'w') as file_out:
    for line in file_in.readlines():
        if step == Step.BEFORE_AUTH:
            file_out.write(line)
            if "<Directory /opt/python/current/app/>" in line:
                hosts = ", ".join([f"'{i}'" for i in os.environ['DJANGO_ALLOWED_HOSTS'].split('__')])
                file_out.write(NEW_AUTH_DIRECTIVE.format(hosts=hosts))
                step = Step.INSIDE_AUTH
        elif step == Step.INSIDE_AUTH:
            if "</Directory>" in line:
                file_out.write(line)
                step = Step.AFTER_AUTH
        elif step == Step.AFTER_AUTH:
            file_out.write(line)

请注意,编辑文件的输出路径是../wsgi.conf,而不是/etc/httpd/conf.d/wsgi.conf。相信我,它有效。

  1. 使用 AWS EB 配置网站中的所有白名单主机定义环境变量:
DJANGO_ALLOWED_HOSTS         whatever.com__www.whatever.com__whatever.us-east-1.elasticbeanstalk.com

请注意,我故意使用__ 分隔符,而不是逗号。这是因为我有时使用eb clone 直接从命令行创建/克隆环境,同时提供环境变量值的更改。如果这样做,则不能在值中包含逗号,并且无法转义它们。

  1. 使用 Django 设置文件中的相同环境变量(我使用 django-environ 库从系统中读取环境变量):
import environ
env = environ.Env()
env.read_env()
ALLOWED_HOSTS = env('DJANGO_ALLOWED_HOSTS', default='*').split('__')

【讨论】:

    猜你喜欢
    • 2017-03-27
    • 2014-04-20
    • 2021-01-20
    • 2017-01-23
    • 2013-02-20
    • 2019-09-29
    • 2014-03-25
    • 2018-07-05
    相关资源
    最近更新 更多