How to serve custom domains pointing to a subdomain in Saas App - dns

I want to create an example SaaS app, whereby users are able to signup, create web pages, use templates and/or customize them with custom css, serve their web pages off custom domains.
I was considering saving the templates on S3/other CDNs, along with media/stylesheets/js files. While all are technically possible (practical? that could be debatable). Anyways, I was having a hard time figuring out how websites would be served off custom domains in this case? For instance, when they sign up, they could get a subdomain.domain.com address. However, how do they point customerdomain.com so that when customerdomain.com is entered, it serves same content as customerdomain.domain.com, and URL remains customerdomain.com
Additionally, if I want to have a "feature" whereby, custom domains could be a paid feature. How would I restrict it to paid users only?
Normally when, we setup websites, we specify it in virtual host config file (apache), and give it aliases, so it looks for and serves those aliases. In this case, I do not want to have a separate vhost file for each person who signs up. Is there an alternative? How can I program this? Are there any gotchas to be aware of?
One solution that I have seen is to have the server serve a wildcard domain i.e *.domain.com, and a separate vhost for each custom domain, however I would prefer to avoid if I can.
Thanks.

The custom domain is usually done through a CNAME DNS record (sort of a symlink for DNS records). You tell your customer (who is usually in control of customerdomain.com) to create a CNAME record saying that customerdomain.com is an alias for customerdomain.domain.com. Then you need to set up your own server to interpret requests to customerdomain.com the same as it would treat requests to customerdomain.domain.com.
Depending on how you serve your subdomains, this can be done in a few different ways.
If you have a vhost file for every single customer, you need to add a "ServerAlias" directive for the custom domain your client has provided.
If you are coding the entry point through your own application server (say, reading the "Host" HTTP header from PHP and then setting the customer name from that) then you need to adjust that code accordingly to interpret requests for external domains according to your own database of custom domains. You can even use straight DNS for this!
Something on the lines of:
if http "host" header does not end in domain.com:
cname = get_cname_record(http "host" header value)
if cname does not end in domain.com:
return error 404
else:
site = first part of cname
else:
site = first part of http "host" header
Then you can use DNS as your "custom domain database". Make sure you are using a DNS cache though, as those queries will be performed on every single request.

The accepted answer is good but it doesn't show the full picture.
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 CNAME app.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 on customer.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 or customer2.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 or X-Serve-For: customer2.com or whatever the Host 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

Related

Nginx configuration does not work

