多租户通配子域名开发环境搭建

摘要:如果希望开发支持多租户(Multiple Tenant)的通配子域名的软件,需要在本地搭建相应的环境,本文介绍如何搭建这样的环境。

alt

概述

通过本文,我们将搭建多租户通配子域名开发环境,该环境的功能如下:

  1. 可以正确的解析任意子域名访问
  2. 可以正确的解析子域名所对应的服务
  3. 可以正确的显示 SSL 证书

本文所用的技术

  1. dnsmasq 本地 DNS 解析服务
  2. nginx 反向代理
  3. ng serve 的选项

假设我们需要设定的域名为 mengxin.science, 其统配子域名为 *.mengxin.science

dnsmasq 配置 DNS

我们都知道,在本地调试的时候,如果希望指定域名,一般都会使用 /etc/hosts 来指定域名对应的解析 IP(一般为本地)。但是这个 hosts 文件并不支持子域名通配。也就是说我们无法使用该文件达到解析所有的 *.mengxin.scienc 到本地 IP。

这时候我们需要一个本地的 DNS 服务来达到这个目标,也就是 dnsmasq。作为一个 DNS server, dnsmasq 有很多功能,我们这里只介绍必要的配置。

安装

安装大家完全可以官方获得手册,本人使用 Fedora,所以直接用 dnf 安装,可以参考该文章

step 1

安装

sudo dnf install dnsmasq

step 2

配置用户和用户组来操作 dnsmasq

sudo groupadd -r dnsmasq
sudo useradd -r -g dnsmasq dnsmasq

dnsmasq 配置

我们需要的配置很简单,实际上只需要一条记录配置统配域名的 DNS A 记录(A record)就行了。

dnsmasq 的配置和 nginx 类似,有一个默认的配置文件 /etc/dnsmasq.conf,同时还可以创建自定义配置文件,因为在主配置文件中包含了相应文件夹下的所有配置:

1
2
# Include all files in /etc/dnsmasq.d except RPM backup files
conf-dir=/etc/dnsmasq.d,.rpmnew,.rpmsave,.rpmorig

所有我们只需要在 /etc/dnsmasq.d 目录下创建一个我们自己的配置文件即可,例如 dev.conf。该文件我们配置如下:

1
address=/mengxin.science/192.168.XXX.XXX

其中后面的 IP 地址为你需要指定的地址,也可以是本地地址。

配置结束后我们可以使用一下命令来测试配置文件:

1
sudo dnsmasq --test

让 dnsmasq 生效

在配置中发现总是不能生效,因为还没有告诉你的操作系统如何使用这个本地的 DNS,所以我们需要配置我们解析网址的 DNS 服务器,其配置文件为 resolve.conf。向该文件中增加条目:nameserver 127.0.0.1,结果如下:

1
2
3
4
5
# Generated by NetworkManager
search XXX.com
nameserver 127.0.0.1
nameserver 192.168.XXX.XXX
nameserver 8.8.8.8

该列表为所有可用的 DNS 解析服务器。

修改了这个文件可以需要重启一下 networkManager

sudo service network-manager restart

管理 dnsmasq 服务

我们可以使用 service 来管理,也可以用 systemctl

启动服务

sudo systemctl start dnsmasq

重启服务

sudo systemctl restart dnsmasq

停止服务

sudo systemctl stop dnsmasq

Nginx 配置反向代理

至此我们已经成功的解析的统配的 *.mengxin.science 到指定的 IP 地址。下面我们就要来将统配的子域名反向代理到争取的服务端口。

假设我们开发环境分为前端后后端:

  1. 前端端口为 3000, 访问路径为 localhost:3000
  2. 后端端口为 8081, 访问路径为 localhost:8081/rest

则我们需要将

  1. *.mengxin.science -> localhost:3000
  2. *.mengxin.science/rest -> localhost:8081/rest

SSL 证书

既然我们需要统配子域名,那么我们就需要一个统配的证书 (同时兼容统配和子域名为空的情形),这个证书的特点是其 issuer to 为一个统配的域名,比如 *.google.co.uk。

enter description here

nginx 配置

dnsmasq 一样,我们只需要配置增加一个配置文件在 /etc/nginx/conf.d 目录下即可,比如 dev.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server {
listen 443 ssl;
server_name .dualshield-cloud.deepnetsecurity.com;
ssl_certificate /etc/nginx/star.mengxin.science.crt;
ssl_certificate_key /etc/nginx/star.mengxin.science.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location ^~ /rest/ {
proxy_pass http://localhost:8081;
proxy_set_header Host $http_host;
#proxy_set_header Host mengxin.science;
}

location /{
proxy_pass https://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
#proxy_set_header Host mengxin.science;
}
}

其中有两点需要注意

  1. server_name 如果前面不加 * 表示统配两种情形:任意的子域名和子域名为空
  2. 如果你的开发程序不能接受在请求头部信息的任意的 Host,那么可以将头部设为可接受的内容,比如顶级域名。

前端 Angular 服务启动配置

前面我们说到关于头部的 Host 信息的问题,在前端开发 Angular (2+),我们使用 ng serve 命令的时候,是可以忽略 Host 检查的,其参数为--disable-host-check,所以在开发的时候加上这个选项,任意子域名的反向代理,即使不配置头部也可以正确的导向前端的服务。

总结

至此我们完成了目标:

  1. 可以正确的解析任意子域名访问
  2. 可以正确的解析子域名所对应的服务
  3. 可以正确的显示 SSL 证书

至于子域名到达服务后,那么就需要程序本身来进行处理,比如 servletfilter 中可以提取 子域名。js 也可以将路径中的子域名提取出来。