django - 在 AWS ElasticBeanstalk 中阻止 Apache 中无效的 HTTP_HOST header

标签 django apache amazon-elastic-beanstalk

我有几个运行 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 header ,这样它们甚至无法到达 Django 并每天摆脱这数百封电子邮件。
我知道,在 Apache 中,有许多示例说明如何在 Apache 中执行此操作,但我还没有找到在 AWS ElasticBeanstalk 中进行部署时如何执行此操作的单个示例。
如果您不熟悉 AWS ElasticBeanstalk,请记住该系统会自动创建 /etc/httpd/conf.d/wsgi.conf文件中包含由亚马逊制作的一些配置,亚马逊可以(并且将)在我们无法控制的 future 修改它们。
因此,当我们想向 Apache 添加一些配置时,例如提供重定向,首选方式是在我们的项目中提供一个 YAML 文件,该文件定义了一个新的独立 Apache 配置文件,除了 wsgi.conf 之外,该文件将被考虑在内。亚马逊自动创建的文件,如下所示:
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 配置文件中提供一些配置来覆盖现有的 <VirtualHost> -> <Directory>部分,所以我可以像 ElasticBeanstalk 理解的那样在 YAML 文件中定义所需的代码。
否则,我将不得不提供一个脚本来即时进行替换。我知道怎么做,但我觉得这很丑陋。我只是问是否有更优雅的解决方案。

最佳答案

由于我没有找到使用 YAML 文件的任何更好的方法,我使用 Python 脚本来修改原始 Apache wsgi.conf以我能想到的最不可知的方式提交文件(无论亚马逊将来是否修改它)。
我在这里分享它,以防任何人发现它有用。使用这种方法,您不必在代码中的任何位置对主机白名单进行硬编码。

  • 首先在.ebextensions/deploy.config中添加一个新命令

  • container_commands:
        01__apache_block_invalid_hosts:
            command: python .ebextensions/apache_block_invalid_hosts.py
    
    请注意,您可以考虑添加 leader_only: true如果您不打算更改 DJANGO_ALLOWED_HOSTS任何时候的环境变量(稍后解释)并且如果您了解其含义。
  • .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 .相信我,它有效。
  • 使用 AWS EB 配置网站中的所有白名单主机定义环境变量:
  • DJANGO_ALLOWED_HOSTS         whatever.com__www.whatever.com__whatever.us-east-1.elasticbeanstalk.com
    
    请注意,我使用的是 __分隔符,而不是逗号。这是因为我有时会使用 eb clone 直接从我的命令行创建/克隆环境。同时提供环境变量值的更改。如果这样做,则不能在值中包含逗号,并且无法转义它们。
  • 使用 Django 设置文件中的相同环境变量(我使用 django-environ 库从系统读取环境变量):

  • import environ
    env = environ.Env()
    env.read_env()
    ALLOWED_HOSTS = env('DJANGO_ALLOWED_HOSTS', default='*').split('__')
    

    关于django - 在 AWS ElasticBeanstalk 中阻止 Apache 中无效的 HTTP_HOST header ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63704288/

    相关文章:

    mysql - 在 Django 中将未知对象类型保存到数据库中

    python - 为什么Django查询要检查唯一性?

    azure - Paas 环境中的服务堆栈

    python - 如何将 python 命令行工具添加到我的 PATH?

    node.js - 运行 npm install 时多次调用回调

    javascript - 用于显示实时消息控制台的 Django(Dajax/Jquery?)插件

    python - 主管没有启动 gunicorn

    Windows 上的 Apache 和 Fiddler

    regex - 重写条件不起作用

    ruby-on-rails - 使用 apache 和 passenger 为 rails 4.2 站点设置 SSL 的问题