Django和Elastic Beanstalk URL运行状况检查

时间:2022-08-25 09:01:55

I have a Django webapp. It runs inside Docker on Elastic Beanstalk.

我有一个Django webapp。它在Elastic Beanstalk上的Docker中运行。

I'd like to specify a health check URL for slightly more advanced health checking than "can the ELB establish a TCP connection".

我想指定一个运行状况检查URL,用于稍微更高级的运行状况检查,而不是“ELB是否可以建立TCP连接”。

Entirely reasonably, the ELB does this by connecting to the instance over HTTP, using the instance's hostname (e.g. ec2-127-0-0-1.compute-1.amazonaws.com) as the Host header.

完全合理的是,ELB通过使用实例的主机名(例如ec2-127-0-0-1.compute-1.amazonaws.com)作为主机头连接到HTTP实例来完成此操作。

Django has ALLOWED_HOSTS which validates the Host header of incoming requests. I set this to my application's external domain via environment variable.

Django有ALLOWED_HOSTS,用于验证传入请求的Host头。我通过环境变量将它设置为我的应用程序的外部域。

Unsurprisingly and entirely reasonably, Django thus rejects ELB URL health checks due to lack of matching Host.

不出所料,完全合理的是,由于缺少匹配的主机,Django拒绝了ELB URL健康检查。

We don't want to disable ALLOWED_HOSTS because we'd like to be able to trust get_host().

我们不想禁用ALLOWED_HOSTS,因为我们希望能够信任get_host()。

The solutions so far seem to be:

到目前为止,解决方案似乎是:

  • Somehow persuade Django to not care about ALLOWED_HOSTS for certain specific paths (i.e. the health check URL)
  • 以某种方式说服Django不关心某些特定路径的ALLOWED_HOSTS(即健康检查URL)

  • Do something funky like calling the EC2 info API on startup to get the host's FQDN and append it to ALLOWED_HOSTS
  • 做一些时髦的事情,比如在启动时调用EC2 info API来获取主机的FQDN并将其附加到ALLOWED_HOSTS

Neither of these seem particularly pleasant. Can anyone recommend a better / existing solution?

这些都不是特别令人愉快。有人可以推荐更好/现有的解决方案吗?

(For the avoidance of doubt, I believe this problem to be identical to the scenario of "Disabled ALLOWED_HOSTS, fronting HTTPD that filters on host" - I want the health check to hit Django, not a fronting HTTPD)

(为避免疑问,我认为这个问题与“禁用ALLOWED_HOSTS,前置HTTPD过滤主机”的情况相同 - 我希望健康检查能够点击Django,而不是前面的HTTPD)

2 个解决方案

#1


6  

If the ELB health check is sending its request with a host header containing the elastic beanstalk domain (*.elasticbeanstalk.com, or an EC2 domain *.amazonaws.com) then the standard ALLOWED_HOSTS can still be used with a wildcard entry of '.amazonaws.com' or '.elasticbeanstalk.com'.

如果ELB运行状况检查使用包含弹性beanstalk域(* .elasticbeanstalk.com或EC2域* .amazonaws.com)的主机头发送其请求,则标准ALLOWED_HOSTS仍可用于'的通配符条目。 amazonaws.com'或'.elasticbeanstalk.com'。

In my case I received standard ipv4 addresses as the health check hosts, so a different solution was needed. If you can't predict the host at all, and it might be safer to assume you can't, you would need to take a route such as one of the following.

在我的情况下,我收到标准的ipv4地址作为健康检查主机,因此需要一个不同的解决方案。如果您根本无法预测主机,并且假设您不能预测主机可能更安全,则需要采取以下路线之一。

You can use Apache to handle approved hosts instead of propagating ambiguous requests to Django. Since the host header is intended to be the hostname of the server receiving the request, this solution changes the header of valid requests to use the expected site hostname. With elastic beanstalk you'll need to configure Apache using .ebextensions as described here. Under the .ebextensions directory in your project root, add the following to a .config file.

您可以使用Apache来处理已批准的主机,而不是将不明确的请求传播到Django。由于主机标头旨在作为接收请求的服务器的主机名,因此此解决方案会更改有效请求的标头以使用预期的站点主机名。使用弹性beanstalk,您需要使用.ebextensions配置Apache,如此处所述。在项目根目录下的.ebextensions目录下,将以下内容添加到.config文件中。

files:
  "/etc/httpd/conf.d/eb_healthcheck.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
        <If "req('User-Agent') == 'ELB-HealthChecker/1.0' && %{REQUEST_URI} == '/status/'">
            RequestHeader set Host "example.com"
        </If>

Replacing /status/ with your health check URL and example.com with your site's appropriate domain. This tells Apache to check all incoming requests and change the host headers on requests with the appropriate health check user agent that are requesting the appropriate health check URL.

使用您站点的相应域替换/状态/使用您的运行状况检查URL和example.com。这告诉Apache检查所有传入请求并使用正在请求相应运行状况检查URL的相应​​运行状况检查用户代理更改请求上的主机头。

If you would really prefer not to configure Apache, you could write a custom middleware to authenticate health checks. The middleware would have to override Django's CommonMiddleware which calls HttpRequest's get_host() method that validates the request's host. You could do something like this

如果您真的不想配置Apache,可以编写自定义中间件来验证运行状况检查。中间件必须覆盖Django的CommonMiddleware,后者调用HttpRequest的get_host()方法来验证请求的主机。你可以这样做

