Contents

自建 Nginx 部署

Warning
本文最后更新于 April 18, 2020,文中内容可能已过时,请谨慎使用。

这段时间搭建自己的 Nginx 服务器,顺便总结回顾了一些运维相关的知识点,详细介绍了如何搭建一个企业级的高性能 Nginx 服务器。

部署

部署环境

  • 本机:Ubuntu 19.10
  • Nginx:tengine-2.3.2
  • 部署脚本:https://github.com/weijiaxiang007/nginx_install
1
$ git clone https://github.com/weijiaxiang007/nginx_install.git

Nginx 部署

Nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器,也是今天的主角。

各组件版本

  • luajit="LuaJIT-2.0.5”
  • openssl="openssl-1.1.1f”
  • pcre="pcre-8.44”
  • tengine="tengine-2.3.2”
  • jemalloc="jemalloc-5.2.1”

nginx 部署脚本 install.sh

1
2
3
$ sudo su
$ cd nginx_install
$ bash install.sh

默认安装用户为 work ,安装目录在 /home/work/nginx, 自定义虚拟主机可以放在 /home/work/nginx/conf.d。默认配置文件在/home/work/nginx/conf/nginx.conf。之后Nginx将作为一个出色的反向代理服务器,成为后面工作的主角。

1
2
3
4
5
6
7
8
# nginx -V
JXWServer version: JXWServer/2.3.2
nginx version: nginx/1.17.3
built by gcc 9.2.1 20191008 (Ubuntu 9.2.1-9ubuntu2) 
built with OpenSSL 1.1.1f  31 Mar 2020
TLS SNI support enabled
configure arguments: --prefix=/home/work/nginx --error-log-path=/home/work/log/nginx/nginx.log --add-module=./modules/ngx_http_upstream_dyups_module --add-module=./modules/ngx_http_concat_module --add-module=./modules/ngx_http_upstream_session_sticky_module --add-module=./modules/ngx_http_upstream_check_module --add-module=./modules/ngx_http_upstream_dynamic_module --add-module=./modules/ngx_http_lua_module --add-module=./modules/ngx_backtrace_module --add-module=./modules/ngx_http_reqstat_module --add-module=./modules/ngx_http_user_agent_module --add-module=./modules/ngx_multi_upstream_module --add-module=./modules/headers-more-nginx-module --add-module=./modules/ngx_cache_purge --add-module=./modules/ngx_devel_kit --add-module=./modules/echo-nginx-module --add-module=./modules/ngx_slab_stat --add-module=./modules/ngx_http_sysguard_module --add-module=./modules/ngx_http_upstream_consistent_hash_module --add-module=./modules/ngx_http_proxy_connect_module --add-module=./modules/ngx_http_upstream_keepalive_module --without-http_upstream_keepalive_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --without-mail_pop3_module --without-mail_imap_module --without-mail_smtp_module --with-http_sub_module --with-pcre=/tmp/nginx_install/pcre-8.44 --with-pcre-jit --with-openssl=/tmp/nginx_install/openssl-1.1.1f --with-openssl-opt=enable-shared --with-jemalloc=/tmp/nginx_install/jemalloc-5.2.1 --with-luajit-inc=/usr/local/include/luajit-2.0 --with-luajit-lib=/usr/local/lib --with-http_auth_request_module --with-http_stub_status_module --with-google_perftools_module --with-ld-opt=-ltcmalloc --with-http_secure_link_module

Bind 部署

BIND是一种开源的DNS(Domain Name System)协议的实现,包含对域名的查询和响应所需的所有软件。它是互联网上最广泛使用的一种DNS服务器,对于类UNIX系统来说,已经成为事实上的标准。

我们搭建内网的dns服务器,不仅可以解析我们自定义的域名,也可以作为转发型\缓存型的DNS服务器解析其他公网域名。

部署版本

  • bind9

安装bind9

1
2
$ sudo apt-get update
$ sudo apt-get install bind9 bind9utils bind9-doc

bind9的配置文件默认在 /etc/bind。主配置文件被保存在下列文件中。

1
2
3
/etc/bind/named.conf
/etc/bind/named.conf.options
/etc/bind/named.conf.local

我的部署脚本中有最简版的bind9配置文件模板,可以将 ./bind9/named.conf.options 拷贝到 /etc/bind/named.conf.options。然后照着例子生成一个 db.demo.com的待解析的文件即可 对配置文件做下简单的介绍

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# cat named.conf.options
...
		forwarders {
                127.0.0.53;  // 对于公网域名,直接转发。并缓存记录
        };

        //========================================================================
        // If BIND logs error messages about the root key being expired,
        // you will need to update your keys.  See https://www.isc.org/bind-keys
        //========================================================================
        dnssec-validation auto;  // 递归查询服务器上开启DNSSEC验证

        max-cache-ttl 120;  
        max-ncache-ttl 120;
        version "[no version.]";
        minimal-responses yes;
        recursion yes;    // 允许递归查询,直接返回结果
        allow-query {any;};  // 允许所有主机查询
...

