Centos7学习笔记(二十三)- nginx的keepalived高可用

一、什么是keepalived

keepalived最初是为LVS设计的,用于管理并监控LVS集群中各个服务节点的状态,后来又加入了可用实现高可用的VRRP协议支持。VRRP是Virtual Router RedundancyProtocol(虚拟路由器冗余协议)的缩写,VRRP出现的目的就是为了解决静态路由单点故障问题的,它能够保证当个别节点宕机时,整个网络可以不间断地运行。这里的VRRP协议,就是纯粹的网络中使用的VRRP协议。

二、keepalived工作原理

这里说“keepalived工作原理”,其实是在说VRRP的工作原理。后者的工作原理,语言描述上大致是这样的:

  1. 虚拟路由器中的路由器根据优先级选举出Master,Master路由器通过发送免费ARP报文,将自己的虚拟MAC地址通告给与它连接的设备。

  2. Master路由器周期性发送VRRP报文,以公布自己的配置信息(优先级等)和工作状态

  3. 如果Master故障,虚拟路由器中的Backup路由器将根据优先级重新选举新的Master

  4. 虚拟路由器状态切换时,Master路由器由一台设备切换会另外一台设备,新的Master路由器只是简单的发送一个携带虚拟MAC地址和虚拟IP的免费ARP报文,这样就可以更新其他设备中缓存的ARP信息

  5. Backup路由器的优先级高于Master时,由Backup的工作方式(抢占式或者非抢占式)决定是否重新选举Master。

在 Keepalived服务对之间,只有作为master的服务器会一直发送 VRRP广播包,告诉backup它还活着,此时backup不会枪占主,当master不可用时,即backup监听不到主发送的广播包时,就会启动相关服务接管资源,保证业务的连续性.接管速度最快可以小于1秒。


三、keepalvied配置文件详解

keepalived配置文件可按照三个大块区分。

全局配置(Global Configuration)
VRRPD配置
LVS配置

其中VRRPD配置,又可以分为如下几个子块

vvrp_script

vrrp_sync_group

garp_group

vrrp_instance

全局配置相关:

! Configuration File for keepalived

                

                global_defs {

                   notification_email {

                     acassen@firewall.loc

                     failover@firewall.loc

                     sysadmin@firewall.loc #邮件报警

                   }

                   notification_email_from Alexandre.Cassen@firewall.loc 指定发件人

                   smtp_server 192.168.200.1  #指定smtp服务器地址

                   smtp_connect_timeout 30    #指定smtp连接超时时间

                   router_id LVS_DEVEL  #负载均衡标识,在局域网内应该是唯一的。

                   vrrp_skip_check_adv_addr

                   vrrp_strict

                   vrrp_garp_interval 0

                   vrrp_gna_interval 0

                }


    说明:

    notification_email  :指定当keepalived出现问题时,发送邮件给哪些用户。

    notification_emai_from :发送邮件时,邮件的来源地址。

    smtp_server <DOMAIN|IP> [<PORT>]  :smtp服务器的地址或域名。默认端口为25.如:smtp_server smtp.felix.com 25

    smtp_helo_name  <HOST_NAME>  :指定在HELO消息中所使用的名称。默认为本地主机名。

    smtp_connect_timeout :指定smtp服务器连接的超时时间。单位是秒。

    

    router_id:指定标识该机器的route_id. 如:route_id LVS_01

    vrrp_mcast_group4 224.0.0.18:指定发送VRRP组播消息使用的IPV4组播地址。默认是224.0.0.18

    vrrp_mcast_group6 ff02::12 指定发送VRRP组播消息所使用的IPV6组播地址。默认是ff02::12

    default_interface eth0:设置静态地址默认绑定的端口。默认是eth0。

    lvs_sync_daemon <INTERFACE> <VRRP_INSTANCE> [id <SYNC_ID>] [maxlen <LEN>] [port <PORT>] [ttl <TTL>] [group <IP ADDR>]

        设置LVS同步服务的相关内容。可以同步LVS的状态信息。

        INTERFACE:指定同步服务绑定的接口。

        VRRP_INSTANCE:指定同步服务绑定的VRRP实例。

        id <SYNC_ID>:指定同步服务所使用的SYNCID,只有相同的SYNCID才会同步。范围是0-255.

        maxlen:指定数据包的最大长度。范围是1-65507

        port:指定同步所使用的UDP端口。

        group:指定组播IP地址。


    lvs_flush:在keepalived启动时,刷新所有已经存在的LVS配置。

    vrrp_garp_master_delay 10:当转换为MASTER状态时,延迟多少秒发送第二组的免费ARP。默认为5s,0表示不发送第二组免的免费ARP。

    vrrp_garp_master_repeat 1:当转换为MASTER状态时,在一组中一次发送的免费ARP数量。默认是5.

    vrrp_garp_lower_prio_delay 10:当MASTER收到更低优先级的通告时,延迟多少秒发送第二组的免费ARP。

    vrrp_garp_lower_prio_repeat 1:当MASTER收到更低优先级的通告时,在一组中一次发送的免费ARP数量。

    vrrp_garp_master_refresh 60:当keepalived成为MASTER以后,刷新免费ARP的最小时间间隔(会再次发送免费ARP)。默认是0,表示不会刷新。

    vrrp_garp_master_refresh_repeat 2: 当keepalived成为MASTER以后,每次刷新会发送多少个免费ARP。默认是1.

    vrrp_garp_interval 0.001:在一个接口发送的两个免费ARP之间的延迟。可以精确到毫秒级。默认是0.

    vrrp_lower_prio_no_advert true|false:默认是false。如果收到低优先级的通告,不发送任何通告。

    vrrp_version 2|3:设置默认的VRRP版本。默认是2.

    vrrp_check_unicast_src:在单播模式中,开启对VRRP数据包的源地址做检查,源地址必须是单播邻居之一。

    vrrp_skip_check_adv_addr:默认是不跳过检查。检查收到的VRRP通告中的所有地址可能会比较耗时,设置此命令的意思是,如果通告与接收的上一个通告来自相同的master路由器,则不执行检查(跳过检查)。

    vrrp_strict:严格遵守VRRP协议。下列情况将会阻止启动Keepalived:1. 没有VIP地址。2. 单播邻居。3. 在VRRP版本2中有IPv6地址。

    

    vrrp_iptables:不添加任何iptables规则。默认是添加iptables规则的。


    如果vrrp进程或check进程超时,可以用下面的4个选项。可以使处于BACKUP状态的VRRP实例变成MASTER状态,即使MASTER实例依然在运行。因为MASTER或BACKUP系统比较慢,不能及时处理VRRP数据包。

    vrrp_priority <-20 -- 19>:设置VRRP进程的优先级。

    checker_priority <-20 -- 19>:设置checker进程的优先级。

    vrrp_no_swap:vrrp进程不能够被交换。

    checker_no_swap:checker进程不能够被交换。

        

    script_user <username> [groupname]:设置运行脚本默认用户和组。如果没有指定,则默认用户为keepalived_script(需要该用户存在),否则为root用户。默认groupname同username。

    enable_script_security:如果脚本路径的任一部分对于非root用户来说,都具有可写权限,则不会以root身份运行脚本。

    nopreempt 默认是抢占模式 要是用非抢占式的就加上nopreempt

