在Amazon EC2 ELB上安装SSL证书

3

我是Amazon Web Services的新手,一直在尝试在EC2实例上安装SSL证书。我尝试按照AWS文档进行操作,但发现它令人困惑。然后我按照http://www.robertbrewitz.com/2014/09/aws-and-setting-up-a-custom-ssl-certificate/指南进行了操作。

我在Go Daddy购买了我的SSL证书,并使用openssl生成了2个文件:

server.key
server.csr

指南上说我应该期望获得3个证书:

DigiCertCA.crt
TrustedRoot.crt
star_yourdomain_com.crt

相反,我混淆地收到了两个文件,它们的名称相同:

f6f65901b1708ae5.crt
gd_bundle-g2-g1.crt

我猜测f6f65901b1708ae5.crt是我的域名证书(但我不确定)。无论如何,指南说我需要一个弹性负载均衡器才能安装SSL证书,所以我创建了一个。

我使用以下命令生成私钥:

openssl rsa -in server.key -text

以及公钥证书:

openssl x509 -inform PEM -in f6f65901b1708ae5.crt

我还需要输入证书链。我不确定这是什么以及如何获取它,所以我猜测了命令:

openssl x509 -inform PEM -in gd_bundle-g2-g1.crt

并输入以“-----BEGIN CERTIFICATE-----”开头的证书密钥。

指南继续说,然后我需要设置Cloudfront。我安装了aws命令行工具,并为了生成PEMs运行了以下命令:

openssl rsa -in server.key -text > aws_private.pem
openssl x509 -inform PEM -in f6f65901b1708ae5.crt > aws_public.pem
openssl x509 -inform PEM -in gd_bundle-g2-g1.crt > aws_public.pem 

我使用以下方式上传了SSL证书:

aws iam upload-server-certificate --server-certificate-name mydomain_com \
--certificate-body file://aws_public.pem --private-key file://aws_private.pem \
--certificate-chain file://aws_chain.pem --path /cloudfront/mydomain_com/

这次操作成功了。

接下来我需要创建一个Cloudfront分发,我已经完成了,选择了SSL证书。

