Nginx

  • Nginx变量概述
  • 内置变量
  • 内置变量是否可以重新赋值
  • 使用变量
  • Set设置自定义变量
  • set自定义变量作用域
  • set变量与子请求
  • 变量mapping
  • Map中让结果变量取值本身具备动态性
  • map缓存
  • map缓存-强制刷新参数
  • 其他重要的变量举例
  • NGINX API(NGINX Plus)
  • API配置持久化
  • TLS/SSL在Nginx中的应用
  • NGINX SSL基本配置介绍
  • SSL 会话卸载
  • SNI支持
  • 基于SNI的证书的lazy loading
  • SSL Session Caching
  • SSL Session Ticket
  • 如何升级nginx的openssl版本
  • SSL module其他功能
  • 快速上线HTTPS站点工作


Nginx变量概述

NGINX中以$开头的字符串为变量对象
变量形态

  • 内置变量
  • Set自定义变量
  • Keyval变量
    常见用途
  • 条件代理
  • 条件日志
  • HTTP报文头映射
  • 采集和收集统计信息
  • 选择性认证
  • 动态黑名单
  • A/B测试
  • Request ID追踪

内置变量

  • 内置变量一般用于获取请求或响应的各种信息,有Nginx各模块自动产生
  • 常见的如nginx的server_name有多个 nginx server_name详解_SSLurl,nginx的server_name有多个 nginx server_name详解_赋值_02geoip_latitude等
  • 单个变量,例如$args
  • 变量群,例如nginx的server_name有多个 nginx server_name详解_SSL_03arg_parameter2…
    类似的变量群:nginx的server_name有多个 nginx server_name详解_SSL_04http_,nginx的server_name有多个 nginx server_name详解_nginx_05upstream_cookie_,upstream_tralier_,nginx的server_name有多个 nginx server_name详解_赋值_06sent_trailer_

内置变量是否可以重新赋值

  • 已索引类的变量,诸如$uri等此类变量一般不能被重新赋值
  • nginx: [emerg] the duplicate “uri” variable in /etc/nginx/./conf.d/var.train.conf:83
  • (已索引的变量,在NGINX内部有存放该变量的容器)

未索引类变量,诸如nginx的server_name有多个 nginx server_name详解_nginx_07arg_*,nginx的server_name有多个 nginx server_name详解_linux_08http_*等可被重新赋值
未索引值的是当用户调用时候去启动一个内置程序去获取当前的值,并不是提前预先索引好

location /internal {
    set $orig_args $args;
    set $args "changed=1&result+1";
    set $orig_testheader $http_testheader;
    set $http_testheader "had-chaged-header-value";
    return 200 "Original arg:$orig_args , But last nginx internal args is:$args ||| Original testheader:  $orig_testheader,But last nginx internal header: $http_testheader";
}

nginx的server_name有多个 nginx server_name详解_SSL_09

使用变量

  • 强制80端口跳转到443端口
server {
    listen 80;
    return https://$host$request_uri 
}
  • 在代理http请求中保留uri
server {
    listen 443 ssl;
    proxy_pass http://example.com:8080$request_uri;
}

NGINX所有内置变量

Set设置自定义变量

SET指令用来自定义变量

  • set有两个含义:创建变量,给变量赋值
  • 创建变量是在加载配置的时候
  • 赋值变量是当请求处理上下文需要时触发
  • 直接调用一个未被创建的变量,会导致配置无法启动
  • 变量名是整个配置文件可见,但变量的值是基于每个独立请求的上下文
#example
set $variable_name
value;

set $foo "hello world";
set $bar 101;
set $combo $foo;

set自定义变量作用域

  • 变量名是整个配置文件可见,但变量的之是基于每个独立请求的上下文
  • Rewrite导致NGINX内部location跳转情形下,此时请求还属于同一个请求,因此变量值是不变的
  • 同一个上下文,多次set同一个变量,使用配置中最后一个set值(rewrite模块return有额外影响)
  • set命令可以在server,location,if配置上下文
##example
location /noset {
    return 200 "F5: $f5";
}
location /set {
    set $f5 "nginx is part of F5";
    return 200 "F5: $f5";
}

curl http://172.16.100.252:8080/noset
F5:
C:\Users\jlin

curl http://172.16.100.252:8080/set
F5: nginx is part of F5

  • 直接调用一个未被创建的变量,会导致配置无法启动
  • 变量名是整个配置文件可见,但变量的值是基于每个独立请求的上下文
  • 赋值变量是当请求处理上下文需要时触发