注意:上述为global_defs中的指令

VRRPD配置之vrrp_script配置:

vrrp_script配置

    作用:添加一个周期性执行的脚本。脚本的退出状态码会被调用它的所有的VRRP Instance记录。

    注意:至少有一个VRRP实例调用它并且优先级不能为0.优先级范围是1-254.

            vrrp_script <SCRIPT_NAME> {

                      ...

                }

            

    选项说明:

            scrip “/path/to/somewhere“:指定要执行的脚本的路径。

            interval <INTEGER>:指定脚本执行的间隔。单位是秒。默认为1s。

            timeout <INTEGER>:指定在多少秒后,脚本被认为执行失败。

            weight <-254 --- 254>:调整优先级。默认为2.

            rise <INTEGER>:执行成功多少次才认为是成功。

            fall <INTEGER>:执行失败多少次才认为失败。

            user <USERNAME> [GROUPNAME]:运行脚本的用户和组。

            init_fail:假设脚本初始状态是失败状态。


    解释:

    weight: 

    1. 如果脚本执行成功(退出状态码为0),weight大于0,则priority增加。

    2. 如果脚本执行失败(退出状态码为非0),weight小于0,则priority减少。

    3. 其他情况下,priority不变。

VRRPD配置之vrrp_sync_group配置:

vrrp_sync_group配置:

作用:将所有相关的VRRP实例定义在一起,作为一个VRRP Group,如果组内的任意一个实例出现问题,都可以实现Failover。

             vrrp_sync_group VG_1 {

                group {

                 inside_network     # vrrp instance name

                 outside_network    # vrrp instance name

                 ...

                }

                ...

            }

            

        说明:

        如果username和groupname没有指定,则以默认的script_user所指定的用户和组。

        1. notify_master /path/to_master.sh [username [groupname]]

            作用:当成为MASTER时,以指定的用户和组执行脚本。

        2. notify_backup /path/to_backup.sh [username [groupname]]

            作用:当成为BACKUP时,以指定的用户和组执行脚本。

        3. notify_fault “/path/fault.sh VG_1” [username [groupname]]

            作用:当该同步组Fault时,以指定的用户和组执行脚本。

        4. notify /path/notify.sh [username [groupname]]

            作用:在任何状态都会以指定的用户和组执行脚本。

            说明:该脚本会在notify_*脚本后执行。

            notify可以使用3个参数,如下:

            $1:可以是GROUP或INTANCE,表明后面是组还是实例。

            $2:组名或实例名。

            $3:转换后的目标状态。有:MASTER、BACKUP、FAULT。

        

        5. smtp_alert:当状态发生改变时,发送邮件。

        6. global_tracking:所有的VRRP实例共享相同的tracking配置。


注意:脚本文件要加上x权限,同时指令最好写绝对路径。

VRRPD配置之vrrp_instance配置:

vrrp_instance配置

命令说明:

                    state MASTER|BACKUP                             :指定该keepalived节点的初始状态。

                    interface eth0                                            :vrrp实例绑定的接口,用于发送VRRP包。

                    use_vmac [<VMAC_INTERFACE>]             :在指定的接口产生一个子接口,如vrrp.51,该接口的MAC地址为组播地址,通过该接口向外发送和接收VRRP包。

                    vmac_xmit_base                                        :通过基本接口向外发送和接收VRRP数据包,而不是通过VMAC接口。

                    native_ipv6                                                :强制VRRP实例使用IPV6.(当同时配置了IPV4和IPV6的时候)

                    dont_track_primary                                   :忽略VRRP接口的错误,默认是没有配置的。

                    

                    track_interface {

                      eth0

                      eth1 weight     <-254----254>

                      ...

                    }                                                                    :如果track的接口有任何一个出现故障,都会进入FAULT状态。


                    track_script {

                      <SCRIPT_NAME>

                      <SCRIPT_NAME> weight <-254----254>

                    }                                                                    :添加一个track脚本(vrrp_script配置的脚本。)

                    

                    mcast_src_ip <IPADDR>                                :指定发送组播数据包的源IP地址。默认是绑定VRRP实例的接口的主IP地址。

                    unicast_src_ip <IPADDR>                                :指定发送单薄数据包的源IP地址。默认是绑定VRRP实例的接口的主IP地址。

                    version 2|3                                                        :指定该实例所使用的VRRP版本。

                    

                    unicast_peer {

                       <IPADDR>

                       ...

                    }                                                                                :采用单播的方式发送VRRP通告,指定单播邻居的IP地址。

                    

                    virtual_router_id 51                                                    :指定VRRP实例ID,范围是0-255.

                    priority 100                                                                :指定优先级,优先级高的将成为MASTER。

                    advert_int  1                                                                :指定发送VRRP通告的间隔。单位是秒。

                    authentication {

                      auth_type PASS|AH                                                    :指定认证方式。PASS简单密码认证(推荐),AH:IPSEC认证(不推荐)。

                      auth_pass 1234                                                            :指定认证所使用的密码。最多8位。

                    }

                    

                    virtual_ipaddress {

                      :完整格式:<IPADDR>/<MASK> brd <IPADDR> dev <STRING> scope <SCOPE> label <LABEL>

                       192.168.200.17/24 dev eth1

                       192.168.200.18/24 dev eth2 label eth2:1

                    }                                                                                    :指定VIP地址。

                    

                    nopreempt                                                                    :设置为不抢占。默认是抢占的,当高优先级的机器恢复后,会抢占低优先级的机器成为MASTER,而不抢占,则允许低优先级的机器继续成为MASTER,即使高优先级的机器已经上线。如果要使用这个功能,则初始化状态必须为BACKUP。

                    preempt_delay   600                                                    :设置抢占延迟。单位是秒,范围是0---1000,默认是0.发现低优先级的MASTER后多少秒开始抢占。

                    


