AWS:如何将多个域重定向到另一个域上的页面?

ssc*_*ssc 6 amazon-s3 redirect amazon-cloudfront amazon-web-services amazon-route53

我的目标

我有多个域(例如 10 或 20 个),我希望将这些页面上任何位置的任何访问者重定向到另一个域上的一个页面(例如我的 stackoverflow.com 个人资料页面)。

这包括

  1. 顶点域使用http(例如http://mydomain01.com
  2. 顶点域使用https(例如https://mydomain01.com
  3. 子域使用http(例如http://www.mydomain01.comhttp://blog.mydomain01.com
  4. 子域使用https(例如https://www.mydomain01.comhttps://blog.mydomain01.com
  5. 任何路径(例如http://mydomain01.com/some_pathhttps://www.mydomain01.com/another/path.html

加上我所有其他域(mydomain02.commydomain03.com等;每个域都有上述用例)。

我的研究

  1. 这篇 AWS 文章介绍了如何使用AWS S3AWS Route 53将 Internet 流量从 apex 域重定向到另一个域(我的这包括上面的列表中的案例 #1):这适用于,但不适用于。httphttps
  2. 这篇 AWS 文章介绍了如何使用AWS S3AWS Route 53AWS CloudFront重定向多种情况下的互联网流量(从表面上看,涵盖了我的这包括上面列表中的所有情况) :这适用于和。(还讨论了使用应用程序负载均衡器,但我想这超出了这里的范围......)httphttps
  3. 这篇 AWS 文章添加了有关设置 CloudFront 分配以及如何深入了解日志文件的更多详细信息。
  4. 这篇 AWS 文章记录了使用高级条件重定向的重定向规则:不确定我是否需要去那里实现我的目标,所以还没有真正研究过这一点。

另外,显然有很多 SO 问题(请参阅与此问题右侧相关的内容)以及有关该主题的其他帖子;其中大多数的问题是它们使用以前版本的 AWS 控制台 UI 的屏幕截图:大多数内容应该仍然相同,但在我看来,将这些屏幕截图与当前 UI 相关联又增加了一层混乱。

AWS(和其他)文档的主要要点:

  1. 我需要在AWS S3中创建一个存储桶并在其中配置重定向,
  2. 我需要在 AWS CloudFront 中创建一个分配;
  3. 为了在 CloudFront 中使用自定义域,我需要在 AWS ACM 中创建一个证书,
  4. 我需要在 AWS Route 53 中创建托管区域并在其中配置记录。

到目前为止我的工作

已安装最新的 AWS CLI,regionoutput在 中进行配置~/.aws/config,在 中设置凭证~/.aws/credentials(每个凭证针对每个 AWS 账户);AWS_*环境变量export编辑。

我使用 AWS 区域US East (N. Virginia) (us-east-1)来处理所有事情,以防止因 AWS 资源在某个区域不可用而导致的任何其他问题。

$ aws --version
aws-cli/2.2.23 Python/3.9.6 Darwin/19.6.0 source/x86_64 prompt/off
Run Code Online (Sandbox Code Playgroud)

我省略了任何 shell 提示或>shell 行继续字符,以便更轻松地从本文复制到 shell 中。

设置 S3 存储桶

警告:这将创建一个“所有公共”存储桶,没有任何访问限制。在这种情况下,这应该不重要,因为没有要保护的存储桶内容,但这种公共存储桶通常是一种不好的做法。另外,我使用公共存储桶来防止访问限制引起的任何其他问题:首先,让它工作;第二,确保安全

创建存储桶

$ aws --version
aws-cli/2.2.23 Python/3.9.6 Darwin/19.6.0 source/x86_64 prompt/off
Run Code Online (Sandbox Code Playgroud)
  • 回复:
aws s3api create-bucket --bucket mydomain01.com
Run Code Online (Sandbox Code Playgroud)

设置重定向

{
    "Location": "/mydomain01.com"
}
Run Code Online (Sandbox Code Playgroud)
  • 没有反应

问题:S3 存储桶名称必须与 apex 域名匹配。

使用任何存储桶名称,但mydomain01.com(对于我的示例)似乎会失败,并且没有任何原因指示。AWS 文档并没有真正明确这一点 - 事实上,我仍然不确定我是否严重误解了这里的某些内容,但据我所知,官方 AWS 文档实际上在这一点上有些草率 - IMO - 至关重要的关键点:例如,#2只是说

  1. 创建具有全局唯一名称的 S3 存储桶。

这可以是任何全球唯一的名称。#1在某种程度上提到了这一点 - 一旦你知道如何阅读这些位......

顺便说一句,第 2 篇文章仍然让我感到困惑

如果您不使用自定义域...

为什么我使用自定义域?!?重点是重定向我的自定义域,不是吗?!?好吧,无论如何……

问题:不得在主机名前添加协议。

AWS 控制台和 AWS CLI 似乎都不会测试是否在主机名UI 字段中输入了协议(http://或) /是否在JSON 字符串中传递了协议。但是,如果在前面添加一个,则重定向会失败;请参阅下面的测试重定向https://HostName

问题:AWS S3 控制台 UI 错误。

设置重定向后,AWS 控制台会在其 UI 中显示指向存储桶 URL ( ) 的可点击链接,该链接位于http://mydomain01.com.s3-website-us-east-1.amazonaws.com存储桶属性选项卡最底部的静态网站托管部分。

单击该链接无法打开该页面,似乎是因为 AWS 控制台弄乱了 URL 并尝试打开http://https//stackoverflow.com/users/217844/ssc/,无论协议如何。

测试重定向

  • HTTPie在 shell 中使用而不是curlorwget因为这就是现在很酷的孩子们使用的
  • 将浏览器中 AWS 控制台的链接复制到 shell
aws s3api put-bucket-website --bucket mydomain01.com --website-configuration \
    '{ "RedirectAllRequestsTo": { "HostName": "stackoverflow.com/users/217844/ssc" } }'
Run Code Online (Sandbox Code Playgroud)

--> 似乎有效

  • 如果协议被错误地添加到主机名前面,则测试重定向;请注意损坏的Location网址:
http http://mydomain01.com.s3-website-us-east-1.amazonaws.com/
HTTP/1.1 301 Moved Permanently
Content-Length: 0
Date: Mon, 02 Aug 2021 12:39:09 GMT
Location: http://stackoverflow.com/users/217844/ssc/
Server: AmazonS3
x-amz-id-2: rakAqUMnRraGvo/WkSa6AnbuhWn/9YZX/CAlI/OJQKYoWp/OdQIbyhsvHSwNved3suwMdgglqpE=
x-amz-request-id: C5BBG833Q9TQ9J6X
Run Code Online (Sandbox Code Playgroud)

我的问题#1

注意:当我开始写这篇文章时,我有这些问题;我我能够自己回答这些问题(请参阅下面的测试www子域记录)。如果我错了,请有人纠正我:

  1. 问:即使我使用 CloudFront,“存储桶名称 == 域名”要求也适用吗?
    答:是的。
  2. 问:我是否需要为顶级域和每个子域各创建一个存储桶?所以,在我的例子中
    • mydomain01.com
    • www.mydomain01.com
    • blog.mydomain01.com
      答:是的。

设置 Route 53 托管区域

创建托管区域

http http://mydomain01.com.s3-website-us-east-1.amazonaws.com/
HTTP/1.1 301 Moved Permanently
Content-Length: 0
Date: Mon, 02 Aug 2021 12:52:10 GMT
Location: http://https://stackoverflow.com/users/217844/ssc/
Server: AmazonS3
x-amz-id-2: Ee2/ob0faTpRdp6mGITdmClozXNmF1Q2oTbPioms8O91VA8n5VA3MoHhveeFz7v2VS65YKFKlDA=
x-amz-request-id: ZJP653R50YD5HSRS
Run Code Online (Sandbox Code Playgroud)
  • 回复
aws route53 create-hosted-zone --caller-reference "$(date '+%Y%m%d-%H%M%S')" --name mydomain01.com
Run Code Online (Sandbox Code Playgroud)
  • 记下Z123456789EXAMPLE0SKX后续步骤中需要的托管区域 ID

为顶级域创建记录

{
    "Location": "https://route53.amazonaws.com/2013-04-01/hostedzone/Z123456789EXAMPLE0SKX",
    "HostedZone": {
        "Id": "/hostedzone/Z123456789EXAMPLE0SKX",
        "Name": "mydomain01.com.",
        "CallerReference": "20210802-150736",
        "Config": {
            "PrivateZone": false
        },
        "ResourceRecordSetCount": 2
    },
    "ChangeInfo": {
        "Id": "/change/C1234567890SKXEXAMPLE",
        "Status": "PENDING",
        "SubmittedAt": "2021-08-02T13:07:37.860000+00:00"
    },
    "DelegationSet": {
        "NameServers": [
            "ns-1234.awsdns-12.com",
            "ns-5678.awsdns-34.co.uk",
            "ns-1234.awsdns-56.net",
            "ns-5678.awsdns-78.org"
        ]
    }
}
Run Code Online (Sandbox Code Playgroud)

陷阱必须逐字使用.s3-website-us-east-1.amazonaws.comDNSName

AWS 文档在各种地方谈论example.comor等​​。在这种情况下,这不是用自己的值(例如)example.com.s3-website-us-east-1.amazonaws.com替换的示例,而是中的逐字值,即mydomain01.com.s3-website-us-east-1.amazonaws.coms3-website-us-east-1.amazonaws.com

问题:不得在主机名前添加协议。

与上面的问题类似,AWS 控制台和 AWS CLI 都乐意接受在主机名UI 字段中输入的值前面添加的协议 (http://或) / 作为 传递。至少,这在控制台中看起来非常错误,例如https://DNSNamehttp\072\057\057mydomain01.s3-website-us-east-1.amazonaws.com.

这两个问题在 AWS 控制台中都得到了一定程度的缓解,在创建或编辑记录时可以从下拉框中选择值;使用 AWS CLI 时,您必须仔细检查发送的内容。

相同的问题和缓解措施适用于记录名称UI 字段/ NameJSON 值。

为顶级域创建记录(续)

  • 用于jq快速测试临时文件包含有效的 json
jq . < change-batch.apex.json 1> /dev/null
Run Code Online (Sandbox Code Playgroud)
  • 无输出 --> 有效的 JSON
{
  "Changes": [
    {
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "mydomain01.com.",
        "Type": "A",
        "AliasTarget": {
          "HostedZoneId": "Z3AQBSTGFYJSTF",
          "DNSName": "s3-website-us-east-1.amazonaws.com",
          "EvaluateTargetHealth": false
        }
      }
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)
  • 回复
jq . < change-batch.apex.json 1> /dev/null
Run Code Online (Sandbox Code Playgroud)

测试顶点域记录

  • 测试http
aws route53 change-resource-record-sets --hosted-zone-id Z123456789EXAMPLE0SKX \
    --change-batch "file://$(pwd)/change-batch.apex.json"
Run Code Online (Sandbox Code Playgroud)

--> 看起来不错

  • http用路径测试
{
    "ChangeInfo": {
        "Id": "/change/C1234567890EXAMPLESKX",
        "Status": "PENDING",
        "SubmittedAt": "2021-08-02T14:20:09.370000+00:00"
    }
}
Run Code Online (Sandbox Code Playgroud)
  • 测试https
http http://mydomain01.com
HTTP/1.1 301 Moved Permanently
Content-Length: 0
Date: Mon, 02 Aug 2021 15:06:08 GMT
Location: http://stackoverflow.com/users/217844/ssc/
Server: AmazonS3
x-amz-id-2: EfDtCxif2iV4eInskirSBAOjQS7o9arzJCeZjscF6mW7cwwmm9Nxb7QJT50x2kjdslX2fOxA+lk=
x-amz-request-id: WM7K9TDEF75A6P1V
Run Code Online (Sandbox Code Playgroud)
  • (为了可读性,将响应包装起来)

--> 超时(60 秒后?) - 正如预期:使用 S3 存储桶进行重定向不起作用https(见上文)

陷阱:DNS 更改传播延迟。

AWS 和 Google在传播 DNS 设置更改方面非常快(如以秒或分钟为单位),但可能还涉及其他“较慢”的名称服务器。按照此处所述绕过它们,以消除混乱的根源。该方法仅适用于 macOS,但该概念对于任何操作系统都是相同的。

陷阱:浏览器缓存。

当不在 shell 中而是在浏览器中测试 DNS 更改时,浏览器可能会从其缓存中获取结果。我的大部分工作都使用 Chrome 完成,但使用 Firefox(或 Safari)进行测试,因此我可以在每次测试之前清除整个缓存,以消除潜在的问题 - 无需退出 Google、AWS 等。

www为子域创建记录

  • 唯一的区别是NameJSON 值
http http://mydomain01.com/some/path
 ... similar output as above ...
Run Code Online (Sandbox Code Playgroud)
  • 响应与上面类似

测试www子域记录

  • 测试http
http https://mydomain01.com

http: error: ConnectionError: HTTPSConnectionPool(host='mydomain01.com', port=443):
  Max retries exceeded with url: / (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x101a48100>:
    Failed to establish a new connection: [Errno 60] Operation timed out')) while doing a GET request to URL: https://mydomain01.com/
Run Code Online (Sandbox Code Playgroud)
  • 我认为这回答了我上面的问题 #1中的第二个:我需要每个顶点/子域一个 S3 存储桶来转发。

设置 CloudFront 分配

创建证书

  • AWS ACMrequest-certificate文档
  • 该证书应该适用于 apex 和所有子域,因此需要为此证书/pass添加另一个名称--subject-alternative-names;请参阅这篇 AWS 文章(上方的蓝色框)。
  • 添加引号,*.mydomain01.com这样 shell 就不会解释*
sed -e 's|mydomain01.com.|www.mydomain01.com.|g' change-batch.apex.json > change-batch.www.json
aws route53 change-resource-record-sets --hosted-zone-id Z123456789EXAMPLE0SKX \
    --change-batch "file://$(pwd)/change-batch.www.json
Run Code Online (Sandbox Code Playgroud)
  • 回复:
http http://www.mydomain01.com
HTTP/1.1 404 Not Found
Content-Length: 363
Content-Type: text/html; charset=utf-8
Date: Mon, 02 Aug 2021 15:28:05 GMT
Server: AmazonS3
x-amz-id-2: MGLcynq1iEGKh+pT6N6iRpCuQSN243q/5zm2Y7rXTnM7iW9nvDokF6s20xEUBr7QiEtBPEzZmII=
x-amz-request-id: TK83G35EMYFR8SKX

<html>
<head><title>404 Not Found</title></head>
<body>
<h1>404 Not Found</h1>
<ul>
<li>Code: NoSuchBucket</li>
<li>Message: The specified bucket does not exist</li>
<li>BucketName: www.mydomain01.com</li>
<li>RequestId: TK83G35EMYFR8SKX</li>
<li>HostId: MGLcynq1iEGKh+pT6N6iRpCuQSN243q/5zm2Y7rXTnM7iW9nvDokF6s20xEUBr7QiEtBPEzZmII=</li>
</ul>
<hr/>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
  • 123456789012是我的 AWS 账户 ID;之后的所有内容都certificate/只是一个 UUID

获取证书详细信息

  • AWS ACMdescribe-certificate文档
  • 将响应保存到临时本地文件;提取ResourceRecord.NameResourceRecord.Value使用jq
  • 证明我拥有 AWS Route 53 记录所需mydomain01.com
  • 或者,使用--query参数aws acm describe-certificate
aws acm request-certificate --domain-name mydomain01.com --validation-method DNS \
    --subject-alternative-names '*.mydomain01.com'
Run Code Online (Sandbox Code Playgroud)
{
    "CertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/12345678-90ab-cdef-1234-1234567890ab"
}
Run Code Online (Sandbox Code Playgroud)

创建用于证书验证的 Route 53 记录

  • 将由 AWS ACM 自动检查,一旦找到此记录,证书将得到验证
  • 和以前一样,使用临时本地文件change-batch.cert.json,请参阅例如为 apex 域创建记录;内容:
aws acm describe-certificate \
    --certificate-arn "arn:aws:acm:us-east-1:123456789012:certificate/12345678-90ab-cdef-1234-1234567890ab" \
 > describe-certificate.json
Run Code Online (Sandbox Code Playgroud)
  • 外壳命令:
jq -r '.Certificate.DomainValidationOptions[0].ResourceRecord.Name' describe-certificate.json
_1234567890abcdef1234567890abcdef.mydomain01.com.

jq -r '.Certificate.DomainValidationOptions[0].ResourceRecord.Value' describe-certificate.json 
_1234567890abcdef1234567890abcdef.weirdchars.acm-validations.aws.
Run Code Online (Sandbox Code Playgroud)
  • 响应类似于上面创建记录时的响应
  • 注意:ACM 可能需要几分钟时间来验证证书。

创建 CloudFront 分配

  • AWS CloudFrontcreate-distribution文档
  • 再次强调,CallerReference必须是一个唯一的字符串;在 shell 中使用例如date '+%Y%m%d-%H%M%S'创建并复制到文件中;请参阅创建托管区域
  • 和以前一样,对复杂值使用临时本地文件create-distribution.json;以下内容
  • MinimumProtocolVersion:从这篇 AWS 文章中获取价值
  • OriginProtocolPolicy:使用是http-only因为源(S3 存储桶)只能做http
  • ViewerProtocolPolicyredirect-to-https创建此发行版的全部目的是从重定向httphttps
  • 注意:我不知道(AWS 文档也没有告诉)哪些字段是强制必需的;如果发送的数据丢失或错误,AWS CLI 命令会显示清晰详细的消息。
{
    "CallerReference": "20210802-191725",
    "Aliases": {
        "Quantity": 2,
        "Items": ["mydomain01.com", "*.mydomain01.com"]
    },
    "Origins": {
        "Quantity": 1,
        "Items": [
            {
                "Id": "mydomain01.com.s3.us-east-1.amazonaws.com_20210802-191725",
                "DomainName": "mydomain01.com.s3.us-east-1.amazonaws.com",
                "CustomOriginConfig": {
                    "HTTPPort": 80,
                    "HTTPSPort": 443,
                    "OriginProtocolPolicy": "http-only"
                }
            }
        ]
    },
    "OriginGroups": {
        "Quantity": 0
    },
    "DefaultCacheBehavior": {
        "TargetOriginId": "mydomain01.com.s3.us-east-1.amazonaws.com_20210802-191725",
        "ForwardedValues": {
            "QueryString": false,
            "Cookies": {
                "Forward": "none"
            },
            "Headers": {
                "Quantity": 0
            },
            "QueryStringCacheKeys": {
                "Quantity": 0
            }
        },
        "TrustedSigners": {
            "Enabled": false,
            "Quantity": 0
        },
        "ViewerProtocolPolicy": "redirect-to-https",
        "MinTTL": 0,
        "AllowedMethods": {
            "Quantity": 2,
            "Items": [
                "HEAD",
                "GET"
            ],
            "CachedMethods": {
                "Quantity": 2,
                "Items": [
                    "HEAD",
                    "GET"
                ]
            }
        },
        "SmoothStreaming": false,
        "DefaultTTL": 86400,
        "MaxTTL": 31536000,
       

jel*_*csc 1

总的来说,你走在正确的轨道上。仅一条评论:如果您的域已使用其他 DNS 服务提供商,则可以省略 Route53。

问:即使我使用 CloudFront,“存储桶名称 == 域名”要求也适用吗?

不,如果您使用 CloudFront。CNAME 在 CloudFront 中单独配置。

问:我是否需要为顶级域和每个子域各创建一个存储桶?

不,您不需要每个域/子域一个存储桶。

为什么 S3 存储桶拒绝访问?

您应该使用您的s3-website-us-east-1.amazonaws.com域名作为 CF 源。

S3 存储桶根本没有访问策略正常吗?通常没有明确允许任何人访问的“公共”存储桶策略吗?

如果您仅使用存储桶来重定向流量,则没有访问策略应该没问题。

与每个顶点/子域一个 S3 存储桶类似,我是否还需要每个顶点/子域一个 CloudFront 分配?

是的,每个域/子域需要一个 CloudFront 分配,因为一个分配最多可以附加一个 ACM 证书。

如果是这样,我想将*.mydomain01.com备用域添加到证书(和分发)中并没有任何意义,不是吗?!?我还需要每个发行版一张专用于一个域的证书,对吗?

添加通配符域确实有意义,因为 CF 发行版也需要处理子域流量。

如果您还有任何其他问题,请加入AWS 聊天并在聊天中@我。