I have created few subdomains for my domain like api.example.com, dev.example.com and www.example.com. For every subdomain I created an virtualhost in Nginx.
But now the problem is when I visit a domain which does not exist it should be redirected to www.example.com. But this is not the exist instead I am getting an error page that the sub domain does not have an secure connection. Since I am using Let's Encrypt, I get this message all the time for sub domains which is incorrect. I contacted my DNS provider and they told me your settings are correct you have to correct your web server configuration. They added a CNAME.
Now I do not know how to add this in my nginx configuration.
So... you type the https ://incorrect.example.com in your browser?
If so, I think the problem cannot be solved.
In the article (https://community.letsencrypt.org/t/can-i-use-letsencrypt-in-more-than-one-subdomain/16588/8) they said
Let's Encrypt does not currently offer "wildcard" certificates. So you will need to be able to list all the domains you want a certificate for, you can't (as you can with some of the pricier paid certificates) get just one that works for every possible name in your domain. With Let's Encrypt you'd need to issue new certificates for any new names you needed.
That showed you can't set all the certificate of incorrect subdomains...
But if you just type "http ://incorrect.example.com", It can be success redirect without error page.
If error page continue occurring, please post your conf of nginx.
I see two separate questions -
security warning can be removed using a wildcard certificate from letsencrypt. Please see detailed instructions here
Redirect non-existent domains to www.domainname.com
You need multiple server sections in place -
Have server {} sections for each existent domain to port 443 (HTTPS port)
Have one server {} section for *.domainname.com to redirect to port 443 or www.domainname.com
If you are running an app that dynamically uses subdomains (for each customer) the app should also implement the redirection to www.domainname.com for non-existent subdomains.

Clarifying interaction of SSL, DNS, webserver

Please forgive the wishy washy nature of this question, I'm unsure how better to phrase it.
I have a nodejs server which will be accessed (HTTP + websockets) through a variety of third party DNSs by the third parties adding a new A record in their DNS entry pointing at my IP. I can find the origination third party DNS name by looking at the request headers. Node is then acting as a proxy and ultimately modifying the request headers/adding metadata before forwarding the request back to another url at the third party.
Could anyone explain please how SSL/TLS operates when the third party certificate is a wildcard cert for the origination DNS; how is the chain of encryption carried to node -> do I need to host a copy of the third party certificate on the node server? (Obviously I'd rather not). Can I use a third party's original SSL set up to any advantage?
Many thanks in advance!
DNS and HTTPS are fairly unrelated here. The client only uses DNS to find the web server's IP address. After that, the http protocol contains the Host name it is requesting in the Host header, as you have determined.
Your server will need an HTTPS certificate for each Host name that is will handle requests for, otherwise browsers will not be able to make a trusted connection to it. The certificate says "This server is authorized to handle requests for this host name".
In practice, though DNS and HTTPS are related, because if you control dns, you can issue a certificate. Let's Encrypt has made this very easy to set up.
I would not recommend sharing certificates with third parties, as that can be a bit of a pain, and it is harder to keep private keys secure if you are emailing them back and forth or something. Just issue your own certs for the third-party domains you need to serve.
My personal favorite solution for a case like yours is running a caddy server instance in front of my app to manage https certificates automatically, and proxy requests to your node backend. It can even issue certs dynamically as it receives requests.

Is it necessary to have an SSL cert for a domain that just forwards to another domain?

I have an application that uses account slugs for various accounts, of the format myapplication.com/{SLUG}, similar to how Github does it. This domain has an SSL cert and everything is fine.
However, I have some clients who purchased domain names that they give to clients that forwards them to their account page on my application.
For instance, let's say "John Smith" purchased domain johnsmithisawesome.com and he sets that domain up to forward to myapplication.com/johnsmithisawesome. It's mostly for branding purposes, so John can give out a cool/catchy domain name to clients which eventually sends them to my application.
Note that this redirect only happens once. I am not using it as a proxy, it is just a convenience method for folks to use a catchy domain that fully redirects them to the application domain. After that initial redirect, the client will be on the application domain and all requests go directly to the secured application, and they should not reference the insecure domain any more.
Also note that there are no security credentials being passed back/forth to the insecure domain. If the client has to login, they are doing it directly on the application domain and are not interacting with the insecure domain at all.
The flow essentially works like this:
User visits johnsmithisawesome.com in their browser
DNS for johnsmithisawesome.com uses an HTTP REDIRECT that points to myapplication.com/johnsmithisawesome
User's browser URL is now at myapplication.com/johnsmithisawesome
All further requests go through myapplication.com/johnsmithisawesome, there are no further requests going through johnsmithisawesome.com
I'm hoping this is explicitly clear in what I'm trying to accomplish, I know I'm not as well versed in the terminology.
Is there any security risk with not having an SSL cert on the domain that just forwards them to my application?
My assumption is it doesn't matter, because the actual application itself is protected with SSL (or TLS as the new standard), and I can't think of any security risk with using a domain to just forward, but wanted to check with some folks who know more about this than I do :)
Unfortunately, the short answer is yes, if you want to secure it, you need a separate SSL cert for the other domain too.
Consider the following. Alice (the enduser) enters johnsmithisawesome.com in her browser, the browser makes a request. A man-in-the-middle attacker Mallory captures the request, makes a request to https://yourapplication.com/johnsmithisawesome, which creates an SSL session between Mallory and your application. When Mallory receives a response, transforms all references to your application to http://johnsmithisawesome.com and returns the resulting (modified) page
to Alice.
What your application can see is t here was a request on https which it served, all is good. What Alice can see is she entered a domain and got the results, even the domain name in the URL bar matches what she wanted to see. What Mallory can see is all (presumably confidential) traffic between Alice and your application. The only hint for Alice is that there is no indication of SSL in the browser, but most users won't spot that.
This is called SSL Stripping, and there are ready-made tools to pull this off with a few clicks. Your scenario is a little bit special compared to the original idea, but it just helps the attacker.
In fact, Mallory doesn't even need to do this. Without a certificate for johnsmithisawesome.com, Mallory can just replace the response and that's it, he doesn't need to strip SSL strictly speaking, because there wasn't any SSL. :)
This is what the HSTS response header was invented for, and should be sent by both your application and johnsmithisawesome.com during redirection.
As long as you will not access the forwarding domain with HTTP you will technically not need to have a certificate, i.e. the redirect will work. Only, when you want to access the forwarding domain with HTTPS you technically need a certificate for this domain, even if all what is done on this domain is to forward to somewhere else. Otherwise the end user will get warnings in the browser and the forwarding will not happen.
But, even if it is not technically required it might still make sense to make use of HTTPS on the forwarding domain for security reasons.
All further requests go through myapplication.com/johnsmithisawesome, there are no further requests going through johnsmithisawesome.com
Note that this is only the technical view of what works and not works.
I interpret this as there is only a single requests to the clients domain which results in a redirect to the final domain. And that no sensitive data will be shared with the clients domain but only with the final (secured) domain.
In this case the main attack vector is an man in the middle attack which changes the target of the redirect, for example from myapplication.com/johnsmithisawesome to an attacker controlled domain and URL like myapplication-secure.com/johnsmithisawesome. With a properly named domain the attacker can surely trick the user into believing that he reached the proper site.
Such attack can be mitigated by protecting the redirect with HTTPS and security-wise this is the best option. But this means that the client should access the clients domain with HTTPS in the first place, because otherwise the attacker could hijack the initial request and change the redirect. If one accepts this risk and hopes that attacker is not present during the first request one might either redirect to HTTPS and add an HSTS header to enforce future use of HTTPS on the clients domain or one could make a 301 redirect to the final domain instead a 302 redirect. With 301 the browser will cache the redirect and will not ask the clients server again but go straight to the final domain on the next visit, thus bypassing an attacker which managed to intercept requests to the clients domain.
DNS for johnsmithisawesome.com uses an HTTP REDIRECT that points to myapplication.com/johnsmithisawesome
That's not how it works.
Technically DNS can not issue HTTP redirects. DNS is only used to resolves the hostname to an IP address. The HTTP server at this address then can do the redirect at the HTTP level.
Some DNS providers still offer such a HTTP redirect. But this works by resolving the hostname to an IP address the DNS provider owns and running a special HTTP server at this IP address which then does the configured HTTP redirect. Sometimes they let the customer configure if this will be a 301 or 302 redirect and sometimes not. And usually there is no way for configure a certificate in this kind of setup anyway.

