动态域名技术允许根据用户请求的不同子域名动态地将流量路由到不同的后端服务器或服务端点。这种方法在多租户系统、多用户系统或需要动态创建子域名的应用程序中尤其有用。本文档将详细介绍如何利用 OpenResty 和 Lua 脚本实现动态域名的配置和路由管理,并强调如何保护用户隐私,隐藏真实的后端服务地址。

示例

假设有两个动态子域名 xeszlizycoacybfur9bm.example.comagfuyxcm2tv6115vx84a.example.com,它们分别被解析到不同的后端服务器:

  • 访问 xeszlizycoacybfur9bm.example.com 将路由到特定的后端服务器。

  • 访问 agfuyxcm2tv6115vx84a.example.com 将路由到另一个后端服务器。

安装OpenResty

# 添加 OpenResty 官方仓库
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
​
# 安装 OpenResty
sudo yum install -y openresty
​
# 验证安装
openresty -v
​

启动OpenResty

# 启动 openresty
sudo systemctl start openresty
​
# 设置开机自启 
sudo systemctl enable openresty
​

简单配置

编辑OpenResty配置文件

vim /usr/local/openresty/nginx/conf/nginx.conf
worker_processes 1;
​
events {
    worker_connections 1024;
}
​
http {
    include       mime.types;
    default_type  application/octet-stream;
​
    lua_package_path "/usr/local/openresty/lualib/?.lua;;";
​
    # 定义DNS解析器
    resolver 8.8.8.8;
​
    server {
        listen 80;
        # 这里修改成自己的域名
        server_name ~^(?<subdomain>.+)\.example\.com$;
​
        # 定义target_url变量
        set $target_url "";
​
        location / {
            access_by_lua_file /usr/local/openresty/lualib/dynamic_subdomains.lua;
            proxy_pass $target_url;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}
​

编写Lua脚本

vim /usr/local/openresty/lualib/dynamic_subdomains.lua
-- Lua脚本示例(例如放在/usr/local/openresty/lualib/dynamic_subdomains.lua)
local domain_map = {
    ["sub1"] = "http://backend1.yourdomain.com",
    ["sub2"] = "http://backend2.yourdomain.com",
    -- 添加更多子域名与后端服务的映射
}
​
local subdomain = ngx.var.subdomain
​
if domain_map[subdomain] then
    ngx.var.target_url = domain_map[subdomain]
else
    ngx.exit(ngx.HTTP_NOT_FOUND)
end
​
-- 设置proxy_pass
ngx.req.set_uri(ngx.var.target_url)

重启openresty

sudo systemctl restart openresty

动态配置

在本文示例中,我使用了一个后端接口来进行动态配置,但你也可以使用数据库查询等方式进行配置。

我的后端接口根据不同的输入返回对应的目标 URL:

  • 输入 louw3earytvfo7vvhovb 返回 https://abc.com

  • 输入 kmhvgh53c5adlmnjjalk 返回 https://qwe.com

因此,当我访问 louw3earytvfo7vvhovb.example.com 时,请求将被转发到 https://abc.com

同样地,当我访问 kmhvgh53c5adlmnjjalk.example.com 时,请求将被转发到 https://qwe.com

编辑OpenResty配置文件

vim /usr/local/openresty/nginx/conf/nginx.conf
worker_processes 1;
​
events {
    worker_connections 1024;
}
​
http {
    include       mime.types;
    default_type  application/octet-stream;
    
    lua_package_path "/usr/local/openresty/lualib/?.lua;;";
    lua_package_cpath "/usr/local/openresty/lualib/?.so;;";
​
​
    # 定义DNS解析器
    resolver 8.8.8.8;
​
    server {
        listen 83;
        server_name  ~^(?<subdomain>.+)\.example\.com$;
​
        # 定义target_url变量
        set $target_url "";
​
        location / {
            access_by_lua_file /usr/local/openresty/lualib/dynamic_subdomains.lua;
            proxy_pass $target_url;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}
​

编写Lua脚本

vim /usr/local/openresty/lualib/dynamic_subdomains.lua
local http = require ("resty.http")
local cjson = require("cjson.safe")
​
-- 模拟的动态子域名
local subdomain = ngx.var.subdomain
​
-- 后台接口的 URL,根据实际情况替换
local backend_api_url = "https://yourdomain.com/api/" .. subdomain
​
-- 发起 HTTP 请求到后台接口
local httpc = http.new()
local res, err = httpc:request_uri(backend_api_url, {
    method = "GET",
    headers = {
        ["Content-Type"] = "application/json",
    }
})
​
if not res then
    ngx.log(ngx.ERR, "failed to request backend API: ", err)
    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
    return
end
​
-- 解析 JSON 响应
local decoded, decode_err = cjson.decode(res.body)
if not decoded then
    ngx.log(ngx.ERR, "failed to decode JSON response: ", decode_err)
    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
    return
end
​
-- 提取出 msg 字段作为 target_url
local target_url = decoded.msg
​
-- 设置 proxy_pass
ngx.var.target_url = target_url
​

安装 lua-resty-http 模块

# 克隆lua-resty-http
git clone https://github.com/ledgetech/lua-resty-http.git
​
# 安装lua-resty-http
cd lua-resty-http/lib/resty
sudo cp http.lua /usr/local/openresty/lualib/resty/
sudo cp http_headers.lua /usr/local/openresty/lualib/resty/
sudo cp http_connect.lua /usr/local/openresty/lualib/resty/

安装 lua-resty-json 模块

# 安装gcc
sudo yum install gcc
​
# 下载模块源码
git clone https://github.com/bungle/lua-resty-json.git
​
# 进入源码目录
cd lua-resty-json
​
# 编译和安装
make
sudo make install
​
# 复制文件到 OpenResty 的 Lua 模块路径
sudo cp *.lua /usr/local/openresty/lualib/resty/

安装 lua-resty-openssl 模块

# 克隆lua-resty-openssl
git clone https://github.com/fffonion/lua-resty-openssl.git

# 安装lua-resty-openssl
cd lua-resty-openssl/lib/resty
sudo cp openssl.lua /usr/local/openresty/lualib/resty/
sudo mkdir -p /usr/local/openresty/lualib/resty/openssl/
sudo cp -r openssl/* /usr/local/openresty/lualib/resty/openssl/

重启openresty

sudo systemctl restart openresty

其他

按照以上配置来,一般就能够实现功能,若需排查问题,可去查看日志

tail -f /usr/local/openresty/nginx/logs/error.log