上述的例子是最简单的例子,没有引入复杂的视图,logging等概念,详情可以参考如下链接。如果这是本机访问的话,大可不必搭建bind服务器,可以直接写 /etc/hosts

More Info:

自签证书

为了能实现https访问当前的自定义的域名。需要自签证书,然后将自签证书导入到系统证书中。

自签证书工具

  • openssl1.1.1k

自签证书已经写好了脚本只需要简单的运行即可生成自签证书

1
2
3
$ cd ssl
$ bash create_ca.sh
$ bash create_client_cert.sh --ou SRE --cn kiosk.io --email admin@kiosk.io

ca的证书在 ./demoCA/cacert.pem kiosk.io 的证书在 ./kiosk.io/kiosk.io.crt , key在 ./kiosk.io/kiosk.io.key 的。拷贝至Nginx所在目录即可。 将ca证书加入系统证书中。

$ sudo cp ./demoCA/cacert.pem /usr/local/share/ca-certificates/cacert.crt
$ sudo update-ca-certificates

其实update-ca-certificates是一个shell脚本,命令的本质其实是将PEM格式的根证书内容附加到**/etc/ssl/certs/ca-certificates.crt**, 而**/etc/ssl/certs/ca-certificates.crt** 中本身就包含了系统自带的各种可信根证书。

当然在企业内网还可以签署 客户端证书,没有装客户端证书的用户将不能访问该站点。在Nginx的配置文件中加上如下配置。

1
2
3
  ssl_client_certificate ssl_certs/demoCA/cacert.pem;
  ssl_crl ssl_certs/demoCA/private/ca.crl;
  ssl_verify_client on;

ssl_client_certificate就是客户端证书的CA证书了,代表此CA签发的证书都是可信的。 ssl_verify_client on;代表强制启用客户端认证,非法客户端(无证书,证书不可信)都会返回400错。

特别注意ssl_crl这个配置,代表Nginx会读取一个CRL(Certificate Revoke List)文件,之前说过,可能会有收回用户权限的需求,因此我们必须有吊销证书的功能,产生一个CRL文件让Nginx知道哪些证书被吊销了即可。

需要收回签发证书时,只需要

1
$ bash ./revoke_cert.sh aaa.demo.com

这个脚本会自动吊销他的签证文件crt,并且自动更新CRL文件。特别注意需要reload或restart nginx才能让nginx重新加载CRL。这样被吊销的证书将无法访问网站了。

可能出现的问题

  • NET::ERR_CERT_COMMON_NAME_INVALID

自建CA和自签证书文档,但是发现自己生成之后,将ca证书导入客户端之后,Chrome访问网站总是会出现该错误。

如果要通过 修改 openssl.cnf 来签发证书,除将上述配置直接改到 openssl.cnf 相应位置外,必须将配置中的 basicConstraints = CA:FLASE 改为 basicConstraints = CA:TRUE,否则修改不生效,这是其他教程没有提到的。

如果是域名证书,也可以在此可以添加多域名,如:

1
2
3
4
5
6
7
8
# vim http.ext
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName=@SubjectAlternativeName

[ SubjectAlternativeName ]
DNS.1=test.com
DNS.2=www.test.com

extendedKeyUsage 可以指定证书目的,即用途,一般有:

  • serverAuth:保证远程计算机的身份
  • clientAuth:向远程计算机证明你的身份
  • codeSigning:确保软件来自软件发布者,保护软件在发行后不被更改
  • emailProtection:保护电子邮件消息
  • timeStamping:允许用当前时间签名数据

如果不指定,则默认为 所有应用程序策略

More Info:

Nginx 配置文件

/home/work/nginx/conf.d/ 下新建kiosk.io.conf 配置文件内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
upstream  hexo_backend {
        server 127.0.0.1:4000  weight=1 max_fails=2 fail_timeout=30s;      
}


server {
	listen 80;
	listen 443 ssl;
        server_name kiosk.io http2;

	if ($scheme != "https") {
    		return 301 https://$http_host$request_uri;
  	}
	
        access_log  /home/work/log/nginx/https_kiosk.io.log jxjson;
        error_log   /home/work/log/nginx/ssl-error.log notice;

        ssl_certificate         ssl/kiosk.io/kiosk.io.crt;
        ssl_certificate_key     ssl/kiosk.io/kiosk.io.key;
        ssl_prefer_server_ciphers   on;

        ssl_session_cache  shared:SSL:80m;
        ssl_session_timeout  5m;
        ssl_protocols   TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4;
        ssl_stapling on; 
        ssl_stapling_verify on; 
        ssl_trusted_certificate ssl/kiosk.io/kiosk.io.crt;
        ssl_dhparam ssl/dhparam.pem;
                
        ssl_early_data     on;
        add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
        add_header X-Content-Type-Options nosniff;

        proxy_set_header   Early-Data $ssl_early_data;
        proxy_set_header   Host  $host;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-Proto $scheme;
	
	location / {
            proxy_pass http://hexo_backend/;
        }
	
	error_page   500 502 503 504  /50x.html;
        proxy_intercept_errors on;
        location = /50x.html {
               root   html;
        }

}

日志格式参考: https://adamtheautomator.com/nginix-logs/