通知脚本:

                    notify_master <STRING>|<QUOTED-STRING> [username [groupname]]

                    notify_backup <STRING>|<QUOTED-STRING> [username [groupname]]

                    notify_fault <STRING>|<QUOTED-STRING> [username [groupname]]

                    notify <STRING>|<QUOTED-STRING> [username [groupname]]

                    

# 当停止VRRP时执行的脚本。

                    notify_stop <STRING>|<QUOTED-STRING> [username [groupname]]

                    smtp_alert

garp_group配置忽略

LVS配置

LVS配置之virtual_server配置:

                virtual_server IP Port | virtual_server fwmark int | virtual_server group string {

                  delay_loop <INT>                         :健康检查的时间间隔。

                  lb_argo rr|wrr|lc|wlc|lblc|sh|dh:LVS调度算法。

                  lb_kind NAT|DR|TUN                 :LVS模式。

                  persistence_timeout 360         :持久化超时时间,单位是秒。默认是6分钟。

                  persistence_granularity                 :持久化连接的颗粒度。

                  protocol TCP|UDP|SCTP         :4层协议。

                  ha_suspend                         :如果virtual server的IP地址没有设置,则不进行后端服务器的健康检查。

                  virtualhost <STRING>                 :为HTTP_GET和SSL_GET执行要检查的虚拟主机。如virtualhost www.felix.com

                  sorry_server <IPADDR> <PORT>         :添加一个备用服务器。当所有的RS都故障时。

                  sorry_server_inhibit         :将inhibit_on_failure指令应用于sorry_server指令。

                

                  alpha                         :在keepalived启动时,假设所有的RS都是down,以及健康检查是失败的。有助于防止启动时的误报。默认是禁用的。

                  omega                         :在keepalived终止时,会执行quorum_down指令所定义的脚本。

                

                  quorum <INT>                 :默认值1. 所有的存活的服务器的总的最小权重。

                  quorum_up <STRING>                 :当quorum增长到满足quorum所定义的值时,执行该脚本。

                  quorum_down <STRING>         :当quorum减少到不满足quorum所定义的值时,执行该脚本。

                }

LVS配置之real_server配置:

            real_server IP Port {

              weight <INT>         :给服务器指定权重。默认是1.

              inhibit_on_failure:当服务器健康检查失败时,将其weight设置为0,而不是从Virtual Server中移除。

              notify_up <STRING>:当服务器健康检查成功时,执行的脚本。

              notify_down <STRING>:当服务器健康检查失败时,执行的脚本。

              uthreshold <INT>:到这台服务器的最大连接数。

              lthreshold <INT>:到这台服务器的最小连接数。

            }