from django.middleware.common import CommonMiddleware


class CommonOverrideMiddleware(CommonMiddleware):
    def process_request(self, request):
        if not('HTTP_USER_AGENT' in request.META and request.META['HTTP_USER_AGENT'] == 'ELB-HealthChecker/1.0' and request.get_full_path() == '/status/'):
            return super().process_request(request)

Which just allows any health check requests to skip the host validation. You'd then replace django.middleware.common.CommonMiddleware with path.CommonOverrideMiddleware in your settings.py.

这只允许任何健康检查请求跳过主机验证。然后用settings.py中的path.CommonOverrideMiddleware替换django.middleware.common.CommonMiddleware。

I would recommend using the Apache configuration approach to avoid any details in the middleware, and to completely isolate Django from host issues.

我建议使用Apache配置方法来避免中间件中的任何细节,并完全将Django与主机问题隔离开来。

#2


3  

This is what I use, and it works well:

这是我使用的,它运作良好:

import socket
local_ip = str(socket.gethostbyname(socket.gethostname()))
ALLOWED_HOSTS=[local_ip, '.mydomain.com', 'mydomain.elasticbeanstalk.com' ]

where you replace mydomain and mydomain.elasticbeanstalk.com with your own.

在哪里用你自己的mydomain和mydomain.elasticbeanstalk.com替换它们。

#1


6  

If the ELB health check is sending its request with a host header containing the elastic beanstalk domain (*.elasticbeanstalk.com, or an EC2 domain *.amazonaws.com) then the standard ALLOWED_HOSTS can still be used with a wildcard entry of '.amazonaws.com' or '.elasticbeanstalk.com'.

如果ELB运行状况检查使用包含弹性beanstalk域(* .elasticbeanstalk.com或EC2域* .amazonaws.com)的主机头发送其请求,则标准ALLOWED_HOSTS仍可用于'的通配符条目。 amazonaws.com'或'.elasticbeanstalk.com'。

In my case I received standard ipv4 addresses as the health check hosts, so a different solution was needed. If you can't predict the host at all, and it might be safer to assume you can't, you would need to take a route such as one of the following.

在我的情况下,我收到标准的ipv4地址作为健康检查主机,因此需要一个不同的解决方案。如果您根本无法预测主机,并且假设您不能预测主机可能更安全,则需要采取以下路线之一。

You can use Apache to handle approved hosts instead of propagating ambiguous requests to Django. Since the host header is intended to be the hostname of the server receiving the request, this solution changes the header of valid requests to use the expected site hostname. With elastic beanstalk you'll need to configure Apache using .ebextensions as described here. Under the .ebextensions directory in your project root, add the following to a .config file.

您可以使用Apache来处理已批准的主机,而不是将不明确的请求传播到Django。由于主机标头旨在作为接收请求的服务器的主机名,因此此解决方案会更改有效请求的标头以使用预期的站点主机名。使用弹性beanstalk,您需要使用.ebextensions配置Apache,如此处所述。在项目根目录下的.ebextensions目录下,将以下内容添加到.config文件中。

files:
  "/etc/httpd/conf.d/eb_healthcheck.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
        <If "req('User-Agent') == 'ELB-HealthChecker/1.0' && %{REQUEST_URI} == '/status/'">
            RequestHeader set Host "example.com"
        </If>

Replacing /status/ with your health check URL and example.com with your site's appropriate domain. This tells Apache to check all incoming requests and change the host headers on requests with the appropriate health check user agent that are requesting the appropriate health check URL.

使用您站点的相应域替换/状态/使用您的运行状况检查URL和example.com。这告诉Apache检查所有传入请求并使用正在请求相应运行状况检查URL的相应​​运行状况检查用户代理更改请求上的主机头。

If you would really prefer not to configure Apache, you could write a custom middleware to authenticate health checks. The middleware would have to override Django's CommonMiddleware which calls HttpRequest's get_host() method that validates the request's host. You could do something like this

如果您真的不想配置Apache,可以编写自定义中间件来验证运行状况检查。中间件必须覆盖Django的CommonMiddleware,后者调用HttpRequest的get_host()方法来验证请求的主机。你可以这样做

from django.middleware.common import CommonMiddleware


class CommonOverrideMiddleware(CommonMiddleware):
    def process_request(self, request):
        if not('HTTP_USER_AGENT' in request.META and request.META['HTTP_USER_AGENT'] == 'ELB-HealthChecker/1.0' and request.get_full_path() == '/status/'):
            return super().process_request(request)

Which just allows any health check requests to skip the host validation. You'd then replace django.middleware.common.CommonMiddleware with path.CommonOverrideMiddleware in your settings.py.

这只允许任何健康检查请求跳过主机验证。然后用settings.py中的path.CommonOverrideMiddleware替换django.middleware.common.CommonMiddleware。

I would recommend using the Apache configuration approach to avoid any details in the middleware, and to completely isolate Django from host issues.

我建议使用Apache配置方法来避免中间件中的任何细节,并完全将Django与主机问题隔离开来。

#2


3  

This is what I use, and it works well:

这是我使用的,它运作良好:

import socket
local_ip = str(socket.gethostbyname(socket.gethostname()))
ALLOWED_HOSTS=[local_ip, '.mydomain.com', 'mydomain.elasticbeanstalk.com' ]

where you replace mydomain and mydomain.elasticbeanstalk.com with your own.

在哪里用你自己的mydomain和mydomain.elasticbeanstalk.com替换它们。