多租户Web应用程序的自定义域名

13
我正在开发一个应用程序 (RoR + Heroku),它允许用户创建自己的网站,可以使用我的子域名 (pagename.myapp.com) 或者使用他们自己的域名 (pagename.com)。
其中一个重要点是,这个选项是我的业务关键:子域名是免费计划,自定义域名是付费计划。因此,我有一个表格存储每个用户的自定义域名,并检查该页面是否处于活动状态 (存在并已支付定额)。
为此,我需要给用户提供指向我的服务器的能力。我们都知道,Heroku不推荐使用DNS A-Records
另外,我希望将这个功能抽象化,以便在将来能够切换我的基础架构 (从Heroku到AWS) 而不必要求所有用户更改他们的DNS区域。考虑到这一点,我认为最好的选项是运行像EC2代理这样的东西 (使用AWS Elastic IP),它将使我拥有这个IP的所有权。这个代理应该重定向到proxy.myapp.com,并且我将在应用程序级别解决请求。
由于我没有找到关于这个问题的明确信息,我不确定这个假设是否是最好的解决方案以及如何设置代理(使用哪种类型的代理?Nginx可能吗?)。
说了这些,我想询问解决这个“常见”功能的建议/最佳实践。
谢谢。
3个回答

5
你想要做的事情相当容易实现。你对设置代理的假设是正确的。Nginx或haproxy都可以很好地完成这项工作(我个人会选择haproxy)。以下是你可能遇到的一些问题:
1. 在代理服务器上更改主机头可能会导致最终的Web应用程序生成不正确的链接。你可以使用相对路径来解决这个问题,但需要确保Web应用程序开发人员了解他们正在运行的环境。 - 用户连接到www.example.com(代理服务器) - 代理服务器连接到www.realdomain.com(Web应用程序) - Web应用程序有一个购物车链接。 www.realdomain.com/shoppingcart - 最终用户单击该链接,但链接是www.realdomain.com/shoppingcart而不是www.example.com/shoppingcart
2. 主机充当代理服务器的成本。这可能会非常快速地失控。例如,你是否需要冗余?如果是这样,你打算如何实施?你计划进行SSL终止吗?如果是,则必须增加CPU数量以适应额外的负载。你是否想从代理到Heroku建立安全连接?如果是,那么你也需要增加CPU数量。根据并发连接数的多少,你可能还需要添加额外的RAM。
3. Heroku定期更改其负载均衡器。这很重要,因为你的代理服务需要每60秒重新加载配置/更新Heroku实例的IP地址。根据我的经验,它们可能每天更改一两次,但它们使用的DNS条目具有60秒的TTL。这意味着你应该确保能够每60秒更新配置。
我公司已经做了类似的事情将近一年了。我们使用haproxy,只需定期重新加载配置即可。我们从未遇到过故障或对最终用户的中断。Nginx也是一个非常好的产品。它具有内置的DNS缓存,因此如果你选择这条路线,则需要确保正确配置DNS缓存TTL为60秒。

2

我喜欢dtorgo提供的答案,并且他提到了TLS终止,这是许多关于自定义域的在线教程都没有涉及到的。

我将详细介绍如何为您的SaaS实现自定义域功能,同时处理TLS / HTTPS。

如果您的客户只是将CNAME指向您的域名或创建指向您的IP的A记录,并且您不处理这些自定义域的TLS终止,则您的应用程序将不支持HTTPS,而没有HTTPS,您的应用程序将无法在这些自定义域上的现代浏览器中运行。

您需要在Web服务器前设置一个TLS终止反向代理。此代理可以在单独的机器上运行,但也可以在与Web服务器相同的机器上运行。

CNAME vs A记录

如果您的客户想要在其子域上使用您的应用程序,例如app.customer.com,他们可以创建一个CNAME app.customer.com 指向您的代理。

如果他们想要在他们的根域名下使用你的应用,例如 customer.com,那么他们必须在 customer.com 上创建一个指向你代理 IP 的 A 记录。确保这个 IP 永远不会改变!

如何处理 TLS 终止?

为了使 TLS 终止工作,你需要为这些自定义域名发放 TLS 证书。你可以使用 Let's Encrypt 来完成。你的代理将看到传入请求的 Host 头,例如 app.customer1.comcustomer2.com 等等,然后通过检查 SNI 来决定使用哪个 TLS 证书。

代理可以设置自动为这些自定义域名发放和更新证书。对于来自新自定义域名的第一个请求,代理将发现它没有适当的证书。它将向 Let's Encrypt 请求一个新的证书。Let's Encrypt 将首先发出一个挑战来查看你是否管理该域名,由于客户已经创建了一个 CNAME 或 A 记录指向你的代理,这告诉 Let's Encrypt 你确实管理该域名,并允许你为其发放证书。

为了自动发放和更新证书,我建议使用 Caddyserver、greenlock.js、OpenResty(Nginx)。

tl;dr 对于这里发生的事情; Caddyserver 监听 443 和 80 端口,自动接收、发放和更新证书,并将流量代理到您的后端。

如何在我的后端中处理它

您的代理正在终止 TLS 并将请求代理到您的后端。但是,您的后端不知道请求背后的原始客户是谁。所以您需要告诉您的代理在代理请求时包含附加的标头来识别客户。只需添加 X-Serve-For: app.customer.comX-Serve-For: customer2.com 或任何原始请求的 Host 标头。

现在当您在后端接收代理请求时,可以读取此自定义标头,从而知道请求背后的客户是谁。您可以基于此实现逻辑,显示属于该客户的数据等。

更多内容

在代理服务器群集前面放置负载均衡器以提高可用性。您还必须使用分布式存储来存储证书和 Let's Encrypt 挑战。使用 AWS ECS 或 EBS 实现自动恢复,以防出现故障,否则您可能会在半夜被叫醒手动重启机器或代理。

如果您需要更多细节,可以在 Twitter 上私信我 @dragocrnjac


1
这个答案是我做程序员已经四年多了第一次真正理解 DNS。谢谢! - greenie-beans

0

你的许多客户是否希望在其域名顶级使用您的应用程序?例如example.com而不是theapp.example.cpm?如果不是,我建议让他们CNAME到proxy.myapp.com,然后再将其CNAME到myapp.herokuapp.com。这样,您就可以更新proxy.myapp.com而不会影响客户。

如果您确实需要apex或A记录支持,则需要为Heroku应用程序设置Nginx反向代理。请记住,如果您需要为客户域提供HTTPS支持,则需要在代理上进行某种证书管理。


1
在Heroku中,CNAME不起作用。他们通过虚拟主机进行路由。他们确实允许您使用自己的DNS名称,但这就是OP所问的问题。为了使用自己的DNS名称,他们会额外收费。 - dtorgo

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