• Nginx配置ssl加密实现https访问

    背景

    关于https和ssl等基本概念,可以参考之前的博文。 今天我们主要讲一下具体在Nginx里面如何配置使用。

    默认的Nginx安装是没有使用SSL模块的,我们可以在编译安装nginx时加入 –with-http_ssl_module 选项。如果我们是从一开始就直接安装编译很简单,但是如果是我们在最开始的时候没有编译这个模块的话,那么如何来将它加入呢 ?  我们来实践一下:

    在已经安装的nginx上,增加ssl模块

    1. 使用 Nginx -V 命令查看Nginx版本以及它已经安装的模块

    从上面的命令我们得到了Nginx的版本号,以及 prefix前缀安装路径,以及它已经安装的模块。

    2. 下载对应的Nginx源码包,重新编译,加入模块

    3. 执行make,但不要执行 make install (否则会形成覆盖)

    4. 执行完之后替换nginx可执行二进制文件

    5. 再次执行 nginx -V 验证Nginx是否已经加入了对应的模块 

    6. 启动Nginx,开始正常使用 

    注:以上过程均在测试环境中完成,生产环境请谨慎参考。

    Nginx改造为https提供服务

    比如我们的网站需要实现全站https,这时候就需要对Nginx进行重新配置:

    注:server.key 和 server.pem 需要放在此配置文件对应的上级目录中。

    SSL相关参数解读:

    • ssl_certificate :证书其实代表公钥,在与客户端的交互过程中它会被发送到客户端。
    • ssl_certificate_key :是公钥对应的私钥,用来解密的,所以它的权限要得到保护但nginx的主进程能够读取。
    • ssl_protocals :用来强制用户连接只能引入SSL/TLS那些强壮的协议版本和强大的加密算法。
    • ssl_ciphers:选择加密套件,不同的浏览器所支持的套件(和顺序)可能会不同。这里指定的是OpenSSL库能够识别的写法。
    • ssl_prefer_server_ciphers on :设置协商加密算法时,优先使用我们服务端的加密套件,而不是客户端浏览器的加密套件。
    • ssl_session_timeout:客户端可以重用会话缓存中ssl参数的过期时间。

    Nginx 配置http与https共存

    很多情况下,因为业务需求的不同,我们往往需要http与https共存,即部分域名走http,部分域名走https,我们来简单的研究下这种配置方式:

    其实挺简单的,没啥区别,把80端口的配置copy一份出来,略作修改,加上https的ssl相关参数即可。

    Nginx实现双向ssl认证

    我们上面的讲的ssl认证是单向的,都是客户端去认证被访问的站点域名是否真实可信,并对传输过程加密,但服务器端并没有认证客户端是否可信。 (实际上除非特别重要的场景,也没必要去认证访问者,除非像银行U盾这样的情况,因为这也是一种成本,通信的效率也会降低)

    要实现双向认证HTTPS,nginx服务器上必须导入CA证书(根证书/中间级证书),因为现在是由服务器端通过CA去验证客户端的信息。还有必须在申请服务器证书的同时,用同样的方法生成客户证书。取得客户证书后,还要将它转换成浏览器识别的格式(大部分浏览器都认识PKCS12格式):

    然后把这个 client.p12 发给你相信的人,让它导入到浏览器中,访问站点建立连接的时候nginx会要求客户端把这个证书发给自己验证,如果没有这个证书就拒绝访问。

    同时别忘了在 nginx.conf 里配置信任的CA:(如果是二级CA,请把根CA放在后面,形成CA证书链)

     

    参考

    http://seanlook.com/2015/05/28/nginx-ssl/

  • Nginx的Location以及Rewrite配置规则

    背景

    Location和Rewrite规则的写法在日常的Nginx配置中很常见,有必要对其有一个了解。在学习这些知识之前,我们首先需要对正则表达式有一个简单的了解,可以参考之前的博文,这里就不再详细赘述了。

    location

    location指令用于匹配client请求uri的path部分,然后对不同的请求提供不同的静态内容,或者通过反向代理重定向到内部的server。

    对于从client过来的request, nginx会进行预处理,nginx首先对采用 ’%XX’(uri采用%+十六进制格式用于在浏览器和插件中显示非标准的字母和字符) 格式文本编码的uri进行解码。然后处理path中的相对路径符号’.’和‘..’,然后对于含有相邻的两个及以上的’/’压缩成一个’/’。 这样处理完后会得到一个干净的path,然后会用这个path会在server的location指令的参数进行匹配。

    关于它的配置详情,我们可以参考对应的Nginx官方文档,这里简单叙述。

    location的正则匹配主要包括如下几种:

    Nginx是按照如下顺序进行规则匹配的:

    1. 精确匹配 = 对应的字符串,如果匹配到,则停止匹配。
    2. 匹配 ^~ 开头的字符串前缀,如果匹配到,则停止匹配。
    3. 按照最长原则匹配剩下所有的常规字符串,如果有完全匹配,则直接使用终止。否则,将匹配记下来,继续下一步。
    4. 匹配所有正则表达,按照它们在配置文件中定义的顺序。 如果匹配到了,那就使用它,否则就使用第三步记下的匹配规则。

    这个匹配过程翻译成优先级后则顺序如下(自上而下,优先级从高到低):

    我们举个实际的例子来验证研究一下:

    首先,我在我的MAC笔记本上安装Nginx,并配置如下配置文件:

    如上,然后我建立每个location配置对应的root目录结构,方便验证测试:

    每个index.html里面的内容,主要是输出标识他所属的目录,比如:

    接下来启动Nginx并且绑定host就可以验证了,比如:

    %e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2018-01-12-%e4%b8%8b%e5%8d%884-45-26

    这样就可以在浏览器输入网址来测试location匹配了。 经过测试,我们得出如下结论:

    最后,我们附上一个日常的基本Nginx的location写法:

    Rewrite

    Nginx的Rewrite规则主要实现url重写以及重定向,在这个过程中可以使用nginx提供的全局变量或自己设置的变量。Rewrite只能对域名后边的除去传递的参数外的字符串起作用

    比如下面这个URL:

    Rewrite只对 wp-admin/post.php 重写。 如果你想对域名或参数字符串起作用,可以使用全局变量匹配,也可以使用proxy_pass反向代理。当然如果URI中含有参数(post=1085&action=edit),那么默认情况下参数会被自动附加到替换厚的串上。如果你不想让那些参数跟在你改写后的uri后面,你可以通过在替换串的末尾加上?标记来解决这一问题。比如:

    rewrite ^/users/(.*)$   /show?user=$1?   last;

    这里我们要区分Location和Rewrite的区别,虽然他俩貌似都能实现跳转功能,但是本质上是不同的。 location是对路径做控制访问或反向代理,也可以proxy_pass到其他机器。rewrite是更改某一域名获取资源的路径。很多情况下rewrite也会写在location里,它们的执行顺序是(其实很简单,从外到里):

    1. 执行server块的rewrite指令
    2. 执行location匹配
    3. 执行选定的location中的rewrite指令

    Rewrite的基本语法我们可以从对应的官方网站上看到,这里简单介绍一下:

    Syntax: rewrite regex replacement [flag];
    Default:
    Context: server, location, if

    在 regex 中匹配小括号()之间匹配的内容,可以在后面的表达式中通过$1来引用,$2表示的是前面第二个()里的内容,依次类推。

    flag 标志位

    Rewrite指令的最后一项参数为flag标记,主要包括:

    • last :表示完成本次rewrite,用重写后的URI继续进行 Location 匹配。
    • break:表示完成本次rewrite,并且直接应用,不再继续进行 Location 匹配。
    • redirect:返回一个302的临时重定向,地址栏会显示跳转后的地址。
    • permanent:返回一个301的永久重定向,地址栏会显示跳转后的地址。

    这里我们举个例子来说明一下last和break的区别:

    如果我在Nginx的配置文件中这样写,那么最终会造成死循环匹配,最后超过10次后就返回500错误码了。 因为我们将URI改写后last指令并不能限制它继续进行Location匹配,它还是会匹配中这个规则。 如果我们把last换成break就不会存在这个问题了。

    if指令

    在使用Rewrite规则时,我们可以对给定的条件condition进行判断。如果为真,大括号内的rewrite指令将被执行。他的语法规则特别简单,就和C语言一样:if(condition){…}  ,if条件(conditon)可以是如下任何内容:

    • 当表达式只是一个变量时,如果值为空或任何以0开头的字符串都会当做false
    • 直接比较变量和内容时,使用=!=
    • ~正则表达式匹配,~*不区分大小写的匹配,!~区分大小写的不匹配

    -f!-f用来判断是否存在文件
    -d!-d用来判断是否存在目录
    -e!-e用来判断是否存在文件或目录
    -x!-x用来判断文件是否可执行

    我们来看一些常见的例子:

    下面是可以用作if判断的全局变量:

    比如,我们的这个网址:

    http://www.foreverlakers.com/wp-admin/post.php?post=1085&action=edit

    • $host:www.foreverlakers.com
    • $server_port:80
    • $request_uri:http://www.foreverlakers.com/wp-admin/post.php
    • $document_uri:wp-admin/post.php
    • $document_root:/var/www/html/wordpress
    • $request_filename:/var/www/html/wordpress/wp-admin/post.php

     

     

    参考

    http://nginx.org/en/docs/http/ngx_http_core_module.html#location

    http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite

    https://segmentfault.com/a/1190000002797606