set变量与子请求

  • 正常情况下,主,子请求都应该维护其自己的独立变量值,就像两个完全不同的请求一样
  • 但是有可能存在父请求使用子请求的变量值情况,比如auth_request指令
#example
localtion /main_auth_request {
    set $var "/";
    auth_request /sub_auth;
    proxy_pass http://192.168.2.71:80$var;
}
location /sub_auth {
    set $var "/get404fromcnadn";
    return 200 "sub request";
}

最后服务器收到的请求uri是http://192.168.2.71:80/get404fromcnadn,如果将/sub_auth下的$var设置为/则不会返回404

内置变量与子请求

  • 一般来说可以将子请求理解为一个独立的请求,因此其内置变量的取值就是该子请求相关的值,比如nginx的server_name有多个 nginx server_name详解_SSL_10args等等
  • 但有些变量在子请求中可能不是期望的,例如nginx的server_name有多个 nginx server_name详解_https_11request_uri,在子请求上下文中其值依然来自主请求

变量mapping

基于源变量值的匹配来赋值结果变量($result-variable)

#example
map $source-variable $result-variable {
    default          foo
    f5               ltm
    f4               shancai

源变量的值匹配:

  • 字符串匹配,无大小写之分
  • 正则表达式匹配,正则表达式可以采取“命名/位置”捕捉方式产生一个新的变量以便其他地方使用,正则表达式应以~ 或~*开头来表达是否大小写敏感匹配
  • 如果多个源变量可匹配,顺序为:
  • 精确最长匹配
  • 最长的前缀匹配
  • 最长的后缀匹配
  • 配置中的第一个正则被匹配
  • 缺省default(如果没有default匹配,则返回空字符串)

结果变量赋值

  • 可以是字符串赋值,也可以用其他变量为其赋值,或者混合(带插值)
  • 设置map映射,并不代表立即执行,只有当访问触发“结果变量”取值时,才会实际到map中去匹配查找

Map中让结果变量取值本身具备动态性

问题:如果一个请求的session cookie不存在,则取样1%的这样请求记录到access.log里

#example
map $cookie_SESSION $logme {
    ""              $perhaps;
    default         0;
}
split_clients $request_id $perhaps {
              1%           1;   # $perhaps is true 1% of the time
              *            0;
}
server {
    listen 80;
    access_log /var/log/nginx/secure.log notes if=$logme;
    ...

map缓存

map与geo指令类似,在一次上请求上下文中会被缓存

#example
map $args $test {
    default 0;
    debug   1;
}
localtion /maptest {
    set $orig_test $test;
    set $args debug;
    return 200 "orginal test mapping value: $orig_test ||| After changing args, the test mapping alue is: $test";
}

curl http://192.168.2.73:8080/maptest
orginal test mapping value: 0 ||| After changing args, the test mapping alue is: 0

map缓存-强制刷新参数

1.11.7版本后增加了缓存控制参数 volatile

#example
map $args $test {
    volatile;
    default 0;
    debug   1;
}
localtion /maptest {
    set $orig_test $test;
    set $args debug;
    return 200 "orginal test mapping value: $orig_test ||| After changing args, the test mapping alue is: $test";
}

curl http://192.168.2.73:8080/maptest
orginal test mapping value: 0 ||| After changing args, the test mapping alue is: 1

其他重要的变量举例

Variable

Definition

$upstream_connect_time

Connecion to upstream/backend

$upstream_header_time

First byte response header

$upstream_response_time

Last byte response body

$request_time

Total time of request

以上变量没有默认的加入到日志中,需要在log_format指令中手工加入

NGINX API(NGINX Plus)

前提:在配置中启用api location
API作用:

  • On the fly改变配置,免reload配置
  • Devops融合
  • 状态检控、统计信息、配置管理

demo体验api地址

#example
server {
    listen 8080;
    set $apimgmt_entry_point -;
    location /api {
      api write=on;
    }
    location = /dashboard.html {
      root /usr/share/nginx/html;
    }
    location /swagger-ui {
      root /usr/share/nginx/html;
    }
}

nginx的server_name有多个 nginx server_name详解_linux_12

API配置持久化

#example
upstream {
    upstream dns {
        zone dns_zone 64k;
        state /var/lib/nginx/state/stream_upstream_dns.conf;
    }
}
cat /var/lib/nginx/state/stream_upstream_dns.conf
server 172.16.10.203:53 max_conns=100 fail timeout=15s slow start=5s;

nginx的server_name有多个 nginx server_name详解_linux_13

TLS/SSL在Nginx中的应用

NGINX SSL基本配置介绍

#HTTPS 典型配置实例
server {
    listen 80 default_server;
    server_name www.example.com;
    ##强制HTTP流量重定向到HTTPS,满足安全标准
    return 301 https://$server_name$request_uri;
}
server {
    listen 443 default_server;
    server_name www.example.com;
    ##配置证书和密钥完成最基础的SSL加解密过程
    ##使用openssl进行所有的SSL处理
    ssl_certificate cert.crt;
    ssl_certificate_key cert.key;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers aRSA:!ECDHE:!EDH:!KDHE;
    ssl_prefer_server_ceiphers on;

    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
    }
}

SSL 会话卸载

#HTTPS 典型配置实例
server {
    listen 80 default_server;
    server_name www.example.com;
    ##强制HTTP流量重定向到HTTPS,满足安全标准
    return 301 https://$server_name$request_uri;
}
server {
    listen 443 ssl;
    ##配置证书和密钥完成最基础的SSL加解密过程
    ##使用openssl进行所有的SSL处理
    ssl_certificate cert.crt;
    ssl_certificate_key cert.key;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers aRSA:!ECDHE:!EDH:!KDHE;
    ssl_prefer_server_ceiphers on;

    location / {
        ##在反向代理场景中,nginx与客户端直接采用加密HTTPS,服务器测采用HTTP传输
        proxy_pass http://backend;
    }
}

SNI支持

  • 基于SNI实现多个域名共享一个IP
  • 不同域名使用不同证书
  • 无需单独配置SNI
server {
    listen 443 default_server;
    server_name www.example1.com;
    ssl_certificate cert1.crt;
    ssl_certificate_key cert1.key;
}
server {
    listen 443 default_server;
    server_name www.example2.com;
    ssl_certificate cert2.crt;
    ssl_certificate_key cert2.key;
}

基于SNI的证书的lazy loading

  • 所有域名复用同一个server配置block,极大减少配置量
  • 可以动态更新证书,无需reload
  • 即使需要reload,因为配置量小,也会大大提高速度
  • 在使用动态证书读取可能会造成初次SSL handshake多用20%-30%左右的时间,但后续流量加密不受影响
server {
    listen 443 ssl;
    
    ssl_certificate /etc/ssl/$ssl_server_name.crt;
    ssl_certificate_key /etc/ssl/$ssl_server_name.key;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ceiphers on;

    location / {
        proxy_set_header Host $host;
        proxy_pass http://backend;
    }
}

SSL Session Caching

  • 避免每次请求都进行ssl handshake
  • 提升SSL/TLS performance
  • 1 MB session cache可以储存4000条session
  • 全部nginx worker共享
server {
    listen 443 ssl default_server;
    
    ssl_certificate cert.crt;
    ssl_certificate_key cert.key;
    
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
}

SSL Session Ticket

  • 提升SSL性能,并减小内存使用
  • 集群中多个nginx可共享session ticket
server {
    listen 443 ssl default_server;
    
    ssl_certificate cert.crt;
    ssl_certificate_key cert.key;
    
    ssl_session_tickets     on;
    ssl_session_ticket_key  ticket_file ;
}

如何升级nginx的openssl版本

  • Nginx使用Openssl进行SSL处理
  • SSL漏洞的快速修复
  • 编译OpenSSL 1.1.1及后续版本,可支持TLS1.3
  • 开源版本可通过重新编译进行对openssl版本的升级
  • 配置无需修改
$ nginx -s stop
$ ./config --with-openssl=/usr/local/src/openssl-1.1.1d
$ make && make install

SSL module其他功能

nginx的server_name有多个 nginx server_name详解_nginx_14

快速上线HTTPS站点工作

Let’s encrypt

#一、安装Let's Encrypt
add-apt-repository paa:certbot/certbot
apt-get update
apt-get install python-certbot-nginx
#二、NGINX准备
server {
    listen 80 default_server;
    listen [::] default_server;
    root /var/www/html;
    server_name example.com www.example.com;
}
#三、获取证书并自动更新NGINX配置
sudo cerbot --nginx -d example.com -d www.example.com
#四、通过屏幕交互内容进行HTTPS配置,完成自动化证书签发和NGINX配置上线