Skip to content
BILLETKnowledge Base

Give tenants their own addresses

Let each tenant sign in at a name of its own, such as acme.desktops.example.com, and the DNS record, certificate and proxy settings that needs.

For
Administrators
Applies to
Billet 1.0
Updated

Each tenant can have an address one label under a name you choose, the base:

https://acme.desktops.example.com/portal
        ^^^^ ^^^^^^^^^^^^^^^^^^^
        the tenant's address, under the base

On its own address, the sign-in page names that organization and does not ask for it, and signs in only that tenant's people.

Four things have to agree on the base, and each is set in a different place:

Set in If it is missing
The portal's base The console, or the portal's service The console says the base is not set
One wildcard DNS record Your DNS provider The name does not resolve
One wildcard certificate Your reverse proxy's certificate The browser refuses the page
The proxy's server names Your reverse proxy Requests may reach the wrong site

1. Set the base in the portal

In the console, open Deployment, find Addresses, type the base (for example desktops.example.com) and press Save. Only system administrators can.

Or set it in the portal's service:

Environment=BILLET_TENANT_HOST_BASE=desktops.example.com

The console's value wins over the service's when both are set. Revert removes the console's value and hands the decision back to the service.

Give the base as a name only: no https://, no path and no port. A leading *. is accepted and dropped.

2. Add the DNS record

At your DNS provider:

*.desktops.example.com    A    <your portal's public address>

A name that already has a record of its own under the base keeps it, so an existing relay.desktops.example.com is left alone. Such a name can never be a tenant's address.

3. Add the wildcard to the certificate

The certificate must name both the portal and *.desktops.example.com. Use one wildcard, never a certificate per tenant: certificates are published in public logs, and one per tenant would publish your list of customers.

Let's Encrypt only issues a wildcard through a DNS challenge, so certbot needs a plugin for your DNS provider. For Cloudflare DNS:

sudo apt-get install -y python3-certbot-dns-cloudflare
sudo certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  --cert-name portal.example.com --expand \
  -d portal.example.com -d '*.desktops.example.com'

The Cloudflare API token in cloudflare.ini needs one permission, Zone DNS Edit, on that zone only. Keep the file mode 0600: renewals use it again.

4. Name the wildcard in the proxy

In the nginx site from Put the portal behind a reverse proxy, add the wildcard to server_name in both the 443 and the 80 server blocks:

server_name portal.example.com *.desktops.example.com;

Nothing else changes. The proxy must keep passing the original Host header, which is how the portal tells which tenant was asked for.

5. Give each tenant its address

Each tenant's address is shown and changed on Tenants. A tenant gets one made from its name when it is created. When a tenant's address changes, the old one sends people on to the new one for 90 days.

If tenants use browser sessions, add the wildcard to the relay's allowed origins, for example BILLET_RELAY_WS_ORIGINS=https://portal.example.com,https://*.desktops.example.com. See Install a relay.

Single sign-on on a tenant's address

A sign-on connection can live on the tenant's own address. On the connection's page, choose Move them to this organization's host. Give your identity provider the new addresses first (or as well, where it accepts more than one), then move them. Changing the tenant's address later moves its connections with it, and the provider has to be given them again.

Check it

P=check.desktops.example.com
dig +short $P
openssl s_client -connect $P:443 -servername $P </dev/null 2>/dev/null \
  | openssl x509 -noout -ext subjectAltName
curl -sS -o /dev/null -w '%{http_code}\n' https://$P/portal

The record resolves to the portal, the certificate names *.desktops.example.com, and the page answers 200.