LVS配置之real_server健康检查:

        HTTP_GET | SSL_GET {

                url {

                          path <STRING>                        :指定要检查的URL的路径。如“path /” 或者 “path /mrtg2”

                          digest <STRING>                        :摘要。计算方式:genhash -s 172.17.100.1 -p 80 -u /index.html

                          status_code <INT>                    :状态码。

                }

                nb_get_retry <INT>                            :get尝试次数。

                delay_before_retry <INT>                    :在尝试之前延迟多长时间。

                

                connect_ip <IP ADDRESS>                    :连接的IP地址。默认是real server的ip地址。

                connect_port <PORT>                            :连接的端口。默认是real server的端口。

                bindto <IP ADDRESS>                            :发起连接的接口的地址。

                bind_port <PORT>                                :发起连接的源端口。

                connect_timeout <INT>                        :连接超时时间。默认是5s。

                fwmark <INTEGER>                                :使用fwmark对所有出去的检查数据包进行标记。

                warmup <INT>                                        :指定一个随机延迟,最大为N秒。可防止网络阻塞。如果为0,则关闭该功能。

        } 


        TCP_CHECK {

                connect_ip <IP ADDRESS>                    :连接的IP地址。默认是real server的ip地址。

                connect_port <PORT>                            :连接的端口。默认是real server的端口。

                bindto <IP ADDRESS>                            :发起连接的接口的地址。

                bind_port <PORT>                                :发起连接的源端口。

                connect_timeout <INT>                        :连接超时时间。默认是5s。

                fwmark <INTEGER>                                :使用fwmark对所有出去的检查数据包进行标记。

                warmup <INT>                                        :指定一个随机延迟,最大为N秒。可防止网络阻塞。如果为0,则关闭该功能。

                retry <INIT>                                            :重试次数。默认是1次。

                delay_before_retry <INT>                        :默认是1秒。在重试之前延迟多少秒。

        }



        SMTP_CHECK {

                connect_ip <IP ADDRESS>                        :连接的IP地址。默认是real server的ip地址。

                connect_port <PORT>                                :连接的端口。默认是real server的端口。 默认是25端口

                bindto <IP ADDRESS>                                :发起连接的接口的地址。

                bind_port <PORT>                                    :发起连接的源端口。

                connect_timeout <INT>                                :连接超时时间。默认是5s。

                fwmark <INTEGER>                                    :使用fwmark对所有出去的检查数据包进行标记。

                warmup <INT>                                            :指定一个随机延迟,最大为N秒。可防止网络阻塞。如果为0,则关闭该功能。

                

                retry <INT>                                                :重试次数。

                delay_before_retry <INT>                            :在重试之前延迟多少秒。

                helo_name <STRING>                                    :用于SMTP HELO请求的字符串。

        }



        DNS_CHECK {

                connect_ip <IP ADDRESS>                            :连接的IP地址。默认是real server的ip地址。

                connect_port <PORT>                                    :连接的端口。默认是real server的端口。 默认是25端口

                bindto <IP ADDRESS>                                    :发起连接的接口的地址。

                bind_port <PORT>                                        :发起连接的源端口。

                connect_timeout <INT>                                :连接超时时间。默认是5s。

                fwmark <INTEGER>                                        :使用fwmark对所有出去的检查数据包进行标记。

                warmup <INT>                                            :指定一个随机延迟,最大为N秒。可防止网络阻塞。如果为0,则关闭该功能。

                

                retry <INT>                                                    :重试次数。默认是3次。

                type <STRING>                                            :DNS查询类型。A/NS/CNAME/SOA/MX/TXT/AAAA

                name <STRING>                                            :DNS查询的域名。默认是(.)

        }




        MISC_CHECK {

                 misc_path <STRING>                                    :外部的脚本或程序路径。

                 misc_timeout <INT>                                        :脚本执行超时时间。

                 user USERNAME [GROUPNAME]                        :指定运行该脚本的用户和组。如果没有指定GROUPNAME,则GROUPNAME同USERNAME。

                 misc_dynamic                                                :根据退出状态码动态调整权重。

                                                                                              0,健康检查成功,权重不变。

                                                                                              1,健康检查失败。

                                                                                              2-255,健康检查成功。权重设置为退出状态码减去2.如退出状态码是250,则权重调整为248

                   warmup <INT>                                            :指定一个随机延迟,最大为N秒。可防止网络阻塞。如果为0,则关闭该功能。

        }

实例:

             global_defs {
                router_id LVS_Server                             《=======指定标识该机器的route_id
                 }
            vrrp_instance VI_1 {
                state MASTER                                         《========指定该keepalived节点的初始状态
                interface ens8                                         《=========vrrp实例绑定的接口,用于发送VRRP包
                virtual_router_id 51                                 《=========指定VRRP实例ID
                priority 150                                                《=========指定优先级,优先级高的将成为MASTER
                nopreempt                                               《==========设置为不抢占。默认是抢占的
                advert_int 1                                              《=========发出vrrp通告的时间间隔,此处设置为1秒
                authentication {
                    auth_type PASS                                      《==========指定认证为“简单密码认证”
                    auth_pass password                                 《==========指定认证所使用的密码为“password”。
                }
                virtual_ipaddress {  
                    192.168.1.217 dev ens8                              《==========指定VIP地址
                }
            }
            virtual_server 192.168.1.217 443 {
                delay_loop 3                                                       《========监控检查的时间间隔为3秒          
                lvs_sched rr                                                          《==========LVS的调度算法                    
                lvs_method DR                                                      《=========LVS 模式
                protocol TCP                                                         《=======4层协议
                real_server 192.168.1.211 443 {                             《========这里对应192.168.1.211的443端口
                    weight 1
                    TCP_CHECK {
                        connect_port 443
                        connect_timeout 3
                        nb_get_retry 3                                              《======get尝试次数
                        delay_before_retry 10                                          《=======在尝试之前延迟多长时间
                    }
                }
                real_server 192.168.1.212 443 {                             《========这里对应192.168.1.212的443端口
                    weight 1
                    TCP_CHECK {
                        connect_port 443
                        connect_timeout 3
                        nb_get_retry 3
                        delay_before_retry 10
                    }
                }
            }
            virtual_server 192.168.1.217 80 {
                delay_loop 3
                lvs_sched rr
                lvs_method DR
                protocol TCP
                real_server 192.168.1.211 80 {                             《========这里对应192.168.1.211的80端口
                    weight 1
                    TCP_CHECK {
                        connect_port 80
                        connect_timeout 3
                        nb_get_retry 3
                        delay_before_retry 10
                    }
                    }
                }
                real_server 192.168.1.212 80 {                             《========这里对应192.168.1.212的80端口
                    weight 1                                                  《==========调整优先级。默认为2
                    TCP_CHECK {
                        connect_port 80                                       《=========连接的端口
                        connect_timeout 3                                  《=========连接超时时间。默认是5s。
                        nb_get_retry 3                                          《==========get尝试次数。
                        delay_before_retry 10
                    }
                }
            }

四、nginx的keepalived高可用

需准备两台做负载均衡(一般是针对七层负载均衡)的机器,10.0.0.5和10.0.0.6,对应的内网IP地址172.16.1.5和172.16.1.6

1、安装keepalived

yum -y install keepalived

2、针对各机器更改keepalived配置文件

vim /etc/keepalived/keepalived.conf 

以下是10.0.0.5的配置:(做MASTER)

global_defs {

        router_id lb01
}                             
vrrp_instance VI_1 {
        state MASTER                                         
        interface eth0                                        
        virtual_router_id 51                                 
        priority 150                                                                                        
        advert_int 1                                              
        authentication {
        auth_type PASS                                      
                auth_pass 12345678                                 
         }
         virtual_ipaddress {  
                10.0.0.3            
         }
}

以下是10.0.0.6的配置:(做BACKUP)