然而,当我访问我的https网址(https://www.example.org/)时,它无法工作。但是http://www.example.org/可以正常工作。

由于安装SSL证书的步骤非常繁琐,我怀疑自己在某个环节上出了错。问题是,我不知道错在哪里。有没有人能提供一些指导?

此外,难道没有更简单的方法来安装SSL证书吗?对于如此普遍的事情,它似乎过于复杂了。我愿意支付专家来为我完成这项任务(我是一名软件开发人员,几乎没有任何与SSL相关的知识),但很难找到这样的人(还有交出登录详细信息等问题)。非常感谢任何帮助。

编辑

下面的建议是我应该使用 AWS证书管理器。我已经看过了,这似乎是一个更加简单的选择。但是,我花了86欧元从Go Daddy购买了一个SSL证书,所以我希望那不会被浪费。我的任何工作都可以挽救吗?有重新销售SSL证书的方法吗?
编辑
我仍然没有找到真正的解决方案。为了澄清,我有一个非常小众的网站,将有很少的访问者。我将网站放在EC2实例上。我按照上面的网站建议使用负载均衡器和Cloudfront来进行SSL加密。然而,它没有起作用,这可能是过度杀伤力的。有人能帮我解决这个问题吗?我想使用我支付的SSL证书,但如果不能,我应该使用像Lets Encrypt这样的东西吗?

你在CloudFront上安装了证书,而不是在EC2实例上安装。你是否在使用CloudFront作为EC2实例前面的CDN?你可以使用新的AWS证书管理器轻松获得免费证书:https://aws.amazon.com/certificate-manager/ - Mark B
我原本没有计划使用Cloudfront,只是因为我在按照指南操作才使用了它。我能否在AWS证书管理器中使用我的GoDaddy证书? - Mark
我已经查看了证书管理器,它似乎非常简单明了。但是我已经从Go Daddy购买了SSL证书:( - Mark
1
请看:沉没成本 - tedder42
@MarkB 实际上,看起来这只在美国东部地区可用。 - Mark
显示剩余5条评论
1个回答

6
你提到指南中提到预计会有3个文件,其中包括一个“DigiCertCA.crt”文件;听起来这是针对DigiCert作为您的证书提供商编写的,而不是GoDaddy。
证书
首先,为了确保“f6f65901b1708ae5.crt”包含您请求的证书(并消除任何疑虑),您可以将“server.csr”文件(即证书签名请求)中的数据(例如通用名称(CN)、DNS主题备用名称(SANs)等)与“f6f65901b1708ae5.crt”文件中的数据进行比较。
$ openssl req -noout -text < server.csr

这应该显示关于CSR文件中您域名的详细信息的易读文本。与此相比较:
$ openssl x509 -noout -text < f6f65901b1708ae5.crt

这应该显示类似于易于阅读的文本,其中包含更多的细节/字段。但它们应该大致符合您的期望。请注意,如果您看到类似于以下内容的错误:注意
51299:error:0906D06C:PEM routines:PEM_read_bio:no start line:/SourceCache/OpenSSL098/OpenSSL098-52.40.1/src/crypto/pem/pem_lib.c:648:Expecting: TRUSTED CERTIFICATE

那么它建议您的"f6f65901b1708ae5.crt"文件是DER格式,而不是PEM格式。如果不是,则您已经拥有了PEM文件,这是AWS ELBs所期望的。如果您有一个DER格式的证书,可以使用以下方法轻松转换为PEM格式:

$ openssl x509 -in f6f65901b1708ae5.crt -inform DER -out f6f65901b1708ae5.pem -outform PEM

我只是想要详尽说明这一部分。

假设我们现在知道"f6f65901b1708ae5.crt"包含您域名的PEM格式证书,我们已经准备好处理“证书链”部分了。

我查看了GoDaddy的在线证书存储库,看看您提到的"gd_bundle-g2-g1.crt"文件是否存在,并且确实存在。(这些文件可以公开使用,因为它们包含的是供任何人/每个人使用的公共证书。)查看gd_bundle-g2-g1.crt文件,我发现它包含多个证书。这很重要。

看,一个“证书链”是一个文件列表,提供了从你拥有的“f6f65901b1708ae5.crt”证书到受信任的根GoDaddy CA证书的“信任路径”(或“链”)。每个证书都有一个“主题”(它是谁颁发给的)和一个“发行者”(谁颁发的)。这意味着你可以向“后”走,从你的证书到发行者的证书,再到那个证书的发行者的证书,以此类推。这个向后走就是“证书链”。
“gd_bundle-g2-g1.crt”文件包含多个证书的事实意味着该文件包含你需要的证书链。这也意味着你不想这样做:
$ openssl x509 -inform PEM -in gd_bundle-g2-g1.crt > aws_public.pem

因为openssl x509只读取指定文件中的第一个证书,而你需要所有证书。
鉴于上述情况,您可能只需要以下内容(确保您的私钥格式为PEM):
$ openssl rsa -in server.key -text > aws_private.pem

接下来,我们假设"f6f65901b1708ae5.crt"已经是PEM格式(如果不是,您知道如何在上面进行转换),而且我们知道"gd_bundle-g2-g1.crt"已经是PEM格式,因此我们现在可以将它们上传到AWS ELB。

AWS ELB

要上传证书和密钥以供ELB使用,请使用以下内容:

$ aws iam upload-server-certificate \
  --server-certificate-name redmatterapp_com2 \
  --certificate-body file://f6f65901b1708ae5.crt \
  --private-key file://aws_private.pem \
  --certificate-chain file://gd_bundle-g2-g1.crt \
  --path /cloudfront/redmatterapp_com/

请注意,我使用了不同的--server-certificate-name,只是为了确保它不会覆盖或冲突您现有的配置。建议您在名称中包含上传证书的日期(或更好的是到期日期),作为提示,告诉未来的自己这个证书是何时添加的,例如“redmatterapp_com-2016-02-18”。
还要注意,如果您使用CloudFront,则不应使用--path选项。如果您曾经使用CloudFront然后将其删除,我强烈建议您再次执行上述aws iam upload-server-certificate命令,只需使用不同的--server-certificate-name并且没有--path选项(并删除先前的名称)。这可能意味着重新配置任何现有的ELB HTTPS侦听器以使用新的证书名称,但这可能是必要的,因为该--path影响SSL处理。
完成上述步骤后,使用 AWS 控制台,您应该能够配置 AWS ELB,在“侦听器”选项卡下点击“编辑”,添加一个名为“https”的侦听器。当添加任何支持 SSL 的侦听器时,您将看到“SSL 证书”列/选项卡下出现一个“更改”链接。点击“更改”,选择“现有证书”按钮。在“证书名称”下拉菜单下,您应该会看到您上面使用的“--server-certificate-name”字符串/标签的条目。选择该条目,然后点击“保存”。现在,对于 AWS ELB 上的该侦听器,连接应该已经正确地配置为浏览器信任的 SSL/TLS 连接。
因此,HTTPS 侦听器配置如下: - 负载均衡器协议:HTTPS - 负载均衡器端口:443 - 实例协议:HTTP - 实例端口:80 - 密码:(默认策略) - SSL 证书:(--server-certificate-name 名称)
请注意,您不希望将端口443用作实例端口;如果这样做,那么就意味着您想要从ELB到实例的HTTPS,这通常是不需要的。 (一些安全站点需要此操作,但这是一个不同的主题/存储。)因此,上述配置使ELB处理SSL终止;对于实例上的Node.js服务器,它只会在端口80上接收普通HTTP请求:
client ---> HTTP ---> ELB port 80 ---> HTTP ---> server port 80
client ---> HTTPS --> ELB port 443 --> HTTP ---> server port 80

如果您的服务器需要知道原始请求是HTTP还是HTTPS,请查找AWS ELB将自动添加到请求中的X-Forwarded-For(和其他请求标头)链接
现在,一旦您配置好ELB,验证其是否正常工作是一个好步骤。首先,您可以使用openssl s_client来验证SSL握手是否正常:
$ openssl s_client -connect example.com:443
CONNECTED(00000003)
depth=3 /C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
verify error:num=19:self signed certificate in certificate chain
verify return:0
---
Certificate chain
 0 s:/OU=Domain Control Validated/CN=redmatterapp.com
   i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2
 1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2
   i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./CN=Go Daddy Root Certificate Authority - G2
 2 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./CN=Go Daddy Root Certificate Authority - G2
   i:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
 3 s:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
   i:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
subject=/OU=Domain Control Validated/CN=example.com
issuer=/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2
---
No client certificate CA names sent
---
SSL handshake has read 4929 bytes and written 456 bytes
---
New, TLSv1/SSLv3, Cipher is AES128-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : AES128-SHA
    ...
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---

以上显示我们成功完成了SSL握手,您可以看到它显示了带有GoDaddy名称的证书链;“服务器证书”部分应与您加载的“f6f65901b1708ae5.crt” PEM文件匹配。
HTTPS和DNS
现在来测试HTTPS部分;为此,我喜欢使用curl,如下所示:
$ curl -kv https://example.com/

-v选项会显示更多信息,特别是证书是否与DNS名称匹配;-k告诉curl忽略任何证书不匹配或有问题的情况,这样我们就可以看到有关事物的详细信息。

这就需要进行一些额外的配置。如果您在curl命令(或浏览器)中使用自动生成的ELB DNS名称,则很可能会看到安全警告/问题。为什么?因为HTTPS的一部分是验证您在URL中使用的DNS名称是否与服务器证书中的域名/主机名匹配,可以作为证书中的公共名称(CN),也可以作为DNS主题备用名称(SAN)之一。而您可能没有在向GoDaddy提交证书签名请求(CSR)时包含ELB DNS名称。

这意味着下一步是配置指向AWS ELB名称的DNS CNAME记录,例如:

www.example.com CNAME to aws-elb-1.elb.amazonaws.com

如果您正在使用AWS进行DNS,则可以使用例如Route 53来执行此操作。然后,您可以重试您的curl命令(或浏览器)。
curl -kv https://www.example.com/

另一个需要注意的重要事项(您可以从curl -v输出中看到)是观察Host请求头的内容。一些HTTP服务器(即在后端实例上运行的服务器)对那里的值非常挑剔;他们希望Host头与其配置中的某些内容匹配,如果不匹配,则可能拒绝请求。
另一个常见的陷阱是:
  • HTTP 503服务不可用:后端服务器已满
如果您从ELB收到此响应,则通常意味着您的后端服务器未响应或未能响应ELB健康检查。请仔细检查用于ELB健康检查的端口和路径/ URL是否正确,并且任何防火墙或AWS安全组是否允许ELB与您的实例进行连接。
因此,现在您应该拥有将端口80(HTTP)和端口443(HTTPS)转发到后端实例的ELB。并且您在DNS中为“www.example.com”设置了指向该ELB名称的CNAME记录。还剩下什么?

HTTP重定向

强烈建议配置您的HTTP服务器,始终将重定向到相同URL的HTTPS等效版本。为什么?

为了保护客户端和服务器之间的数据安全,现在预期使用SSL/TLS。事实上,越来越多的浏览器自动首选HTTPS,并且只会勉强使用HTTP作为备选方案。像Chrome(以及其他浏览器)这样的浏览器希望尽可能避免此HTTP回退,因此他们引入了机制,例如HTTP Strict Transport Security:让您的网站告诉浏览器仅对该网站使用HTTPS,永不使用HTTP。此外,这也是一个更好的营销故事。实际上,如果您使用HTTPS,则可以预期从客户/用户那里得到负面反应。

使用HTTPS保护您网站的所有流量还可以防止他人使用其DNS名称和ELB。您为“www.example.com”设置了指向“aws-elb-1.elb.amazonaws.com”的CNAME记录。但是,如果我还为“www.evilco.com”创建自己的CNAME,该CNAME也指向相同的“aws-elb-1.elb.amazonaws.com”会怎样呢?访问“www.evilco.com”的人将看到您的网站,并认为它是我的!
通过强制所有网站流量都使用HTTPS,您可以强制所有HTTPS客户端验证服务器(即您的“f6f65901b1708ae5.crt”文件)提供的证书是否包含与客户端在其URL中使用的域名匹配的公共名称(CN)或DNS主题备用名称(SAN)。显然,您的证书不包含“www.evilco.com”的CN或DNS SAN,因此该验证过程将失败,用户会发现有可疑之处。但是,如果您也允许HTTP流量,则可能会发生这种现象,而您查看网站日志时将永远不会知道!

附言

我个人没有使用过AWS证书管理器或CloudFront(因此无法对它们发表评论),但我已经多次使用上述过程,为多个域名的多个证书提供服务。

希望这可以帮到你!


感谢您非常详细的回答。我尝试了所有这些,但仍然无法使用https。我还删除了CloudFront分发,因为看起来我不需要它。f6f65901b1708ae5.crt是PEM格式的,当我运行上面的命令时,我看到它是针对我的域名的。我的Node应用程序正在侦听端口8080。我已经有一个iptables规则将流量从端口80转发到端口8080。这适用于http流量。我添加了另一条规则,将流量转发到443到8080。然而,https仍然无法正常工作... - Mark
“not working” 究竟是什么意思?您的 ELB HTTPS 监听器使用哪些端口(前端、后端/实例)?有没有 ELB 的 DNS 名称/IP 地址,我可以自己测试,以便提供更多帮助? - Castaglia
我已经更新了我的帖子,并添加了一些关于必要的DNS CNAME配置的部分,以及调试/测试步骤。 - Castaglia
让我们在聊天中继续这个讨论 - Castaglia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接