Technical Articles
Step-By-Step Guide to Custom Domains with Multitenant Multi-Target Applications
Note: If you’re renewing a custom domain certificate, check out this accompanying blog post.
Renewing Your Custom Domain Certificate For Multi-Target Applications
While there are a couple of other great posts for configuring custom domains in cloud foundry,
Demystifying SAP Cloud Platform Custom Domains
Fun and Games with SAP Cloud Domains
… and with all the emphasis on creating multitenant SaaS solutions that integrate with SAP’s cloud offerings (read, make money), I wanted to make sure I could put together an example of all that’s needed to support this exact scenario. The following is the set of steps I took to accomplish this exact task for a sample project I’m working on(conciletime.com). I didn’t take to time to generalize the domain name in this example so you’ll need to refactor it for your purposes. For the most part, a simple search/replace exercise in the project which can be found here is all that should be needed.
I’ll try to explain where this scenario is a bit more complicated than that for a single hostname, but most of this post will be screen shots and command line usage.
How multitenant apps work in the context of SAP Cloud Foundry.
Before we get started, it’s important to understand a bit of how these types of applications exist in our cloud foundry environment. Cloud Foundry application execution is in a SHARED namespace environment even when the app itself runs in it’s own isolated environment(container). As such some things need to be unique within the whole cloud foundry landscape. A multitenant app lives within a space within a subaccount within a master account on the landscape. It’s designed in a way that it can field subscription/unsubscription requests and be registered to do so. When a client subscribes to your multitenant application, they get a unique hostname that’s based on your custom domain. That way when your client’s users access their tenant(subscription) they can be indentified as belonging to that client. I’m using client here to mean the client of the SaaS solution that you(likely a SAP partner/customer) have built and not you the customer of SAP. Each client will be given their own subaccount of your master account so that they can connect to their own identity provider(and manage their own users). Each subaccount has it’s own subdomain which is an identifier used in the authentication mechanism and must be unique within the lanscape. It is this subdomain name concatenated with your custom domain name that forms the client’s hostname for their subscription. Again, I’ll use my sample project to illustrate.
Developing Multitenant Business Applications in the Cloud FoundryEnvironment
www.conciletime.com = Main web entry point for static content.
conciletime.conciletime.com = Hostname for administrative use of the application.(the subdomain conciletime is used for the main subaccount where the actual application lives. It happens to be the same as the domain.com name, but doesn’t have to be)
client1.conciletime.com = A subscriber client that has a subaccount with the subdomain name client1. Note again that this subdomain name must be unique within the entire landscape so once claimed nobody else can use it. For this reason, don’t be surprised if client1 is already taken. This is likely to occur if your client has subscribed to any other multitenant app on the landscape.
ct-client2.conciletime.com = A more reasonable convention to use when signing up subscribers to your multitenant application. By prefixing the client with “ct-” makes it more likely that the subdomain won’t already be taken, but doesn’t guarantee it. You’ll need to keep this in mind as well.
By this point you should realize that you’re going to need to support *.conciletime.com because you can’t know ahead of time what subdomains will be available for your clients. This is important when it comes to purchasing the SSL certificate you’ll need to protect your application.
Official docs.
Creating Custom Domains with TLS/SSL Server Authentication
Buy custom domain quota
In order to create a custom domain in Cloud Foundry you need a quota for doing so. Depending on your account you might be able to do this yourself(CPEA) but more likely you’ll have to purchase this from your SAP salesperson.
Buy the domain
We’ll assume you’ve already done this and have access to the DNS administrative tool or access to the IT person who does. There are plenty of domain registrars out there, I used GoDaddy.
Create the custom domain
If you’re scared of command line tools you’ll need to stop here because there seems to be currently no way to do this through the cloud cockpit UI. If you don’t have the cf cli got get it for your platform.
Getting Started with SAP Cloud Platform Cloud Foundry Environment
- Download and install the Cloud Foundry CLI
IMPORTANT: Dowload and install the custom domain CF CLI Plugin.
- Custom Domain Self-service – Manage TLS server certificates and client authentication trust for custom domains
Follow the documentation for details.
Once you’ve got things installed, get connected to your account landscape’s api endpoint and login.
Target the organization where you’ll create your custom domain (don’t forget you need a quota, look above for info).
cf t -o ConcileTime
Now create your domain name in your organization.
cf create-domain ConcileTime conciletime.com
Creating domain conciletime.com for org ConcileTime as andrew.lunde@sap.com... OK
cf domains
Getting domains in org ConcileTime as andrew.lunde@sap.com... name status type cfapps.us10.hana.ondemand.com shared cert.cfapps.us10.hana.ondemand.com shared apps.internal shared conciletime.com owned
Generate private key and CSR
Now you need to create a special domain key. This is an encryption key that will be used to create your Certificate Signing Request(CSR). You’ll need to supply some details and list the domain names you’ll use with the resultant certificate. “C=US, ST=State, O=ConcileTime, CN=*.conciletime.com” Be sure that the CN value contains an asterix(*) or the certificate won’t work for all possible hostnames generated for your domain name.
cf custom-domain-create-key ComodoConcileTimeKey "C=US, ST=State, O=ConcileTime, CN=*.conciletime.com" conciletime.com --verbose
Command: custom-domain-create-key Organisation: ConcileTime (4d641712-8d17-45c6-adca-65c4f61e4202) API Endpoint: https://api.cf.us10.hana.ondemand.com/ Default API Server: https://custom-domain-certificates-api.cf.us10.hana.ondemand.com/ Key:ComodoConcileTimeKey Subject: C=US, ST=State, O=ConcileTime, CN=*.conciletime.com Domain Names: Host Names: conciletime.com Are you sure to generate this key in the system? (y/N) y DEBUG:2019/07/30 14:22:07 client.go:100: POST to https://custom-domain-certificates-api.cf.us10.hana.ondemand.com/api/v1/organizations/4d641712-8d17-45c6-adca-65c4f61e4202/identities DEBUG:2019/07/30 14:22:07 client.go:105: Request: [{ ComodoConcileTimeKey C=US, ST=State, O=ConcileTime, CN=*.conciletime.com [conciletime.com] }] DEBUG:2019/07/30 14:22:14 client.go:129: HTTP Status: 202 DEBUG:2019/07/30 14:22:14 client.go:130: Response: [{"guid":"173006fd-5f0d-4ac4-a3e7-980c8739bfd6","alias":"ComodoConcileTimeKey","subject":"C=US, ST=State, O=ConcileTime, CN=*.conciletime.com","dns":["conciletime.com"]}] Successfully created key request with name: ComodoConcileTimeKey The private key will be generated in the system. This operation will take some time! Call custom-domain-get-csr now to get the corresponding certificate signing request cf custom-domain-get-csr ComodoConcileTimeKey csr.pem
Download the CSR
Now get the CSR with the following command. Save the certificate text somewhere safe as you’ll need it later.
cf custom-domain-get-csr ComodoConcileTimeKey comodo_conciletime_csr.pem
Command: custom-domain-get-csr
Organisation: ConcileTime (4d641712-8d17-45c6-adca-65c4f61e4202)
API Endpoint: https://api.cf.us10.hana.ondemand.com/
Default API Server: https://custom-domain-certificates-api.cf.us10.hana.ondemand.com/
—–BEGIN CERTIFICATE REQUEST—–
MIIC1DCCAbwCAQAwTzELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVN0YXRlMRQwEgYD
VQQKEwtDb25jaWxlVGltZTEaMBgGA1UEAwwRKi5jb25jaWxldGltZS5jb20wggEi
…
UrrQ4Wo0y3VW41u4o7YGSVJlWQ5GNxQOVbfdlzLJoLCIpb7nQgWb0NnWhC0ap5Qb
n8BbI37SEow=
—–END CERTIFICATE REQUEST—–
Send the CSR to get signed
Purchasing a wildcard SSL certificate.
I’m going to assume you already have a domain name that you can use for this purpose. If you need to you can have your DNS administrator create a new subdomain on your domain name (ex. sapcloud.conciletime.com) but to keep things as clear as possible, I’ll create a wildcard certificate for a single level of domain matching on my domain name. It is possible to create a SSL wildcard certificate for multiple levels of subdomain(ex. *.*.conciletime.com) but let’s keep it simple.
You’ll need to grab your manager’s credit card for the following. While it’s possible to create SSL certificates using your own custom certificate authority, that’s beyond the scope of this post and requires that every system that accesses your application install and trust your certificate authority(something IT admins are reluctant to do). Anyway, the point it to look like a legitimate business. Here’s the link to official docs.
Creating Custom Domains with TLS/SSL Server Authentication
You’re looking for the Positive SSL Wildcard certificate.
Add it to your cart.
Put in your Billing Address and Credit Card Detail (Omitted). Confirm and Complete the Order.
You’ll be prompted to set a password for your account with Comodo.
Next generate your certificate. Click GENERATE CERT NOW
Select Order Type.
I used the CNAME method for authentication. Basically they want you to prove that you are creating a certificate for a domain name that you actually own and not someone else’s.
Now take the plaintext of the CSR you generated above and paste it here in Step #4.
Select a couple other options and select OTHER as the server type. Click Continue.
Continue with the process. MAKE SURE THAT THE DOMAIN NAME HAS AN ASTERIX * !
Continue looking over the form and verify that everything is correct.
If all looks correct, Click Continue.
You’ve now completed the request.
Pay special attention to the instructions for creating a DNS CNAME entry. This is needed to confirm that you are authorized to makes requests for your domain name.
Log into or ask your IT admin to make a CNAME record in your domain administration tool. Again, I’ll use GoDaddy since that’s where I registered my domain.
You may have to wait a bit for the DNS change to trickle through the Internet. Then you can test to be sure the changes took.
dig _0A75XXXXXXXXXXXXXXXXXXB2E2932182.conciletime.com
; <<>> DiG 9.10.6 <<>> _0A75XXXXXXXXXXXXXXXXXXB2E2932182.conciletime.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 28823 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;_0A75XXXXXXXXXXXXXXXXXXB2E2932182.conciletime.com. IN A ;; ANSWER SECTION: _0A75XXXXXXXXXXXXXXXXXXB2E2932182.conciletime.com. 1800 IN CNAME xxxxx8fca937722e2ac4165f165fbcd0.xxxxx620f65fc3a390f19560c9816f30.xxxxxptoo9pw50e59ka5.comodoca.com. ;; AUTHORITY SECTION: comodoca.com. 600 IN SOA ns1.as48447.net. hostmaster.comodoca.com. 2019070802 1800 1200 1814400 5400 ;; Query time: 117 msec ;; SERVER: 192.168.1.254#53(192.168.1.254) ;; WHEN: Tue Jul 30 13:44:51 EDT 2019 ;; MSG SIZE rcvd: 250
After a few minute, check your email box. You should get an email with an attachment containing your certificate,
From: Sectigo Certification Authority <noreply@notifications.sectigo.com>
Date: Tuesday, July 30, 2019 at 1:48 PM
To: “Lunde, Andrew” <andrew.lunde@sap.com>
Subject: ORDER #25500XXXX – Your PositiveSSL Wildcard Certificate for *.conciletime.com
|
Download and unzip the files locally. You should see the 4 crt files mentioned in the email.
You’ll need to concatenate these files together before uploading them.
cat STAR_conciletime_com/AddTrustExternalCARoot.crt > comodo-conciletime-certchain.pem
cat STAR_conciletime_com/USERTrustRSAAddTrustCA.crt >> comodo-conciletime-certchain.pem
cat STAR_conciletime_com/SectigoRSADomainValidationSecureServerCA.crt >> comodo-conciletime-certchain.pem
cat STAR_conciletime_com/STAR_conciletime_com.crt >> comodo-conciletime-certchain.pem
Upload certificate
Use the cf upload command to upload the composite certificate bundle you just created.
cf custom-domain-upload-certificate-chain ConcileTimeDomainKey comodo-conciletime-certchain.pem
Command: custom-domain-upload-certificate-chain Organisation: ConcileTime (4d641712-8d17-45c6-adca-65c4f61e4202) API Endpoint: https://api.cf.us10.hana.ondemand.com/ Default API Server: https://custom-domain-certificates-api.cf.us10.hana.ondemand.com/ Key: ComodoConcileTimeKey Subject:CN=AddTrust External CA Root,OU=AddTrust External TTP Network,O=AddTrust AB,C=SE Issuer:CN=AddTrust External CA Root,OU=AddTrust External TTP Network,O=AddTrust AB,C=SE Validity:valid Not Before:Tue May 30 10:48:38 UTC 2000 Not After:Sat May 30 10:48:38 UTC 2020 -----BEGIN CERTIFICATE----- MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs ... 6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= -----END CERTIFICATE----- Subject:CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US Issuer:CN=AddTrust External CA Root,OU=AddTrust External TTP Network,O=AddTrust AB,C=SE Validity:valid Not Before:Tue May 30 10:48:38 UTC 2000 Not After:Sat May 30 10:48:38 UTC 2020 -----BEGIN CERTIFICATE----- MIIFdzCCBF+gAwIBAgIQE+oocFv07O0MNmMJgGFDNjANBgkqhkiG9w0BAQwFADBv MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF ... lQ9ew4IcH9Z35zyKwKoJ8OkLJvHgwmp1ocd5yblSYMgpEg7wrQPWCcR23+WmgZWn RtqCV6mVksW2jwMibDN3wXsyF24HzloUQToFJBv2FAY7qCUkDrvMKnXduXBBP3zQ YzYhBx9G/2CkkeFnvN4ffhkUyWNnkepnB2u0j4vAbkN9w6GAbLIevFOFfdyQoaS8 Le9Gclc1Bb+7RrtubTeZtv8jkpHGbkD4jylW6l/VXxRTrPBPYer3IsynVgviuDQf Jtl7GQVoP7o81DgGotPmjw7jtHFtQELFhLRAlSv0ZaBIefYdgWOWnU914Ph85I6p 0fKtirOMxyHNwu8= -----END CERTIFICATE----- Subject:CN=Sectigo RSA Domain Validation Secure Server CA,O=Sectigo Limited,L=Salford,ST=Greater Manchester,C=GB Issuer:CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US Validity:valid Not Before:Fri Nov 2 00:00:00 UTC 2018 Not After:Tue Dec 31 23:59:59 UTC 2030 -----BEGIN CERTIFICATE----- MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCB iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV ... LcmsJWTyXnW0OMGuf1pGg+pRyrbxmRE1a6Vqe8YAsOf4vmSyrcjC8azjUeqkk+B5 yOGBQMkKW+ESPMFgKuOXwIlCypTPRpgSabuY0MLTDXJLR27lk8QyKGOHQ+SwMj4K 00u/I5sUKUErmgQfky3xxzlIPK1aEn8= -----END CERTIFICATE----- Subject: CN=*.conciletime.com,OU=DomainControl Validated+OU=PositiveSSL Wildcard Subject Alternative Names: DNSName: *.conciletime.com DNSName: conciletime.com Issuer:CN=Sectigo RSA Domain Validation Secure Server CA,O=Sectigo Limited,L=Salford,ST=Greater Manchester,C=GB Validity:valid Not Before:Tue Jul 30 00:00:00 UTC 2019 Not After:Wed Jul 29 23:59:59 UTC 2020 -----BEGIN CERTIFICATE----- MIIGBTCCBO2gAwIBAgIRANVR1hWyMasqKrOMc28GWOQwDQYJKoZIhvcNAQELBQAw gY8xCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO BgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE3MDUGA1UE ... wwMFLeAxS6MYivKclw3oOz2GU3n1IK2cXlTeC6JO+zsl5l4l1AUYz4ykNKihnW+n IHnmckbC1GIAfN19OvaIE/CssI7VNdZQ4fGc3WKNDcYjseVEZpDaigMPTxDt8vIt DvzjLzuyws2zlLzw0oeJuml1YqC2MKo1u2wTlVhQMrd3rTQWi9GQhfJypqXac0Zm diGqTzgO96vxZwIHsYXQMLz5Eps7Yo8rLcfNnCxYzNhQM4rvCqRkg6klvOgmncue z6iP+ouzZAYm -----END CERTIFICATE----- Are you sure to import this certificate chain into the system? (y/N) y OK Server certificate is still not activated: First activate your server certificate with custom-domain-activate
Activate the certificate
cf custom-domain-activate ComodoConcileTimeKey conciletime.com --verbose
Command: custom-domain-activate Organisation: ConcileTime (4d641712-8d17-45c6-adca-65c4f61e4202) API Endpoint: https://api.cf.us10.hana.ondemand.com/ Default API Server: https://custom-domain-certificates-api.cf.us10.hana.ondemand.com/ Key alias: ComodoConcileTimeKey Domain Names: conciletime.com DEBUG:2019/07/30 15:15:18 client.go:81: GET to https://custom-domain-certificates-api.cf.us10.hana.ondemand.com/api/v1/organizations/4d641712-8d17-45c6-adca-65c4f61e4202/identities/ComodoConcileTimeKey DEBUG:2019/07/30 15:15:23 client.go:95: RESPONSE: [{"guid":"aa316360-c14a-4f9e-8830-ca3662c89f9f","alias":"ComodoConcileTimeKey","subject":"C=US, ST=State, O=ConcileTime, CN=*.conciletime.com","dns":["conciletime.com","*.conciletime.com"],"chain":"-----BEGIN CERTIFICATE-----\nMIIGBTCCBO2gAwIBAgIRANVR1hWyMasqKrOMc28GWOQwDQYJKoZIhvcNAQELBQAw\ngY8xCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO\nBgNVBAcTB... c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= -----END CERTIFICATE----- }] DEBUG:2019/07/30 15:15:23 client.go:324: IDENTITY FOUND: aa316360-c14a-4f9e-8830-ca3662c89f9f DEBUG:2019/07/30 15:15:23 client.go:100: POST to https://custom-domain-certificates-api.cf.us10.hana.ondemand.com/api/v1/organizations/4d641712-8d17-45c6-adca-65c4f61e4202/serverNames DEBUG:2019/07/30 15:15:23 client.go:105: Request: [{ conciletime.com {aa316360-c14a-4f9e-8830-ca3662c89f9f} {} []}] DEBUG:2019/07/30 15:15:25 client.go:129: HTTP Status: 200 DEBUG:2019/07/30 15:15:25 client.go:130: Response: [{"guid":"fa572e0e-f900-4c49-bf47-61ee3e1f78eb","name":"conciletime.com","state":"inprogress","tlsConfig":{"identityGuid":"aa316360-c14a-4f9e-8830-ca3662c89f9f"},"tlsClientTrustConfig":{}}] OK Activating conciletime.comin progress This operation can take some time. Use custom-domain-list to track the status
Create a CNAME mapping
First, double check that Cloud Foundry understands when to use your custom domain certificate. Run this cf command and verify that the Domain name with wildcard is listed and activated(in green).
cf custom-domain-list
Command: custom-domain-list Organisation: ConcileTime (4d641712-8d17-45c6-adca-65c4f61e4202) API Endpoint: https://api.cf.us10.hana.ondemand.com Default API Server: https://custom-domain-certificates-api.cf.us10.hana.ondemand.com Activated Certificates: 1 Activated Certificates Quota: 2 Domain Name: conciletime.com Key: ComodoConcileTimeKey Key Status: created, certificate chain uploaded Certificate Status: valid Client Authentication: disabled Custom Domain Status: activated Domain Name: *.conciletime.com
From an external request mapping standpoint, you need to make sure that requests are all mapped to the Cloud Foundry API endpoint for the landscape where your application is deployed. Again, I use GoDaddy for DNS management, so I’ll illustrate what creating a CNAME mapping looks like. You may have to enlist your IT department to make CNAME additions to domains your company owns.
Notice that this DNS entry is of type CNAME and the targeted host is the wildcard “*“. Most critically the target of this entry is to the api endpoint of YOUR deployment landscape. In this case it’s us10 with api.cf.us10.hana.ondemand.com but yours may be eu10(AWS Europe) instead of us10 or otherwise. This change may take a while to propagate throughout the Internet. After some time, verify that DNS resolution is correct. I use the Linux dig utility for this but you might use named or other tool.
dig www.conciletime.com
$ dig www.conciletime.com ; <<>> DiG 9.10.6 <<>> www.conciletime.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14920 ;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 4, ADDITIONAL: 9 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;www.conciletime.com. IN A ;; ANSWER SECTION: www.conciletime.com. 3600 IN CNAME api.cf.us10.hana.ondemand.com. api.cf.us10.hana.ondemand.com. 9915 IN CNAME cf-proxy-hcp-live-us10-2030508056.us-east-1.elb.amazonaws.com. cf-proxy-hcp-live-us10-2030508056.us-east-1.elb.amazonaws.com. 6 IN A 54.209.228.56 cf-proxy-hcp-live-us10-2030508056.us-east-1.elb.amazonaws.com. 6 IN A 52.7.38.158 cf-proxy-hcp-live-us10-2030508056.us-east-1.elb.amazonaws.com. 6 IN A 52.4.61.78
Be sure to try a few other hostnames. You need to be sure ANY domain name maps to the api endpoint.
dig abc.conciletime.com
$ dig abc.conciletime.com ; <<>> DiG 9.10.6 <<>> abc.conciletime.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5664 ;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 4, ADDITIONAL: 9 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;abc.conciletime.com. IN A ;; ANSWER SECTION: abc.conciletime.com. 3600 IN CNAME api.cf.us10.hana.ondemand.com.
dig whatever-hostname.conciletime.com
…
;; ANSWER SECTION:
whatever-hostname.conciletime.com. 3600 IN CNAME api.cf.us10.hana.ondemand.com.
Once you’re confident external mapping is set and that you’re not looking at any cached DNS results and the target is always the api endpoint, you can continue with configuring the internal route mapping.
Map domain to application
Once you have your application deployed into your space, the urls will all point to the hana.ondemand.com domain. This is the default behavior when deploying. What you want to do now is to create a new http route to your application so that requests coming in on your custom domain will be forwarded to your application.
Here’s what it looks like before adding a route.
cf a
Getting apps in org ConcileTime / space dev as andrew.lunde@sap.com... OK name requested state instances memory disk urls concile_app_v0 started 1/1 512M 256M conciletime-app.cfapps.us10.hana.ondemand.com concile_web_v0 started 1/1 512M 256M conciletime.cfapps.us10.hana.ondemand.com concile_utl_v0 started 1/1 512M 256M conciletime-utl.cfapps.us10.hana.ondemand.com concile_srv_v0 started 1/1 512M 256M conciletime-srv.cfapps.us10.hana.ondemand.com concile_db_v0 stopped 0/1 256M 256M
Now let’s map a route that will cover all possible hostnames for your domain. We’ll use the asterix here as well as the hostname. Since the * has meaning on our command line shell we need to put it in single quotes so that the shell doesn’t try to expand it.
cf map-route concile_web_v0 conciletime.com --hostname '*'
Creating route *.conciletime.com for org ConcileTime / space dev as andrew.lunde@sap.com... OK Route *.conciletime.com is created Adding route *.conciletime.com to app concile_web_v0 in org ConcileTime / space dev as andrew.lunde@sap.com... OK
Now we can check again, this time I’ll just check the app-router component of my application.
cf app concile_web_v0
Showing health and status for app concile_web_v0 in org ConcileTime / space dev as andrew.lunde@sap.com... name: concile_web_v0 requested state: started instances: 1/1 usage: 512M x 1 instances routes: conciletime.cfapps.us10.hana.ondemand.com, *.conciletime.com last uploaded: Thu 01 Aug 17:23:06 EDT 2019 stack: cflinuxfs3 buildpack: nodejs state since cpu memory disk details #0 running 2019-08-01T21:23:57Z 0.0% 47.1M of 512M 104.7M of 256M
Notice that in routes: *.conciletime.com also appears.
We can now browse to www.conciletime.com, or abc.conciletime.com, or xyz.conciletime.com and get the same application. Again, this is important in order to support multitenant application subscriptions.
You browser should also be completely happy and not show any certificate errors whatsoever.
!Critical configuration!
When your MTA application is deployed, the uaa service instance needs to be passed additional information so that the authentication mechanism can validate the relay destination upon the user’s authentication. This is a kind of whitelist and while you don’t have to do this if you’re not using custom domains, if you do (like we are here) then it’s mandatory.
Maker sure your project’s mta.yaml file is configured to pick up an xs-security.json file in your project.
resources: ... - name: conciletime-uaa type: org.cloudfoundry.managed-service parameters: path: xs-security.json service-name: CONCILETIME_UAA service-plan: default service: xsuaa config: xsappname: conciletime-${space} tenant-mode: dedicated
Now in your xs-security.json file, make sure that you have an oauth2-configuration section.
"oauth2-configuration": { "redirect-uris": [ "http*://*.conciletime.com/**" ] }
Notice that the redirect-uris contains a uri with wildcard matching of the protocol type, the hostname, and any path that follows. This is pretty broad matching so you might want to restrict it to known login paths, but it’s vital that the hostname wildcard matching be in place in order to support subscriptions of clients as we’ve discussed above.
Full docs for Application Security Descriptor Configuration Syntax
Philip has put together an extensive collection of videos with all the particulars of building multitenant apps. https://www.youtube.com/playlist?list=PLkzo92owKnVx3Sh0nemX8GoSNzJGfsWJM
Wow, this post got quite long. If you’re still with me, congratulations! Let me know how your efforts at using custom domains in your mutitenant apps go or leave me a question below.
If I’ve missed any detail, take a look at this project where I’ve implemented everything I mention here.
https://github.com/alundesap/conciletime/tree/post01
-Andrew
Partners: If you have a question, click here to ask it in the SAP Community . Be sure to tag it with Partnership and leave your company name in the question so that we can better assist you. |
Great stuff Andrew ... didn't even realize that custom domains where possible on CF .. sounds logical if you think about it ... thanks for laying out all the details 🙂
Excellent post Andrew, wanted to do something similar for our internal wiki. One question: do you know maybe if it is possible to export custom domain certificate and private key from SAP Cloud platform to place it on some other host (I understand possible security issues)?
Thanks
For CP-Neo it was not possible to export the key since it's internal to the platform. For CP-CF you create your own key and from it generate the Certificate Request which you send to the certificate issuer and then install the received certificate into the system. So for CF you already have the key and cert and can install it wherever you additionally need it. (ex: Apache).
-Andrew
Hi,
for CF, the same is valid. Our plugin custom-domains requires to create all your private keys inside the landscape. It is not possible to import or export it.
Best, Stephan
Thanks Andrew Lunde,for sharing the detail blog about custom domain can be used in cloud foundry applications.
I have marked steps to be performed for achieving the following task.
1.Buy a custom domain quota
2.Choose and buy the domain names to be used by your applications
3.Create the custom domain
4.Choose an SSL certificate;
Standard certificate
Wildcard certificate
Subject Alternative Name (SAN) certificate"
5.Generate a CSR
6.Issue the SSL certificate
7.Upload and Activate the Signed Certificate
8.Configure the DNS for a Custom Domain
9.Map Your Own Application to a Custom Domain
10.Test the Custom Domain
As per the above steps i have few questions.
Already customer is having domain name example www.mycomapny.come for on-promise applications.
Can we use the same for building the cloud related applications by including sub-domains by Wildcard certificate/SAN certificate.
or Should i need to buy new domain?
is Custom domain quota is mandatory step?Where will buy this?is it internal to SAP Cloud platform to host SSL Host for binding the application?
Is it already available to customer when the customer creates global accounts/create any sub accounts?How much it is costs?
Please answer my query, hope soon my successful custom domain blog will be available for others 🙂
Can we use the same for building the cloud related applications by including sub-domains by Wildcard certificate/SAN certificate.
or Should i need to buy new domain?
You can use the existing customer domain name. All that is required is to purchase a valid certificate and to be sure not to use the same host names that might be hosted by other systems. If you're using wildcard matching then you would have to direct wildcard hostname matches to SAP CloudPlatform and use explicit hostname matching for any other hostname that you might have hosted on another provider(www, blog, ftp, etc.)
is Custom domain quota is mandatory step?Where will buy this?is it internal to SAP Cloud platform to host SSL Host for binding the application?
Yes, your subaccount needs to have a custom domain quota, but I'm not sure how you purchase it. This depends on your account type and you may have to engage your SAP sales person to verify that you have it available in the global account first. Once you've assigned the quota to the subaccount in which the space you want to deploy your app resides, you can check that it is available with this command.
Is it already available to customer when the customer creates global accounts/create any sub accounts?How much it is costs?
It is not automatically available when you create a new SAP CloudPlatform account, you'll need to purchase it or if you have a Cloud Platform Enterprise Agreement you should be able to self-provision it. Again, costs vary depending.
Hope this helps.
-Andrew
Thanks Andrew.
I will check the command and let you know the results.
Regarding below answer from you,i have few doubts.
You can use the existing customer domain name.
All that is required is to purchase a valid certificate.
(Valid Certificate means,is it related to Standard,Wilcard and SAN certificate)?
and to be sure not to use the same host names that might be hosted by other systems.(got it)
If you’re using wildcard matching then you would have to direct wildcard hostname matches to SAP CloudPlatform and use explicit hostname matching for any other hostname that you might have hosted on another provider(www, blog, ftp, etc.)
Can you explain this in more detail, I am getting confused here.
Thanks in advance for your help.
Valid certificate is intended to mean one that is signed by a public certificate authority(GoDaddy, Kommondo, etc, not self-signed or private certificate authority).
In DNS you can specify a wildcard match with '*' and also explicit hostnames. The trick is to make sure that any explicit hostnames get matched before the wildcard match(which I think is the default behavior). Also the wildcard entry need to point to the SAP CloudFoundry gateway since that is where your application will be running. Otherwise you'll have to manually set the internal routes for your application as needed. (See the "cf create-route, cf map-route" commands).
-Andrew
Hi Andrew,
in fact, if it is a valid use case, you can also use your own CA, e.g. an Enterprise PKI, or some private CA, which is not publicly known and implicitly trusted by the standard browsers and clients.
Our CF plugin custom-domains does not limit this.
It depends on your clients and users.
Best, Stephan
Perfect tutorial. Saved me a lot of time!
Hi Andrew,
Thanks for the very detailed blog.
I am receiving error “The redirect_uri has an invalid domain” just right after authentication.
I have added the oauth2-configuration section in xs-security.json as below,
“oauth2-configuration”: {
“redirect-uris”: [
“http*://*.mycust-domain.com/**”
]
}
Please help.
Thanks,
Ravindra
Ravindra,
2 things I can think to check.
From above…
First, double check that Cloud Foundry understands when to use your custom domain certificate. Run this cf command and verify that the Domain name with wildcard is listed and activated(in green).
Second, make sure your TENANT_HOST_PATTERN has a regular expression such that the first matching group matches the subdomain of your subscribers properly. From my example above this was in the section in my mta.yaml file under my app-router module definition.
Thanks a lot Andrew. This solves my issue.
Hi Andrew
I got a question, this can be done with SAP Cloud Portal Service ? i mean, in this tutorial you are doing it for apps in Cloud Foundry, but can i use my subscription to portal services, with a custom domain so i can log in with my preferred domain to the portal sites that i create?
I did a cf a in command line but couldn't find portal as an application there to follow your blog's steps. Its possible to do it?
Hi Andrew,
Is it possible for SAP Customers to have a custom domain URL for the multi tenant applications (or SaaS applications) that SAP has developed and released in SAP CP CF.
Regards,
indeevar
I'd say no for two reasons.
1. You can't create a route to the app even if you've registered a custom domain name in your space because the map-route command requires that the app you're targeting be in the space and this isn't the case for SAP provided applications.
Hi Andrew,
Thank you for the answer.
Regards,
indeevar
Hello,
Thank you very much for this very detailed article.
I am configuring the "Custom Domains" but I get an error in the next step:
cf custom-domain-create-key gmao "CN=xxx.com, O=xx, C=FR, ST=xxx, L=xxx, OU=DSI, " "*.xxx.com" -verbose
Command: custom-domain-create-key
Organisation: xxxx (xxxxx-fc009e08f04e)
API Endpoint: https://api.cf.eu10.hana.ondemand.com
Default API Server: https://custom-domain-certificates-api.cf.eu10.hana.ondemand.com
Key: xxx
Subject: CN=xxx.com, O=xx, C=FR, ST=xxx, L=xx, OU=DSI,
Domain Names: *.xxx.com
Are you sure to generate this key in the system? (y/N)
y
DEBUG:2020/11/12 16:13:51 client.go:105: POST to https://custom-domain-certificates-api.cf.eu10.hana.ondemand.com/api/v1/organizations/xxxxx-fc009e08f04e/identities
DEBUG:2020/11/12 16:13:51 client.go:110: Request: [{ gmao CN=xxxz.com, O=xxx, C=FR, ST=xxx, L=xx, OU=DSI, [*.xxx.com] }]
DEBUG:2020/11/12 16:13:52 client.go:131: HTTP Status: 409
DEBUG:2020/11/12 16:13:52 client.go:132: Response: {"code":16,"message":"Domain is not registered for this ORG"}
Domain is not registered for this ORG
FYI, I deleted my client's information.
Can you help me to solve this problem?
Best regards,
Hello Andrew,
Thank you for the detailed blog, very informative.
Reaching out for quick help on the below
We have created a launchpad site for which we would like leverage the custom domain functionality.
the following steps are complete
however, when I launch the URL with custom domain name, I get the below error
404 Not Found: Requested route (xxx.xxx.xxxx.com') does not exist.
I am following this blog and the SAP Help site where it says the below needs to be done
cf custom-domain-map-route <default-saas-url> <my-saas-app.subdomain.mydomain.com>
The default SaaS URL consists of the following elements:< subaccount-name>.<saas-app-name>.cfapps.<region>.hana.ondemand.com, for example https://subaccount.enterprise-messaging.cfapps.eu10.hana.ondemand.com
However, following these instructions got little confused on the <default-saas-url> value.
Could you share an example of how this can achieved?