Solution to point customers domains at our SAAS app

I have a SAAS application on node express. The app creates tenant sub-domains for each sign up like mybiz.ourgreatapp.com.
We want to allow the users to point a custom domain at that account/url, ie a domain they have bought.
somedomainibought.com---->mybiz.ourgreatapp.com
We also need that domain to be HTTPS.
Uur domain, and wildcard sub domains are SSL.
So: https://mybiz.ourgreatapp.com: works.
We have tried to solve the problem using https://www.cloudflare.com which works for our sub-domain routes but we can't set it up to use customers domains and point them at those routes.
How can we achieve this?
To create certificaes for different domains
You need to solve 2 problems:
that your customers should point their domains to your IP/domain in order to use your service. Solution for it: you can create a dedicated domain like cname.ourgreatapp.com. If customer wants to use his own custom domain - he should point his domain (in 99% cases it will be customer's subdomain) to your CNAME. In 1% cases when customer would like to attach domain - better to provide also public IP for such customers. As not all DNS providers support using CNAME for 1st level domain
that you need to issue certificate for the customer. When you issue a certificate, you must prove your rights for this domain (different certificates authorities provide multiple options in order to do it). Luckily when you already have a customer who already pointed to your CNAME - you can use "HTTP validation type" (when you should provide specific text at some URL and CA verifies that secret value is there by doing GET request. For example, free LetsEncrypt certificate provider will verify specific value at the URL customersdomain.com/.well-known/acme-challenge/)
This second step I suppose is too heavy to be handled by your node-express application. Better to extract SSL-signing logic to a separate layer, even to a separate micro-service.
In order to automate this process, you can create a service which will request SSL for all your customer's domains (for example by implementing ACME protocol, requesting certificates from LetsEncrypt). And setup proxy server which will proxy requests from all customer's domains, getting data and signing the responses with proper certificates.
Also you can check third party solutions which already do this from the box. For example, Kilo SSL, Cloudflare etc. I didn't try doing it with Cloudflare yet, but just checked that it's easy to do with KiloSSL
You can set up a reverse proxy (using nginx for example). Configure a virtual host with server name somedomainibought.com that does a proxy pass to https://mybiz.ourgreatapp.com/. Configure this virtual host to use ssl and listen on port 443.
Then, the Zone file for the domain somedomainibought.com must be configured with an A record pointing to the IP address of your reverse proxy.

Does Azure offer https for "cloudapp.net"?

One great advantage of using Azure Websites is that I can get secure HTTP (HTTPS) without doing nothing: I simply type https://xyz.azurewebsites.net and it works. I don't have to worry about certificates because I use the subdomain that Azure gives me (in the example it would be xyz)
So, what I usually do is that people come by through some registered domain I have, eg. http://www.my-application-homepage.com, and there, if they want to use my application, I redirect them to the subdomain at azurewebsites.net, using HTTPS.
Now, having said that:
I'm in need of upgrading to Azure Cloud Services or Azure Virtual Machines, because these have capabilities that Azure Websites don't . These two also offer a free subdomain: xyz.cloudapp.net, but my question is: will I get HTTPS there too? and how?
I searched in google for some cloudapp examples and what I tested was the following:
1) Connect through HTTP (ie. type http://xyz.cloudapp.net). Result: worked
2) Connect through HTTPS (ie. type https://xyz.cloudapp.net). Result: didn't work (chrome gave ERR_CONNECTION_TIMED_OUT)
No. HTTPS is not offered for .cloudapp.net domain as of today. Also since you don't own .cloudapp.net domain, I don't think you can buy a SSL certificate for that. If you want you could create a self-signed certificate and use that.
I would walk through the documentation listed here:
http://azure.microsoft.com/en-us/documentation/articles/cloud-services-configure-ssl-certificate/
Since you're getting a timeout with HTTPS (rather than a certificate error), check that you have a HTTPS endpoint defined in ServiceDefinition.csdef.
Additionally, be aware that the redirect-to-subdomain approach isn't much more secure than using a self-signed certificate. The reason browsers reject self-signed certs is that they are vulnerable to spoofing attacks: a user can't detect if an attacker has, for example, hijacked the DNS to point to his IP address instead of yours, where he hosts a facade of your site that just collects passwords or whatever.
In your scenario, the cloned site could redirect to another a second clone, one that is a facade of your cloudapp.net site. It could be even be secured with the attacker's SSL certificate. Unless the user was trained to recognize the host name of the real cloudapp.net, she wouldn't know she was on the attacker's "secure" site.
** Update: This method is not valid as well, we got the certificate revoked after one week using it **
We use this approach for staging/dev servers:
If you don't want to use a self-signed certificate, one option is to purchase a cheap SSL certificate, e.g.:
https://www.ssls.com/comodo-ssl-certificates/positivessl.html
Then once you need to approve it you have to ask support to change the approver validation process: instead of sending an email to a admin#mydomain.cloudapp.net you can ask to change the validation process to placing a given file with a given file in the root of your website (you have to ask in the support / chat room about that option).
More info:
https://support.comodo.com/index.php?/Default/Knowledgebase/Article/View/791/16/alternative-methods-of-domain-control-validation-dcv

Resources