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各模块自动产生
- 常见的如url,geoip_latitude等
- 单个变量,例如$args
- 变量群,例如arg_parameter2…
类似的变量群:http_,upstream_cookie_,upstream_tralier_,sent_trailer_
内置变量是否可以重新赋值
- 已索引类的变量,诸如$uri等此类变量一般不能被重新赋值
- nginx: [emerg] the duplicate “uri” variable in /etc/nginx/./conf.d/var.train.conf:83
- (已索引的变量,在NGINX内部有存放该变量的容器)
未索引类变量,诸如arg_*,http_*等可被重新赋值
未索引值的是当用户调用时候去启动一个内置程序去获取当前的值,并不是提前预先索引好
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";
}
使用变量
- 强制80端口跳转到443端口
server {
listen 80;
return https://$host$request_uri
}
- 在代理http请求中保留uri
server {
listen 443 ssl;
proxy_pass http://example.com:8080$request_uri;
}
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\jlincurl 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
内置变量与子请求
- 一般来说可以将子请求理解为一个独立的请求,因此其内置变量的取值就是该子请求相关的值,比如args等等
- 但有些变量在子请求中可能不是期望的,例如request_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融合
- 状态检控、统计信息、配置管理
#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;
}
}
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;
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其他功能
快速上线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配置上线