global_defs {
        router_id lb02
}                             
vrrp_instance VI_1 {
        state BACKUP                                         
        interface eth0                                        
        virtual_router_id 51                                 
        priority 100                                                                                        
        advert_int 1                                              
        authentication {
        auth_type PASS                                      
                auth_pass 12345678                               
         }
         virtual_ipaddress {  
                10.0.0.3            
         }
}

两个配置文件的差异:

差异点MASTERBACKUP
router_id(唯一标识)<br/>lb01lb02
state(状态标识)<br/>MASTERBACKUP
priority(优先级)150100

两个配置文件的相同点:

相同MASTERBACKUP
vitual_router_id(虚拟路由区域id)<br/>5151
auth_type和auth_pass(认证方式和密码)<br/>PASS                 12345678PASS                12345678
virtual_ipaddress10.0.0.310.0.0.3

3、启动keepalived

systemctl start keepalived

systemctl enable keepalived

4、检查keepalived

ps aux |grep keepalived

ip add |grep 10.0.0.3

5、nopreempt的配置

当启用nopreempt时,必须将state状态标识,统一改为BACKUP。

vrrp_instance VI_1 {
        state BACKUP                                         
        interface eth0                                        
        virtual_router_id 51                                 
        priority 150
        nopreempt
}
vrrp_instance VI_1 {
        state BACKUP                                         
        interface eth0                                        
        virtual_router_id 51                                 
        priority 100
        nopreempt
}

这里,更改完之后,仅以priority优先级来确定MASTER和BACKUP。

五、keepalived针对nginx的监控

我们通过用keepalived来实现了nginx负载均衡的高可用性,但是,如果nginx自身因为各种异常而死掉或者假死了,那么,web服务仍然会变的不可用。这时,就要通过监控nginx进程状态,写监控脚本来处理了。

这里,抄录网上的一个脚本:<br/>

vrrp_script配置如下:<br/>

vrrp_script check_nginx {

            script "/usr/local/keepalived-1.3.4/nginx_check.sh"

            interval 3

            weight -2

}

script一般有两种写法: 

1.通过脚本执行的返回结果,改变优先级,keepalived继续发送通告消息,backup比较优先级再决定

2.脚本里面检测到异常,直接关闭keepalived进程,backup机器接收不到advertisement会抢占IP ,我们采用的是第二种:

看一下nginx_check.sh内容:

#!/bin/bash
nxPidNum=ps&nbsp;-C&nbsp;nginx&nbsp;--no-header&nbsp;|wc&nbsp;-l
keepalivedPidNum=ps&nbsp;-C&nbsp;keepalived&nbsp;--no-header&nbsp;|wc&nbsp;-l          
if [ $nxPidNum -eq 0 ]; then                          
    systemctl start nginx  
    sleep 3
    if [ ps&nbsp;-C&nbsp;nginx&nbsp;--no-header&nbsp;|wc&nbsp;-l -eq 0 ];then
        killall keepalived                     
    elif [ $keepalivedPidNum -eq 0 ];then
        systemctal start keepalived
    fi
elif [ $keepalivedPidNum -eq 0 ];then
    systemctl start keepalived
fi

<br/>

// 查看全文
2021 11月 10

Centos7学习笔记(二十二)- HTTPS

一、http劫持原理

首先通过dns污染,将访问的目标服务器,解析到问题服务器。问题服务器上,做nginx代理,利用sub_filter模块,可以匹配并替换原目标服务器上的部分(或全部)内容,以达到广告发布、挂马等目的。

最好的防范措施是hosts直接解析,防止污染。


二、https原理

http原理具体来说,是很复杂的。这里仅简单描述一下它的大致流程——

网站服务器先通过向“登记机构”发起“证书签名申请”(CSR),CA机构在获取CSR后,确认无误,将证书颁发给网站服务器,其证书还包括公钥和私钥内容。网站在获取CA颁发的证书后,需要在自身部署好证书验证内容(nginx配置)。

当浏览器发起访问时,它又会经过一系列复杂的验证过程,其中,关于如何验证证书合法性,它通过3方面来验证:

1. 验证证书是否在有效期内。

  在服务端面返回的证书中会包含证书的有效期,可以通过失效日期来验证 证书是否过期

2. 验证证书是否被吊销了。

  被吊销后的证书是无效的。验证吊销有CRL(证书吊销列表)和OCSP(在线证书检查)两种方法。

证书被吊销后会被记录在CRL中,CA会定期发布CRL。应用程序可以依靠CRL来检查证书是否被吊销了。

CRL有两个缺点,一是有可能会很大,下载很麻烦。针对这种情况有增量CRL这种方案。二是有滞后性,就算证书被吊销了,应用也只能等到发布最新的CRL后才能知道。

增量CRL也能解决一部分问题,但没有彻底解决。OCSP是在线证书状态检查协议。应用按照标准发送一个请求,对某张证书进行查询,之后服务器返回证书状态。

OCSP可以认为是即时的(实际实现中可能会有一定延迟),所以没有CRL的缺点。

 3. 验证证书是否是上级CA签发的。

        windows中保留了所有受信任的根证书,浏览器可以查看信任的根证书,自然可以验证web服务器的证书,
是不是由这些受信任根证书颁发的或者受信任根证书的二级证书机构颁发的(根证书机构可能会受权给底下的中级证书机构,然后由中级证书机构颁发中级证书)

在验证证书的时候,浏览器会调用系统的证书管理器接口对证书路径中的所有证书一级一级的进行验证,只有路径中所有的证书都是受信的,整个验证的结果才是受信


三、https证书类型及证书的颁发

https证书无非三种:

DV(个人用或者测试用,多为免费,多为仅对单一域名有效)

OV(企业用,可根据情况申请单一域名、多域名或者通配符域名(*.example.com)证书)

EV(增强型OV证书,一般用于大型公司、金融公司、政府机构)

证书也是可以自签发的,流程如下:

a、生成CA根证书

