由一个 Rails 应用程序支持的多个域
我正在创建一个类似博客的应用程序,我们允许客户使用他们自己的自定义域名,例如domainexample.com,因此每个不同的域都提供相同的应用程序,但内容不同。
然而,我正在努力弄清楚如何在生产服务器上进行设置。如果我的生产服务器有静态 IP,那么我当然可以在每个域上将 a 记录设置为生产服务器的 IP。
但是如果生产服务器没有静态IP怎么办?例如,如果我们想将其托管在heroku 或engineyard 上?我在网上看到了一些需要使用重写规则的解决方案,但它们需要服务器重新启动,并且无法在新用户注册时真正动态地添加和删除新域。有谁知道有什么好的解决方案可以让多个域访问一个 Rails 应用程序吗?
I am creating a blogging-like application where we allow our customers to use their own custom domain names such as domainexample.com, so each different domain serves the same application but with different content.
However I am struggling to figure out how to set this up on a production server. If my production server has a static IP then I can surely just set an a-record on each domain to the ip of the production server.
But what if the production server does not have a static IP. For example if we want to host it on heroku or engineyard? I have seen a few solutions online that require using rewrite rules but they require server restarts and cant really dynamically add and remove new domains as new users sign up. Does anyone know any good solutions to let multiple domains hit one rails app?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Heroku 不是您唯一的选择。如果您可以预测客户的域,请查看此。如果不能,Rails 会路由 约束 和接受的答案的组合上面链接的问题应该可以让您到达您需要去的地方。听起来你不想重新启动你的服务器——所以不需要编辑路由。您还可以将域作为模型的一部分,或者区分在控制器级别或使用 URL在你的网络服务器层重写。
在我看来,问题是 Rails 在这里打破了它对配置的看法。从多个域提供服务的方式有很多。这可能是一种内在的复杂性,但 Rails 指南至少可以记录一种可能的解决方案。
Heroku isn't your only option. If you can anticipate your customer's domains, have a look at this. If you can't, Rails routes constraints and a combination of the accepted answer to the question linked above should get you where you need to be going. Sounds like you wouldn't want to restart your server--so no editing of the routes. You might also make domains part of your models, or distinguish at the controller level or use URL rewriting in your web-server layer.
The problem, as I see it, is that Rails breaks its mantra of opinion over configuration here. There are many ways of serving up from multiple domains. That might be an intrinsic complexity, but the Rails Guides could at least document one possible solution.
如果您的客户只是对您的域进行 CNAME 或为您的 IP 创建 A 记录,并且您不处理这些自定义域的 TLS 终止,则您的应用程序将不支持 HTTPS,如果没有它,您的应用程序将无法在现代浏览器中运行这些自定义域。
您需要在网络服务器前面设置 TLS 终止反向代理。该代理可以在单独的计算机上运行,但您也可以在与网络服务器相同的计算机上运行它。
CNAME 与 A 记录
如果您的客户希望将您的应用放在他们的子域(例如
app.customer.com
)上,他们可以创建一个指向您代理的 CNAMEapp.customer.com
。如果他们希望将您的应用放在其根域(例如
customer.com
)上,那么他们必须在customer.com
上创建一条指向您代理 IP 的 A 记录。确保此 IP 永远不会改变!如何处理 TLS 终止?
要使 TLS 终止发挥作用,您必须为这些自定义域颁发 TLS 证书。您可以使用 Let's Encrypt 来实现这一点。您的代理将看到传入请求的
Host
标头,例如app.customer1.com
或customer2.com
等,然后它将通过检查 SNI 来决定使用哪个 TLS 证书。可以将代理设置为自动颁发和续订这些自定义域的证书。在来自新自定义域的第一个请求时,代理将发现它没有适当的证书。它会要求 Let's Encrypt 提供新证书。 Let's Encrypt 首先会发出一个质询,看看您是否管理该域,并且由于客户已经创建了指向您的代理的 CNAME 或 A 记录,这告诉 Let's Encrypt 您确实管理该域,并且它会让您为它。
要自动颁发和续订证书,我建议使用 Caddyserver、greenlock.js、OpenResty (Nginx)。
tl;博士关于这里发生的事情;
Caddyserver 监听 443 和 80,它自动接收请求、颁发和更新证书,将流量代理到您的后端。
如何在我的后端处理它
您的代理正在终止 TLS 并将请求代理到您的后端。但是,您的后端不知道请求背后的原始客户是谁。这就是为什么您需要告诉代理在代理请求中包含附加标头以识别客户。只需添加
X-Serve-For: app.customer.com
或X-Serve-For: customer2.com
或任何Host
标头原始请求的。现在,当您在后端收到代理请求时,您可以读取此自定义标头,并且知道谁是该请求背后的客户。您可以基于此实现您的逻辑,显示属于该客户的数据等。
更多
将负载均衡器放在代理队列前面,以获得更高的可用性。您还必须使用分布式存储来存储证书和 Let's Encrypt 挑战。如果发生故障,请使用 AWS ECS 或 EBS 进行自动恢复,否则,您可能会在半夜醒来手动重新启动机器或代理。
或者,最近出现了一些类似 this 的服务,允许您向应用程序添加自定义域,而无需自己运行基础架构。
如果您需要更多详细信息,可以在 Twitter 上私信我 @dragocrnjac
If your customers just CNAME to your domain or create the A record to your IP and you don't handle TLS termination for these custom domains, your app will not support HTTPS, and without it, your app won't work in modern browsers on these custom domains.
You need to set up a TLS termination reverse proxy in front of your webserver. This proxy can be run on a separate machine but you can run it on the same machine as the webserver.
CNAME vs A record
If your customers want to have your app on their subdomain, e.g.
app.customer.com
they can create a CNAMEapp.customer.com
pointing to your proxy.If they want to have your app on their root domain, e.g.
customer.com
then they'll have to create an A record oncustomer.com
pointing to your proxy's IP. Make sure this IP doesn't change, ever!How to handle TLS termination?
To make TLS termination work, you'll have to issue TLS certificates for these custom domains. You can use Let's Encrypt for that. Your proxy will see the
Host
header of the incoming request, e.g.app.customer1.com
orcustomer2.com
etc., and then it will decide which TLS certificate to use by checking the SNI.The proxy can be set up to automatically issue and renew certificates for these custom domains. On the first request from a new custom domain, the proxy will see it doesn't have the appropriate certificate. It will ask Let's Encrypt for a new certificate. Let's Encrypt will first issue a challenge to see if you manage the domain, and since the customer already created a CNAME or A record pointing to your proxy, that tells Let's Encrypt you indeed manage the domain, and it will let you issue a certificate for it.
To issue and renew certificates automatically, I'd recommend using Caddyserver, greenlock.js, OpenResty (Nginx).
tl;dr on what happens here;
Caddyserver listens on 443 and 80, it receives requests, issues, and renews certificates automatically, proxies traffic to your backend.
How to handle it on my backend
Your proxy is terminating TLS and proxying requests to your backend. However, your backend doesn't know who is the original customer behind the request. This is why you need to tell your proxy to include additional headers in proxied requests to identify the customer. Just add
X-Serve-For: app.customer.com
orX-Serve-For: customer2.com
or whatever theHost
header is of the original request.Now when you receive the proxied request on the backend, you can read this custom header and you know who is the customer behind the request. You can implement your logic based on that, show data belonging to this customer, etc.
More
Put a load balancer in front of your fleet of proxies for higher availability. You'll also have to use distributed storage for certificates and Let's Encrypt challenges. Use AWS ECS or EBS for automated recovery if something fails, otherwise, you may be waking up in the middle of the night restarting machines, or your proxy manually.
Alternatively, there have been a few services like this recently that allow you to add custom domains to your app without running the infrastructure yourself.
If you need more detail you can DM me on Twitter @dragocrnjac