Khai báo SSL theo server name

Nhiều domain, subdomain có thể dùng cho một website, khi đó khai báo khối server sẽ lặp lại chỉ khác server_name và các khai báo SSL.

Có vài cách để tránh việc khai báo lại này:

server {
  listen                443 ssl http2;
  server_name           example.com example.org;
  root                  /path/to/www;
  
  ssl_certificate       /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key   /etc/letsencrypt/live/example.com/privkey.pem;
  include               php.conf;
  include               ssl.conf;
  location / {
    try_files $uri $uri/ /index.php?$args =404;
  }
}
  • Khai báo SSL phụ thuộc vào một biến khác của Nginx
map $host $piCert {
  example.com /etc/letsencrypt/live/example.com/fullchain.pem;
  example.org /etc/letsencrypt/live/example.org/fullchain.pem;
}

map $host $piKey {
  example.com /etc/letsencrypt/live/example.com/privkey.pem;
  example.org /etc/letsencrypt/live/example.org/privkey.pem;
}

server {
  listen                443 ssl http2;
  server_name           example.com example.org;
  root                  /path/to/www;
  
  ssl_certificate       $piCert;
  ssl_certificate_key   $piKey;
  include               php.conf;
  include               ssl.conf;
  location / {
    try_files $uri $uri/ /index.php?$args =404;
  }
}

Biến $host chứa đựng giá trị của server_name. Nhưng trong khai báo nhiều domain, $host chỉ lấy domain đầu tiên. Vì vậy cách trên thất bại, vẫn phải khai báo 2 khối server cho từng domain chỉ khác nhau ở server_name.

Thay vì dùng biến $host, có thể dùng biến $ssl_server_name phản ánh đúng domain đang dùng của server_name:

map $ssl_server_name $piCert {
  example.com /etc/letsencrypt/live/example.com/fullchain.pem;
  example.org /etc/letsencrypt/live/example.org/fullchain.pem;
}

map $ssl_server_name $piKey {
  example.com /etc/letsencrypt/live/example.com/privkey.pem;
  example.org /etc/letsencrypt/live/example.org/privkey.pem;
}

Một cách khác chính xác hơn là dùng biến $ssl_preread_server_name. Tuy nhiên cách này đòi hỏi Nginx được biên dịch có module ngx_stream_ssl_preread_module và khai báo cũng phức tạp hơn.

  • Trường hợp backend của proxy

Nhờ tùy chọn proxy_set_header Host $host; ở proxy server, biến $host ở backend chứa tên domain và chúng ta có thể ứng dụng như sau:

server {
  listen 443 ssl http2;
  listen [::]:443;
  include /etc/nginx/templates/ssl.tmpl;

  server_name adminer.example.com
              mailadmin.example.com
              webmail.example.com;
  return 301 https://mail.example.com/$sub_to_folder;
}
map $host $sub_to_folder {
  adminer.example.com adminer;
  mailadmin.example.com iredadmin;
  webmail.example.com mail;
}

Chú thích

Trường hợp có nhiều domain và subdomain, cách dùng $ssl_server_name như trên tương đối rườm rà. Vì domain.com và *.domain.com có SSL trong cùng file nên có thể tìm cách bỏ subdomain trong $ssl_server_name để dùng SSL của domain.com

  1. *.example.com -> example.com
  2. *.example.org -> example.org
map $ssl_server_name $ssl_domain_name {
  volatile;
  hostnames;
  default $ssl_server_name;
  ~^(\w+\.)?(?<basedomain>[^.]+\.[^.]+)$ $basedomain;
}
server {
  listen                443 ssl http2;
  server_name           example.com example.org
                        cloud.example.com mail.example.org;
  root                  /path/to/www;
  
  ssl_certificate       /etc/letsencrypt/live/$ssl_domain_name/fullchain.pem;
  ssl_certificate_key   /etc/letsencrypt/live/$ssl_domain_name/privkey.pem;
  include               php.conf;
  include               ssl.conf;
  location / {
    try_files $uri $uri/ /index.php?$args =404;
  }
}

Comments Off on Khai báo SSL theo server name

Filed under Software

Comments are closed.