openssl genrsa -des3 -out myCA.key 2048    #www
openssl req -x509 -new -key myCA.key -sha256 -days 36000 -out myCA.pem

b、生成服务器私钥和证书请求

openssl genrsa -out www.xyz.key 2048

openssl req -new -key www.xyz.key -out www.xyz.csr

c、签发服务器证书

openssl x509 -req -in www.xyz.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial \
-out www.xyz.crt -days 36000 -sha256

d、查看服务器私钥及证书

openssl rsa -noout -text -in www.xyz.key     #查看私钥
openssl req -noout -text -in www.xyz.csr      #查看证书请求文件
openssl x509 -noout -text -in www.xyz.crt     #查看CA证书

e、服务器导入CA根证书

yum install -y ca-certificates
cp myCA.pem /usr/local/share/ca-certificates/myCA.crt
update-ca-certificates


四、证书在nginx中的部署

涉及三个指令:

Syntax: ssl on | off;
Default: ssl off;
context: http, server
Syntax: ssl_certificate  file;
Default: - ;
context: http, server
Syntax: ssl_certificate_key keyfile;
Default: - ;
context: http, server

单站点https配置示例:

server
{
    listen 80;
    server_name www.hcrbbs.top;
    rewrite ^(.*) https://$server_name$1 redirect;
}
server
{
    listen 443;
    server_name www.hcrbbs.top;
    root /www/html/;
    index index.php;
    ssl on;
    ssl_certificate /etc/nginx/cert/5370791_www.hcrbbs.top.pem; ### 证书换成你的
    ssl_certificate_key /etc/nginx/cert/5370791_www.hcrbbs.top.key; ### 证书换成你的
    location / {
        ....
    }
}

带有负载均衡的配置示例:

后端:

server
{
    listen 80;                                                《=========后端不需要配置ssl
    server_name www.hcrbbs.top;   
    root /www/html/;
    index index.php;
    location / {
        ....
    }
}

负载均衡LB上:

upstream website {
    server    172.16.1.17:80;
    server    172.16.1.18:80;
    server    172.16.1.19:80;
    server    172.16.1.20:80;
}
server {
    listen 443;                        《==============前端负载均衡监听443,开启ssl即可
    server_name    www.hcrbbs.top; 
    ssl    on;
    ssl_certificate    cert/5370791_www.hcrbbs.top.pem; 
    ssl_certificate_key /etc/nginx/cert/5370791_www.hcrbbs.top.key;
    location / {
        proxy_pass    http://website;
        proxy_set_header HOST    $http_host;
    }
}
server {
    listen 80;
    server_name     www.hcrbbs.top; 
    retunr 302 https://$server_name$request_uri;
}

下面贴上一个完整的前端四层负载均衡做端口转发,七层负载均衡做https,后端web集群仍然配置http的示例配置:

前端四层做端口转发及七层负载均衡:

stream {
        log_format      proxy   '$remote_addr&nbsp;-&nbsp;$remote_port - [$time_local]&nbsp;-&nbsp;$server_addr - $server_port&nbsp;&nbsp;$protocol '
                                 '"$status&quot;&nbsp;&quot;$bytes_received" "$upstream_addr&quot;&nbsp;&quot;$upstream_bytes_sent"';
        access_log      /var/log/nginx/proxy.log  proxy;
        upstream lb {
                server 172.16.1.5:443;
                server 172.16.1.6:443;
        }
        server {
                listen 80;
                proxy_connect_timeout 3s;
                proxy_pass      lb;
        }
        server {
                listen 443;
                proxy_connect_timeout 3s;
                proxy_pass      lb;
        }
}

七层负载均衡做https,80端口跳转https:

upstream  testweb {
        server  172.16.1.17:80;
        server  172.16.1.18:80;
        server  172.16.1.19:80;
        server  172.16.1.20:80;
        check interval=3000    rise=2    fall=3    timeout=1000    type=tcp;
        }
server {
        listen 80;
        server_name     test.hcrbbs.top;
        rewrite ^(.*)   https://$server_name$1  redirect;
        #return 302     https://$server_name$request_uri;
}
server {
        listen  443 ssl;
        server_name     test.hcrbbs.top;
        ssl_certificate cert/test.hcrbbs.top.crt;
        ssl_certificate_key     cert/test.hcrbbs.top.key;
        location / {
                proxy_pass      http://testweb;
                include proxy_params;
                proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
                }
         location  /status {
                check_status;
                access_log off;
                allow 10.0.0.0/24;
                deny all;
                }
        }

<br/>

后端web集群其一机器的配置:

server {
        listen 80;
        server_name test.hcrbbs.top;
        root /www/html;
        client_max_body_size    200m;
        location / {
                index    index.php;
        }
        location ~ .*.php$ {
                fastcgi_pass    127.0.0.1:9000;
                fastcgi_index   index.php;
                fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param   HTTPS   on;            《============这里,需要php-fpm开启https传输识别,php才能正常工作。这点尤为重要。
                include fastcgi_params;
        }
}

PS:上面的"fastcgi_param HTTPS    on;",最好替换为“fastcgi_param  HTTPS    $https  if_not_empty;”


五、nginx中SSL的优化

为何要优化?

网站启用https后,会加剧服务器的负担。每次新的TLS连续都需要握手,以便创建共享的加密密钥,在TCP三次握手之上还需要两个来回。传统的http使用TCP三次握手建立连接,而SSL和TLS在这个基础上还需要9个握手包,所以这个负担显而易见。不过,通过重用Session提高https的性能,TLS有几个特点可以抵消额外的握手:重用一个Session。有两个标准会话重用机制:session IDs (RFC 5246) 和 session tickets (RFC 5077),使用其中一个技术,一个客户端可以重用之前创建的会话,这个会话是之前和服务器进行握手成功的,这样可以减少一次来回过程。基于SessionID的会话重用适合现代所有浏览器,FireFox和Chrome甚至还支持 session tickets。


Nginx之ssl_session_cache详解:

