Skip to Content
Technical Articles
Author's profile photo Andrew Lunde

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

  1. 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
Check that your domain got created properly.
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

https://comodosslstore.com

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

 

Your PositiveSSL Wildcard Certificate for *.conciletime.com is attached!

Dear andrew.lunde@sap.com,

Thank you for placing your order. We are pleased to announce that your PositiveSSL Wildcard Certificate for *.conciletime.com has been issued.

To help reduce domain name mismatch warnings, we have also included the domain name conciletime.com in your certificate.

We strongly recommend that you click here for instructions to ensure that your certificate is installed and your webserver is configured correctly.

Attached to this email you should find a .zip file containing:

  • Root CA Certificate – AddTrustExternalCARoot.crt
  • Intermediate CA Certificate – USERTrustRSAAddTrustCA.crt
  • Intermediate CA Certificate – SectigoRSADomainValidationSecureServerCA.crt
  • Your PositiveSSL Wildcard Certificate – STAR_conciletime_com.crt

You can also find your PositiveSSL Wildcard Certificate for *.conciletime.com in text format at the bottom of this email.

Should you have any questions or issues you would like to discuss, please do not hesitate to contact us.

Thank you for being a valued Sectigo customer.

 

visit our website

get support

Copyright Ⓒ Sectigo Limited, All rights reserved.

Your PositiveSSL Wildcard Certificate for *.conciletime.com in text format (if required):

—–BEGIN CERTIFICATE—–

MIIGBzCCBO+gAwIBAgIRAOu8RmSkyiweALyVCVENPugwDQYJKoZIhvcNAQELBQAw

gY8xCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO

 

EarDqD2umMD244ohsoWTgnJdj3F5nl03TzU/QOVXfB8ESQyDoNgvppOO052Nxbo4

viaxq3C4zkNZH5o=

—–END CERTIFICATE—–

 

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.

 

Assigned Tags

      19 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Steven De Saeger
      Steven De Saeger

      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 🙂

      Author's profile photo Marin Simunovic
      Marin Simunovic

      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

      Author's profile photo Andrew Lunde
      Andrew Lunde
      Blog Post Author

      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

      Author's profile photo Stephan Andre
      Stephan Andre

      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

      Author's profile photo Zameer Ahamad
      Zameer Ahamad

      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 🙂

      Author's profile photo Andrew Lunde
      Andrew Lunde
      Blog Post Author

      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.

      $ cf m | grep domain
      INFRA                     custom_domains*                         Shared service for Custom Domain Certificates
      

       

      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

       

      Author's profile photo Zameer Ahamad
      Zameer Ahamad

      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.

      Author's profile photo Andrew Lunde
      Andrew Lunde
      Blog Post Author

      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

      Author's profile photo Stephan Andre
      Stephan Andre

      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

      Author's profile photo Eric Solberg
      Eric Solberg

      Perfect tutorial. Saved me a lot of time!

      Author's profile photo Ravindra PAWAR
      Ravindra PAWAR

      Hi Andrew,

      Thanks for the very detailed blog.

      I am receiving error “The redirect_uri has an invalid domain” just right after authentication.

      Which is 400 Bad Request response for GET /oauth/authorize request.

      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

       

       

      Author's profile photo Andrew Lunde
      Andrew Lunde
      Blog Post Author

      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).

      cf custom-domain-list

       

      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.

      ...
      properties:
      TENANT_HOST_PATTERN: ‘^(.*).conciletime.com’
      ...
      This is what allows the redirect to get you back to the app after the user authenticates.
      Hope this helps, let me know if you continue to be stuck.
      -Andrew
      Author's profile photo Ravindra PAWAR
      Ravindra PAWAR

      Thanks a lot Andrew. This solves my issue.

      Author's profile photo Alejandro Colmenares
      Alejandro Colmenares

      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?

      Author's profile photo Indeevar Reddy Aduri
      Indeevar Reddy Aduri

      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

      Author's profile photo Andrew Lunde
      Andrew Lunde
      Blog Post Author

      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.

      EXAMPLES:
         cf map-route my-app example.com --hostname myhost            # myhost.example.com
      2. Even if you could, the login wouldn't work because you don't control the whiltelist for oauth handoff once a user has been authenticated.  This is configured in the xsuaa binding of the original app and wouldn't include your custom domain unless you somehow coordinated with the original publisher of the app(not likely).
      I actually tried this in my own account where I have a custom domain and couldn't get pas step #1.
      Sorry,
      -Andrew
      Author's profile photo Indeevar Reddy Aduri
      Indeevar Reddy Aduri

      Hi Andrew,

       

      Thank you for the answer.

       

      Regards,

      indeevar

      Author's profile photo Former Member
      Former Member

      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,

      Author's profile photo Chakravarthy Adidam
      Chakravarthy Adidam

      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

      • Perform custom domain setup using CLI
      • Register CNAME in the DNS with alias pointing to the eu10 cf name
      • certificates uploaded and custom domain has been activated
      • SAP Backend team has maintained the required mapping at the backend as well

      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.

      • Is it the URL for the launchpad site which was shared with SAP for the backend registration?
        • if yes, the command is not taking special characters in the URL like /, # and ?
        • if no, what would be the default SaaS URL
      • Is the below default SaaS URL without "https://"
        • subaccount.enterprise-messaging.cfapps.eu10.hana.ondemand.com
        • If yes, how would the routing work as this is only a part of the SaaS URL which is the launchpad site SAP has mapped in the backend

      Could you share an example of how this can achieved?