Syntax:ssl_session_cache off | none | [builtin[:size]] [shared:name:size];
Default:ssl_session_cache none;
Context:http, server

参数解释:
设置存储session参数的缓存的类型和大小。缓存可以是以下任何一种类型:
off
严禁使用session缓存:nginx明确告诉客户端session可能不会被重用。
none
session缓存的使用被禁止:nginx告诉客户端session可能会被重用,但实际上并不会将session参数存储在缓存中。
builtin
在OpenSSL中构建的缓存;仅由一个工作进程使用。缓存大小在session中指定。如果没有给出大小,则等于20480个会话。使用内置高速缓存可能导致内存碎片。
shared
所有工作进程之间共享缓存。缓存大小以字节为单位指定;一兆字节可以存储大约4000个session。每个共享缓存都应该有一个任意名称。具有相同名称的缓存可以用于多个虚拟服务器。

两种类型的缓存可以同时使用:配置案例:
ssl_session_cache builtin:1000 shared:SSL:10m;
但是只使用shared缓存,而不使用built-in缓存性能应该会更高。

目前使用较多的配置是built-in和shared同时使用:ssl_session_cache builtin:1000 shared:SSL:10m;但是Nginx官方说只使用shared,性能会更高,配置方法为:ssl_session_cache shared:SSL:10m;

Nginx之ssl_session_timeout详解:

Syntax:ssl_session_timeout  time;

Default:ssl_session_timeout 5m;

Context:  http server

中文解释:指定客户端可以重用会话参数的时间(超时之后不可使用)

Nginx之ssl_prefer_server_ciphers详解:

 ssl_prefer_server_ciphers on|off 作用:是否由服务器决定采用哪种加密算法,默认是off。


如果ssl协议支持tlsv1 tls1.1这种老协议,设置为 on ,并配合ssl_ciphers使用

如果ssl协议只支持tlsv1.2 tlsv1.3新协议,设置为 off (nginx默认为off),因为新协议不再采纳此参数

Nginx之ssl_protocols详解:

指定ssl所使用的协议版本。具体见后文示例。

Nginx之ssl_ciphers详解:

指定上述ssl_protocols指定协议所使用的加密算法。配合ssl_protocols和ssl_perfer_server_ciphers使用。


OSCP Stapling

当我们通过HTTPS访问网站的时候,客户端会通过证书颁发机构的证书吊销列表(CRL)或者数字证书在线状态协议(OCSP)记录验证网站服务器的证书是否有效。前一种验证方式是最低效的,CA会不断向CRL文件添加证书吊销记录,CRL文件就会变得越来越大,客户端在验证前就需要耗费越来越长的时间来下载CRL文件。

相比 CRL 验证方式,OCSP 就更加高效,OCSP 每次只查询并获取一条记录。然而这些默认查询 OCSP 的客户端在获得查询结果的响应前势必会一直阻塞后续的事件,在网络情况堪忧的情况下(尤其是大陆地区)会造成较长时间的页面空白。并且一旦有黑客或者组织对CA的OCSP发动DDos攻击,客户端便无法从 OCSP 服务器获取查询结果并完成证书验证, 客户端就可能会提示网站不受信任。

而 OCSP Stapling ,顾名思义,是将查询 OCSP 接口的工作交给服务器来做,服务器除了可以直接查询 OCSP 信息,还可以仅进行少数次查询并将响应缓存起来。当有客户端向服务器发起 TLS 握手请求时,服务器将证书的 OCSP 信息随证书链一同发送给客户端,从而避免了客户端验证会产生的阻塞问题。由于 OCSP 响应是无法伪造的,因此这一过程也不会产生额外的安全问题。

值得注意的是:Nginx会在客户端的HELLO握手信息中返回OCSP记录,并且只有当客户端对Nginx发出OCSP信息请求的情况下,Nginx才会发送缓存的OCSP 权威记录到客户端。

  1. ssl_stapling on;
  2. # OCSP Stapling 开启。OCSP是用于在线查询证书吊销情况的服务,使用OCSP Stapling能将证书有效状态的信息缓存到服务器,提高 TLS 握手速度
  3. ssl_stapling_verify on;
  4. # OCSP Stapling 验证开启
  5. ssl_trusted_certificate /etc/nginx/cert/trustchain.crt;


  6. # OCSP Stapling 的证书位置(完整的证书链)
  7. resolver 233.5.5.5 233.6.6.6 valid=300s;
  8. # 用于查询 OCSP 服务器的DNS
  9. resolver_timeout 5s;
  10. #查询域名超时时间


注意上述 DNS 是阿里云的,如果不信任的话可以改成 Google 的:8.8.8.8 8.8.4.4 [2001:4860:4860::8888] [2001:4860:4860::8844]


根据以上SSL优化的相关指令,mozilla wiki中推荐了三种配置,分别是最新配置、中等兼容(默认)和旧的向后兼容性。。具体见下:

A、最新配置

对于具有支持 TLS 1.3 且不需要向后兼容的客户端服务,最新配置提供了极高级别的安全性。

<br/>

# generated 2021-06-22, Mozilla Guideline v5.6, nginx 1.17.7, OpenSSL 1.1.1d, modern configuration

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    
    location / {
        return 301 https://$host$request_uri;
    }
}
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    
    ssl_certificate /path/to/signed_cert_plus_intermediates;
    ssl_certificate_key /path/to/private_key;
    
    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
    ssl_session_tickets off;
    
    # modern configuration
    ssl_protocols TLSv1.3;
    ssl_prefer_server_ciphers off;
    
    # HSTS (ngx_http_headers_module is required) (63072000 seconds)
    add_header Strict-Transport-Security "max-age=63072000" always;
    
    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    
    # verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;
    
    # replace with the IP address of your resolver
    resolver 127.0.0.1;
}

B、中等兼容(默认)

对于不需要与旧客户端兼容的服务,例如 Windows XP 或旧版本的 OpenSSL。这是绝大多数服务的推荐配置,因为它高度安全并且与过去五年(或更长时间)发布的几乎所有客户端兼容。

# generated 2021-06-22, Mozilla Guideline v5.6, nginx 1.17.7, OpenSSL 1.1.1d, intermediate configuration

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    
    location / {
        return 301 https://$host$request_uri;
    }
}
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    
    ssl_certificate /path/to/signed_cert_plus_intermediates;
    ssl_certificate_key /path/to/private_key;
    
    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
    ssl_session_tickets off;
    
    # curl https://ssl-config.mozilla.org/ffdhe2048.txt ;> /path/to/dhparam
    ssl_dhparam /path/to/dhparam;
    
    # intermediate configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    
    # HSTS (ngx_http_headers_module is required) (63072000 seconds)
    add_header Strict-Transport-Security "max-age=63072000" always;
    
    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    
    # verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;
    
    # replace with the IP address of your resolver
    resolver 127.0.0.1;
}

C、旧的向后兼容性

与许多非常老的客户端兼容,只能作为最后的手段使用。

# generated 2021-06-22, Mozilla Guideline v5.6, nginx 1.17.7, OpenSSL 1.1.1d, old configuration

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    
    location / {
        return 301 https://$host$request_uri;
    }
}
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    
    ssl_certificate /path/to/signed_cert_plus_intermediates;
    ssl_certificate_key /path/to/private_key;
    
    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
    ssl_session_tickets off;
    
    # openssl dhparam 1024 > /path/to/dhparam
    ssl_dhparam /path/to/dhparam;
    
    # old configuration
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA;
    ssl_prefer_server_ciphers on;
    
    # HSTS (ngx_http_headers_module is required) (63072000 seconds)
    add_header Strict-Transport-Security "max-age=63072000" always;
    
    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    
    # verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;
    
    # replace with the IP address of your resolver
    resolver 127.0.0.1;
}

防止 MIME 类型混淆攻击

add_header X-Content-Type-Options nosniff;

HSTS(HTTP Strict Transport Security,严格传输安全)

<br/>

HSTS 简单说就是在一定时间内强制客户端使用 HTTPS 访问页面。原理如下:

<br/>

在服务器响应头中添加 Strict-Transport-Security,可以设置 max-age

<br/>

用户访问时,服务器种下这个头

<br/>

下次如果使用 HTTP 访问,只要 max-age 未过期,客户端会进行内部跳转,可以看到 307 Redirect Internel 的响应码(注意是客户端浏览器相应的,这里给服务器省下了一次 302 跳转)

<br/>

变成 HTTPS 访问源服务器

<br/>

这个过程有效避免了中间人对 80 端口的劫持。但是这里存在一个问题:如果用户在劫持状态,并且没有访问过源服务器,那么源服务器是没有办法给客户端种下 Strict-Transport-Security 响应头的(都被中间人挡下来了)。如何解决?请自行谷歌 HSTS preload。

<br/>

需要注意的是,只有启用 preload 之后才是严格意义上安全的 HTTPS。否则都可能在最薄弱环节被攻破。比如:

<br/>

允许 SSL 连接但不强制从 HTTP 跳转到 HTTPS,用户访问 HTTP 被劫持

<br/>

部署了 HSTS,但用户第一次访问是 HTTP 的,Strict-Transport-Security 的响应头没有作用的机会,还是被劫持

<br/>

不过,即使你启用了 preload 也不是 100% 高枕无忧了,一旦客户端被恶意软件安装了恶意的根证书,这些措施就都没有用了。所以不要轻易安装根证书,不要随意安装可疑软件(尤其在 Windows 上)。比如,Google 曾在2015 年报告说 CNNIC (中国互联网络信息中心)签发的一个中级 CA 签发了一个伪造的 Google 证书,从而导致 Google 和 Mozilla 在其产品中取消了对 CNNIC 后继签发的证书信任。

<br/>

如何解决呢?请看后文的 DNS CAA

<br/>

开启 HSTS

add_header Strict-Transport-Security "max-age=6307200; includeSubdomains; preload";

以上语句开启了 HSTS,并设置有效期为“6307200秒”(6个月),包括子域名(根据情况可删掉),预加载到浏览器缓存(根据情况可删掉,注意加入后需要向 hstspreload.org 提交申请)。

<br/>

注意如果要申请preload,所有子域名都必须使用 HTTPS,且max-age至少设置为一年。有关preload的更多信息可以前往上述网址查看。

<br/>

提醒一下,只有443端口的监听需要设定 HSTS,80端口不需要增加 header。

<br/>

 DNS CAA 保护

<br/>

DNS CAA 保护可以使域持有人可以指定允许为其域签发证书的 CA。它会在 DNS 下发 IP 的同时,同时下发一条资源记录,标记该域名下使用的证书必须由某证书颁发机构颁发。比如本站使用了 Let’s Encrypt 颁发的免费证书,我可以同时使用 CAA 技术标记本站使用的 SSL 证书必须由 Let’s Encrypt 颁发,这样就可以(在一定程度上)解决上面所述的问题。

<br/>

并不是所有 DNS 服务器都支持 CAA 保护,支持 CAA 记录的国外 DNS 服务这里有比较详细的记录:https://sslmate.com/caa/support 。国内的话, CloudXNS 是支持的。

<br/>

开启方法

在你的 DNS 服务的后台添加一条 CAA 记录:

<br/>

Name 可以直接填写顶级域名,会自动应用到多级域名

<br/>

CAA data 填写 0 issue "证书颁发机构域名",如果你用 Let’s Encrypt 颁发的免费证书,CAA data 部分直接填写 0 issue "letsencrypt.org" 即可。

<br/>

你还可以添加一条为 0 iodef "mailto:你的邮箱" 的 CAA 记录,表示如果发现违背 CAA 记录的情况给这个邮箱发邮件通知

<br/><br/>

<br/>

// 查看全文
2021 11月 02