Linux伺服器 Linux伺服器

資工所專業課程上課教材

資工所專業課程 > 課程內容 > 專題二 - 實體主機母系統 (Host) 的設計

專題二 - 實體主機母系統 (Host) 的設計

上次更新日期 2019/07/07

之所以要架設虛擬機器,是因為虛擬機器具有可以簡單方便回收的動作,而且設定方面較有彈性,能夠解決我們實體機器不足的問題。 那因為虛擬機器已經負責了原本實體主機所應該要執行的任務,因此,我們的實體機器母系統 (Host OS) 就得要有能力提供更加穩定的環境, 讓在這個母系統上面跑的客戶端系統 (Guest OS, 其實就是虛擬機器) 可以提供更好的效能與服務。

因此,這個 Host OS 上面,應該盡量減少不必要的服務,同時減少讓用戶登入的機會,也限制了連線的管理員身份, 這樣在系統的保護上面,可能會比較好一些。此外,效能方面也需要進行一些調整,如果 Host OS 效能已經很爛, 那麼 Guest OS 的效能當然會更糟!所以,這得要好好的來設計一下較好喔!

  • Host OS 的整理
  • 效能測試軟體
  • 特別的防火牆管理與跳板管理 (knockd, fail2ban)
  • 參考資料

Host OS 的整理

上週我們主要的目的是讓整個系統的硬體資源穩定耐操!本週我們則需要針對這個母系統來進行一些設計,除了可以讓我們掌握住系統的狀態之外, 也能夠減少不必要的資安問題。不過,在開始之前,我們大概針對母系統與客戶系統做個簡單的說明。

  • 母系統:就是 Host OS,亦即是實體主機上面的作業系統。
  • 虛擬機器:就是 Virtual Machine 簡稱 VM,也就是在母系統上面模擬出來的一個虛擬的機器!這個機器有 CPU、記憶體、磁碟與相關的週邊設備。
  • 客戶端系統:就是 Guest OS,也就是在 VM 上面安裝的作業系統。

Host OS 大概是最重要的!畢竟它是所有 VM 的最底層系統,沒有 Host OS,所有的 VM 當然就不存在!因此,這個 Host OS 就得要比較穩定,而且要針對你的系統進行一些優化才好:

  1. 系統軟體自動更新與維護:

    讓你的母系統能夠更快、更有效率的自我更新,這也是相當重要的步驟!此外,鳥哥不喜歡有持續的校時服務, 因此,定期執行時間校準,也是很重要的!所以,就來建立一隻自我維護的腳本吧!

    1. 先檢查時間,同時進行網路自動校時!
      # date          <==單純查閱時間喔!
      # timedatectl   <==檢測日期時間與時區
      # date --help   <==可以自行檢查如何設定喔!
      # hwclock [-rw] <==查看 BIOS 的時間鐘
      
    2. 修改 yum 來源,直接指向崑山計中 (http://ftp.ksu.edu.tw)
      # cd /etc/yum.repos.d/
      # vim CentOSxxx
      # yum repolist [all]
      # yum clean all
      
    3. 進行一次全系統更新
      # yum update
      
    4. 觀察一下核心的項目 (rpm -qa | grep kernel | sort) 以及核心位置的目錄資料 (/lib/modules)
      # rpm -qa | grep kernel | sort
      # rpm -e kernel-xxx 
      # ll /lib/modules
      
    5. 在 /root/bin 底下,建立一隻名為 maintain.sh 的 shell script,且每日 1:20 執行這個指令。
      # mkdir /root/bin
      # vim /root/bin/maintain.sh
      # chmod a+x /etc/crontab
      # vim /etc/crontab
      
    6. 在 maintain.sh 裡面:
      • 先對我們主機所在環境可以網路校時的主機進行校時的任務: ntpdate 120.114.100.1
      • 進行全系統更新

  2. 讓系統維護狀態可以讓你瞧見:

    如同上面的情況,我們每天進行系統的維護,但是維護的結果到底是誰可以看得到?難道得要登入系統來查閱? 似乎沒必要如此~如果能夠將資料每日彙整成一份 email 寄給我們,似乎會是比較好的管理行為!那是否要架設 mail server 呢? 好像不必要喔!來玩玩先!

    1. 因為我們的環境裡面已經有既有的 mail server,所以無須重新自己設定 mail server,因為目的不同! 我們只需要讓這部 server 的訊息,可以傳送到外部的 public mail server 即可。
    2. 修改 /etc/postfix/main.cf 的內容,將 relayhost 設定為 [mail.ksu.edu.tw] 的模樣, 重新啟動 postfix 之後,就支援信件轉送到 relayhost 的機器上了!
    3. 再加上 /etc/aliases 修改 root 的收件人成為 root: root,yourname@mail.ksu.edu.tw 這樣, 當 root 有信件時,就可以送一份給你在 mail.ksu.edu.tw 上面的帳號!如果沒有,那可能還是收不到...
      # echo "relay test" | mail -s 'check' root
      # mail
      # tail -n 100 /var/log/maillog
      

  3. 系統安全強化:

    還有一些小細節需要處理一下比較好!包括服務的關閉、防火牆的處理、特殊連線服務的處置等等行為, 這都與你的系統安全有關喔!另外,系統安全除了所謂的網路安全之外,還有資料安全!我們得要隨時去檢查硬碟是否正常運作! 因為資料都在上頭的緣故啊!

    1. 未來可能會開發一些奇怪的東西,因此建議 SELinux 設定改為 permissive 即可。
      # vim /etc/selinux/config
      # getenforce
      
    2. 將不必要的服務關閉,對 Internet 提供服務的,只需要 port 22 即可
      • 記得讓 root 不能登入系統
      • 記得讓管理員一般帳號可以透過 sudo 切換身份
      • 記得 sshd_config 將 DNS 反查功能關閉
      # netstat -tlunp
      # vim /etc/ssh/sshd_config
      # 這裡處理 root 無法 ssh 以及 DNS 反查取消
      # systemctl restart sshd
      
    3. 將 firewalld 服務關閉,改成 iptables 的狀態。
      • 預設的防火牆規則啟動
      • 放行所在地內部區域網路 (本例為資訊傳播系) 的 ssh 連線
      • 記得需要處理一下其他可能需要針對你常用的 IP 進行完全解鎖的功能。
      • NAT 的部份,針對 192.168.19.0/24 這一段進行 SNAT 的設定。
      • 記得 ip_forward 需要啟用喔!
      # yum install iptables-ser*
      # systemctl disable firewalld
      # systemctl stop firewalld
      # systemctl start iptables
      # systemctl enable iptables
      # iptables-save
      # iptables-save > firewall.sh
      # vim firewall.sh
      
    4. 定期觀察你的磁碟陣列狀態:
      • lsblk
      • cat /proc/mdstat
      • mdadm --detail /dev/md126 (注意看 State 必須要是 clean 才好!)
      • 透過 smart 查看系統上面的所有硬碟資訊: smartctl --scan
      • 透過 smart 列出兩顆實體硬碟的狀態: smartctl --all /dev/sda
      • 透過 smart 自我測試磁碟的好壞: smartctl -t [short|long] /dev/sda
    5. 將磁碟陣列檢測的結果,比較重要的項目列出到 /root/bin/maintain.sh 裡面去喔!很重要!很重要!

  4. 系統效能調整:

    所謂的效能有 CPU 效能、網路效能、磁碟 I/O 效能等等,底下我們就一項一項來處理相關的效能問題!

    1. CPU 與系統:先透過 CentOS 官方提供的效能調校服務 (tuned) 來處理預設的效能問題:

      為了簡化系統優化的設定流程, Red Hat 有推出一套名為 tuned 的服務,這個服務提供了 tuned-adm 的指令來協助我們設定。 這個服務會依據你的系統使用情境,來給予不同的優化設定,包括 CPU 與核心參數的優化! 作法相對得很簡單:

      • 先透過 tuned-adm list 來查看目前的系統自動調整效能
      • 可以使用 tuned-adm profile XXX 來設定好使用的 profile
      • 現在,請將你的 tuned profile 指定為 virtual-host 或者是 throughput-performance 的相關類型!
      • 事實上,還有個 tuned-adm recommend 與 tuned-adm verify 之類的方式來推薦與檢查喔! 如果想要知道 verify 到底在檢查什麼?可以查閱 /var/log/tuned/tuned.log 的內容喔。
    2. 核心參數:可以針對主機的網路參數進行優化,這通常是 tuned-adm 沒有指定到的部份。 不過設定後,比較有影響的應該是內網互相連線的狀態,外網倒是影響不大的。
      # vim /etc/sysctl.d/vbird.conf
      net.core.optmem_max     =  262144
      net.core.rmem_default   =  262144
      net.core.wmem_default   =  262144
      net.core.rmem_max       = 8388608
      net.core.wmem_max       = 8388608
      net.ipv4.tcp_rmem       = 4096 87380 8388608
      net.ipv4.tcp_wmem       = 4096 65536 8388608
      net.ipv4.tcp_tw_reuse           = 1
      net.ipv4.tcp_tw_recycle         = 1
      net.ipv4.tcp_window_scaling     = 1
      net.ipv4.tcp_sack               = 0
      net.ipv4.tcp_timestamps         = 0
      net.ipv4.tcp_syncookies         = 0
      net.core.netdev_max_backlog     = 10000
      net.ipv4.ip_forward           = 1
      
      # sysctl -p /etc/sysctl.d/vbird.conf
      
      可以將上述的資料寫入 /etc/sysctl.d/somename.conf ,未來會自動生效,或者使用『 sysctl -p filename 』立刻啟動!
    3. 網卡的效能調整:一般網卡為了節省 CPU 的效能,所以預設都會將很多莫名其妙的事情交給網卡自己做。 不過,這樣的效能好像並不見得會很好!尤其是在大量資料接收傳送時,這些動作可能會影響到系統的網路效能! 所以,作為 Server 的角度來看,最好將底下的設定停用較佳!
      # ip link show
      # ethtool -i [nicname]
      # vim /root/bin/performance.sh
      #!/bin/bash
      for nic in [nicname]
      do
              ifconfig ${nic} txqueuelen 10000
              ethtool -G ${nic} rx 4096 tx 4096
              ethtool -K ${nic} lro off gro off
      done
      
      # chmod a+x /root/bin/performance.sh
      # vim /etc/rc.d/rc.local
      sh /root/bin/performance.sh
      
      # chmod a+x /etc/rc.d/rc.local
      
      可將上述的資料寫入 /etc/rc.d/rc.local,每次自動生效
    4. 磁碟 I/O 調整:因為 VM 還是會讀到本機的檔案,而檔案在本機的 Software RAID 上頭,因此, 當然就得要針對 Host 系統進行一些調整較好。基本上,Linux 大致上會針對本機的 software raid 進行最佳化, 不過,我們還是可以進行一些微調的。
      # 先查詢硬碟是否啟動了 NCQ 呢?
      # dmesg | grep NCQ
      [    1.941177] ata2.00: 976773168 sectors, multi 16: LBA48 NCQ (depth 31/32)
      [    2.018528] ata4.00: 976773168 sectors, multi 16: LBA48 NCQ (depth 31/32)
      [    2.039796] ata3.00: 976773168 sectors, multi 16: LBA48 NCQ (depth 31/32)
      [    2.043903] ata1.00: 976773168 sectors, multi 16: LBA48 NCQ (depth 31/32)
      # 如果出現了 NCQ (depth 31/32) ,那就代表啟用了!如果是只有 1/32,就代表支援但沒啟用。
      
      # echo 1 > /sys/block/sda/device/queue_depth
      # 這代表關閉 NCQ 喔!如果將 1 換成 31,就代表啟用!
      
      因為我們目前的環境下,使用到的是 RAID10 的 Intel 主機板內建磁碟陣列,可以透過作業系統來處理資料的定位, 似乎不用啟動 NCQ !很多的文章都提到,關閉 NCQ 對於 Linux RAID 會有幫助!如果是單顆磁碟,那就一定要啟用 NCQ 比較好!
      # 找出磁碟的檔名之後,設定
      # lsblk -ti
      # vim /root/bin/performance.sh
      # 先針對實體磁碟,(1)設定 16M 的預讀功能,(2)設定 deadline 佇列,(3)關閉 NCQ,(4)增加佇列數量
      for hdd in sda sdb sbc sdd
      do
      	echo 16384    > /sys/block/${hdd}/queue/read_ahead_kb
      	echo deadline > /sys/block/${hdd}/queue/scheduler
      	echo 1        > /sys/block/${hdd}/device/queue_depth
      	echo 256      > /sys/block/${hdd}/queue/nr_requests
      done
      # 再針對 /dev/md126 進行調整
      	echo 32768    > /sys/block/md126/queue/read_ahead_kb
      	echo 512      > /sys/block/${hdd}/queue/nr_requests
      
      # sh /root/bin/performance.sh
      
    5. 檔案系統優化:

      當資料變成電子數據資訊後,傳輸到磁碟內的時候,主要是透過檔案系統來處理的。 檔案系統有一定的方式來轉換這個過程。但是,我們後端實際是使用 software RAID 來進行資料放置到磁碟的動作, software RAID 又有自己的資料放置方式與 chunk 的資料大小 (我們這個範例中,使用的是 chunk 64K 的格式)。 這當中如果給予的資料大小不一樣時,那在不同的界面就需要再透過一層轉換,這可能會造成些許效能的誤差。

      因此,通常在格式化的時候,檔案系統都會針對是否具有 RAID 的資料來進行檔案系統的優化。 我們以 XFS 檔案系統搭配 RAID 來說明。

      # df /vmdisk
      檔案系統         1K-區段  已用      可用 已用% 掛載點
      /dev/md126p4   862893404 34720 862858684    1% /vmdisk
      
      # xfs_info /dev/md126p4
      meta-data=/dev/md126p4           isize=512    agcount=32, agsize=6744656 blks
               =                       sectsz=4096  attr=2, projid32bit=1
               =                       crc=1        finobt=0 spinodes=0
      data     =                       bsize=4096   blocks=215828736, imaxpct=25
               =                       sunit=16     swidth=32 blks
      naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
      log      =internal               bsize=4096   blocks=105385, version=2
               =                       sectsz=4096  sunit=1 blks, lazy-count=1
      realtime =none                   extsz=4096   blocks=0, rtextents=0
      

      以上面顯示的資料為例,在 /dev/md126p4 這個檔案系統內:

      • 每一個格式化後的 block 容量為 4K (就是 bsize=4096);
      • 而 chunk (或 stripe) 佔用的 block 數量 (那個 sunit 的數值) 為 16 個,因此在這個檔案系統中,預設的資料大小就是 16*4K = 64K!
      • 而每次傳輸的資料,使用的資料頻寬為 switdh=32 個 block,也就是 32*4K = 128K 的資料頻寬。 也就是說,每次傳送出 128K 的資料,裡面使用了 2 個 sunit 的意思!

      現在來看看既有的 software RAID 設定:

      # cat /proc/mdstat
      Personalities : [raid10]
      md126 : active raid10 sda[3] sdb[2] sdc[1] sdd[0]
            976768000 blocks super external:/md127/0 64K chunks 2 near-copies [4/4] [UUUU]
      
      md127 : inactive sdd[3](S) sdc[2](S) sda[1](S) sdb[0](S)
            10336 blocks super external:imsm
      
      unused devices: 
      

      chunk 就是 64K,剛 XFS 格式相同!而因為我們使用 4 顆磁碟做成 RAID10,因此只有 2 顆磁碟在進行分散式寫入, 亦即是每顆磁碟可以記載 64K,所以總頻寬會是 64K*2 = 128K,也就是剛剛好 swidth 的資料量!

      如果你的 XFS 顯示的資料與實際的 RAID 不相符的時候,最好進行重新格式化來處理!這樣資料的寫入會比較正常些! 那該如何處理?其實很簡單!我們的 /vmdisk 尚未使用,所以先卸載,之後重新格式化!格式化的時候,輸入的參數是:

      • su=64K :因為 chunk 為 64K 的緣故
      • sw=2 :因為 RAID10 共 4 顆,所以有兩顆分散寫入的意思。

      最後再修改 /etc/fstab ,輸入最新的 UUID 資料即可!

      # umount /vmdisk
      # mkfs.xfs -f -d su=64k,sw=2 /dev/md126p4
      meta-data=/dev/md126p4           isize=512    agcount=8, agsize=262128 blks
               =                       sectsz=4096  attr=2, projid32bit=1
               =                       crc=1        finobt=0, sparse=0
      data     =                       bsize=4096   blocks=2097024, imaxpct=25
               =                       sunit=16     swidth=32 blks
      naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
      log      =internal log           bsize=4096   blocks=2560, version=2
               =                       sectsz=4096  sunit=1 blks, lazy-count=1
      realtime =none                   extsz=4096   blocks=0, rtextents=0
      
      # blkid /dev/md126p4
      /dev/md126p4: UUID="05c65eaa-6bf9-4d49-a877-51753e823d7c" TYPE="xfs" PARTUUID="653d4a10-7cde-4106-835c-d70ca8248f75"
      
      # vim /etc/fstab
      # mount -a
      

效能測試軟體

不是做了上述的設定,系統效能就一定會很好!不見得的!需要有數據佐證才行!所以,當然就得要實際來測試一下系統的效能才行! 每種效能有各自的測試軟體,而測試也得依據你的環境不同而進行個別的處置才行。底下我們介紹幾個常見的效能測試軟體,大家互相測試看看囉!

  1. 網路效能測試,使用 iperf3 軟體

    網路測試當然就得要有 Server 與 Client 端了!不同的資料流向可能產生的效率並不一樣喔!我們建議可以簡單的使用 iperf3 這個軟體來測試 Server/client 兩端的網路傳輸喔!

    1. iperf3 軟體下載:每個同學各自前往底下的網站,下載所需要的軟體。因為我們使用的是 CentOS 7,因此直接下載 3.1.3 那個 64 位元的版本即可: 下載完畢直接安裝它即可:
      # yum install ./iperf3-3.1.3-1.fc24.x86_64.rpm
      
    2. Server 端的執行:同學兩兩成對,一個當 Server 一個當 Client,這個部份是針對 Server 的同學實做!
      # iperf3 -s &
      Server listening on 5201
      
      # netstat -tlunp
      Active Internet connections (only servers)
      Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
      tcp6       0      0 :::5201                 :::*                    LISTEN      5944/iperf3
      
      # iptables -I INPUT -p tcp --dport 5201 -j ACCEPT
      
    3. Client 端的執行:另一位同學取得 Server 的 IP 之後,就可以這樣進行測試:
      # 流量從 client 傳向 server 的方向:
      # iperf3 -c 172.16.10.101 -t 10 -i 5
      Connecting to host 172.16.10.101, port 5201
      [  4] local 172.16.60.100 port 59204 connected to 172.16.10.101 port 5201
      [ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
      [  4]   0.00-5.00   sec  9.85 GBytes  16.9 Gbits/sec    2   2.82 MBytes
      [  4]   5.00-10.00  sec  10.3 GBytes  17.7 Gbits/sec    0   2.96 MBytes
      - - - - - - - - - - - - - - - - - - - - - - - - -
      [ ID] Interval           Transfer     Bandwidth       Retr
      [  4]   0.00-10.00  sec  20.1 GBytes  17.3 Gbits/sec    2             sender
      [  4]   0.00-10.00  sec  20.1 GBytes  17.3 Gbits/sec                  receiver
      
      相關的意義是:
      • -c Server_IP:使用 client 模式,連線到 Server 去;
      • -t 10:僅偵測 10 秒鐘
      • -i 5:每 5 秒鐘報告一次傳輸的資訊
      # 流量從 server 傳向 client 的方向:
      # iperf3 -c 172.16.10.101 -t 10 -i 5 -R
      Connecting to host 172.16.10.101, port 5201
      Reverse mode, remote host 172.16.10.101 is sending
      [  4] local 172.16.60.100 port 59210 connected to 172.16.10.101 port 5201
      [ ID] Interval           Transfer     Bandwidth
      [  4]   0.00-5.00   sec  9.53 GBytes  16.4 Gbits/sec
      [  4]   5.00-10.00  sec  9.04 GBytes  15.5 Gbits/sec
      - - - - - - - - - - - - - - - - - - - - - - - - -
      [ ID] Interval           Transfer     Bandwidth       Retr
      [  4]   0.00-10.00  sec  18.6 GBytes  16.0 Gbits/sec    0             sender
      [  4]   0.00-10.00  sec  18.6 GBytes  16.0 Gbits/sec                  receiver
      
  2. 使用 fio 執行磁碟 I/O 的測試方式:

    磁碟的測試會比較困擾,因為不同的運作會有不一樣的設計方式!舉例來說,檔案系統通常使用隨機存取的模式,而大型檔案的讀寫, 通常卻是連續讀寫的方式!因此,測試的項目差異很大!無論如何,我們還是可以透過一個名為 fio 的測試軟體,來測試整體磁碟讀寫的效能!

    1. 安裝:fio 已經包含在 CentOS 官網資料中,所以直接 yum 即可!
      # yum install fio
      # fio --help
      
    2. 執行隨機寫入的檔案測試,得先跑到 /vmdisk 底下喔!此外,因為我們的 chunk 設定為 64K,因此, 假定隨機寫入使用的 block 為 64K 喔!
      # cd /vmdisk
      # fio --name=randwrite --ioengine=libaio --iodepth=1 --rw=randwrite --bs=64k --direct=1 --size=1G --numjobs=1 --runtime=240 --group_reporting
      randwrite: (g=0): rw=randwrite, bs=(R) 64.0KiB-64.0KiB, (W) 64.0KiB-64.0KiB, (T) 64.0KiB-64.0KiB, ioengine=libaio, iodepth=1
      fio-3.1
      Starting 1 process
      randwrite: Laying out IO file (1 file / 1024MiB)
      Jobs: 1 (f=1): [w(1)][100.0%][r=0KiB/s,w=103MiB/s][r=0,w=1652 IOPS][eta 00m:00s]
      randwrite: (groupid=0, jobs=1): err= 0: pid=4240: Fri Jul  5 23:56:54 2019
        write: IOPS=1668, BW=104MiB/s (109MB/s)(1024MiB/9821msec)
          slat (usec): min=19, max=138, avg=25.78, stdev= 5.76
          clat (usec): min=380, max=10512, avg=567.89, stdev=214.54
           lat (usec): min=402, max=10548, avg=594.74, stdev=214.87
          clat percentiles (usec):
           |  1.00th=[  408],  5.00th=[  424], 10.00th=[  433], 20.00th=[  445],
           | 30.00th=[  457], 40.00th=[  469], 50.00th=[  486], 60.00th=[  570],
           | 70.00th=[  676], 80.00th=[  717], 90.00th=[  766], 95.00th=[  799],
           | 99.00th=[  873], 99.50th=[  906], 99.90th=[ 1004], 99.95th=[ 1549],
           | 99.99th=[10552]
         bw (  KiB/s): min=103040, max=112384, per=100.00%, avg=106884.11, stdev=2810.76, samples=19
         iops        : min= 1610, max= 1756, avg=1670.00, stdev=43.92, samples=19
        lat (usec)   : 500=54.00%, 750=33.37%, 1000=12.51%
        lat (msec)   : 2=0.07%, 4=0.02%, 10=0.01%, 20=0.02%
        cpu          : usr=1.45%, sys=4.78%, ctx=16387, majf=0, minf=27
        IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
           submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
           complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
           issued rwt: total=0,16384,0, short=0,0,0, dropped=0,0,0
           latency   : target=0, window=0, percentile=100.00%, depth=1
      
      Run status group 0 (all jobs):
        WRITE: bw=104MiB/s (109MB/s), 104MiB/s-104MiB/s (109MB/s-109MB/s), io=1024MiB (1074MB), run=9821-9821msec
      
      Disk stats (read/write):
          dm-3: ios=0/16062, merge=0/0, ticks=0/8789, in_queue=8789, util=90.36%, aggrios=0/16385, aggrmerge=0/0, aggrticks=0/8939, aggrin_queue=8924, aggrutil=89.85%
        vda: ios=0/16385, merge=0/0, ticks=0/8939, in_queue=8924, util=89.85%
      
    3. 隨機讀寫:再來測試隨機讀寫的方法:
      # fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=test --filename=random_read_write.fio --bs=64k --iodepth=64 --size=512M --readwrite=randrw --rwmixread=75
      test: (g=0): rw=randrw, bs=(R) 64.0KiB-64.0KiB, (W) 64.0KiB-64.0KiB, (T) 64.0KiB-64.0KiB, ioengine=libaio, iodepth=64
      fio-3.1
      Starting 1 process
      test: Laying out IO file (1 file / 512MiB)
      
      test: (groupid=0, jobs=1): err= 0: pid=4247: Fri Jul  5 23:59:11 2019
         read: IOPS=18.7k, BW=1171MiB/s (1228MB/s)(382MiB/326msec)
        write: IOPS=6389, BW=399MiB/s (419MB/s)(130MiB/326msec)
        cpu          : usr=8.62%, sys=46.46%, ctx=2085, majf=0, minf=22
        IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.2%, 32=0.4%, >=64=99.2%
           submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
           complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0%
           issued rwt: total=6109,2083,0, short=0,0,0, dropped=0,0,0
           latency   : target=0, window=0, percentile=100.00%, depth=64
      
      Run status group 0 (all jobs):
         READ: bw=1171MiB/s (1228MB/s), 1171MiB/s-1171MiB/s (1228MB/s-1228MB/s), io=382MiB (400MB), run=326-326msec
        WRITE: bw=399MiB/s (419MB/s), 399MiB/s-399MiB/s (419MB/s-419MB/s), io=130MiB (137MB), run=326-326msec
      
      Disk stats (read/write):
          dm-3: ios=5091/1760, merge=0/0, ticks=12277/4376, in_queue=16704, util=72.31%, aggrios=6109/2083, aggrmerge=0/0, aggrticks=14927/5212, aggrin_queue=20138, aggrutil=71.74%
        vda: ios=6109/2083, merge=0/0, ticks=14927/5212, in_queue=20138, util=71.74%
      
      更詳細的測試資料,可以參考文末的參考文獻喔!

特別的防火牆管理與跳板管理 (knockd, fail2ban)

你可能知道,sshd 是個很厲害的服務,可以讓我們遠端登入。但是為了避免被攻擊,所以我們可能都會將 port 22 只針對少部份來源做放行。 另外,某些單位可能從入口處就直接關閉 port 22 了!如此一來,你當然無法使用 ssh 登入系統了。那怎辦?

通常的作法,大多數都是透過 sshd_config 增加一個埠口,或者是透過 iptables 的 port mapping 功能,直接將某個 port number 轉到 port 22 上面。但是,如此一來,這個新開的 port ,舉例說, 2222 好了,就是在一直開啟的狀態,那,就可能會被一直攻擊啊! 畢竟網路上面的 port scan 軟體實在太多了。

這個時候,你有兩個可以做的動作!一個是透過 knockd 的服務,讓你的這個 port 只有在某個特別的狀態發生時, 才會主動的放行防火牆。另外一個則是透過 fail2ban 的功能,限制錯誤登入的次數與抵擋時間,都可以有效的避免被瘋狂的猜密碼!

  1. 讓你的 sshd 服務啟動一個額外的埠口:

    一般的規劃,來自區域網路對你的 ssh 做連線時,預設還是全放行,然後透過 port 22 來連線,不必加上特別的埠口特徵。 但是如果來自於外部網路的連線,最好不要針對 port 22 啦!畢竟這個 port 22 實在很危險!很容易被攻擊! 所以,我們假設使用 port 2222 來作為外部連線的方式。這時,請讓你的 sshd 服務放行兩個埠口吧!

    # vim /etc/ssh/sshd_config
    Port 22
    Port 2222
    # 上面這兩個設定值需要同時存在才行喔!
    
    # systemctl restart sshd
    # systemctl status  sshd
    ● sshd.service - OpenSSH server daemon
       Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
       Active: active (running) since 六 2019-07-06 22:05:35 CST; 14s ago
         Docs: man:sshd(8)
               man:sshd_config(5)
      Process: 23354 ExecStart=/usr/sbin/sshd $OPTIONS (code=exited, status=0/SUCCESS)
     Main PID: 23355 (sshd)
       CGroup: /system.slice/sshd.service
               └─23355 /usr/sbin/sshd
    
     7月 06 22:05:35 station10-101.gocloud.vm systemd[1]: Starting OpenSSH server daemon...
     7月 06 22:05:35 station10-101.gocloud.vm systemd[1]: PID file /var/run/sshd.pid not readable (yet?) after start.
     7月 06 22:05:35 station10-101.gocloud.vm sshd[23355]: error: Bind to port 2222 on 0.0.0.0 failed: Permission denied.
     7月 06 22:05:35 station10-101.gocloud.vm sshd[23355]: error: Bind to port 2222 on :: failed: Permission denied.
     7月 06 22:05:35 station10-101.gocloud.vm systemd[1]: Started OpenSSH server daemon.
     7月 06 22:05:35 station10-101.gocloud.vm sshd[23355]: Server listening on 0.0.0.0 port 22.
     7月 06 22:05:35 station10-101.gocloud.vm sshd[23355]: Server listening on :: port 22.
    
    你會發現到 port 22 正常沒問題,但是 port 2222 會出現警示!雖然可能還是會放行 (因為我們使用了 permissive 的 SELinux type), 建議你還是加入這個 SELinux 的規則較佳:
    # semanage port -a -t ssh_port_t -p tcp 2222
    # systemctl restart sshd
    # systemctl status  sshd
    ● sshd.service - OpenSSH server daemon
       Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
       Active: active (running) since 六 2019-07-06 22:09:15 CST; 3s ago
         Docs: man:sshd(8)
               man:sshd_config(5)
      Process: 23430 ExecStart=/usr/sbin/sshd $OPTIONS (code=exited, status=0/SUCCESS)
     Main PID: 23431 (sshd)
       CGroup: /system.slice/sshd.service
               └─23431 /usr/sbin/sshd
    
     7月 06 22:09:15 station10-101.gocloud.vm systemd[1]: Starting OpenSSH server daemon...
     7月 06 22:09:15 station10-101.gocloud.vm systemd[1]: PID file /var/run/sshd.pid not readable (yet?) after start.
     7月 06 22:09:15 station10-101.gocloud.vm sshd[23431]: Server listening on 0.0.0.0 port 2222.
     7月 06 22:09:15 station10-101.gocloud.vm sshd[23431]: Server listening on :: port 2222.
     7月 06 22:09:15 station10-101.gocloud.vm sshd[23431]: Server listening on 0.0.0.0 port 22.
     7月 06 22:09:15 station10-101.gocloud.vm systemd[1]: Started OpenSSH server daemon.
     7月 06 22:09:15 station10-101.gocloud.vm sshd[23431]: Server listening on :: port 22.
    
    # netstat -tlunp | grep ssh
    tcp        0      0 0.0.0.0:2222            0.0.0.0:*               LISTEN      23431/sshd
    tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      23431/sshd
    tcp6       0      0 :::2222                 :::*                    LISTEN      23431/sshd
    tcp6       0      0 :::22                   :::*                    LISTEN      23431/sshd
    
    這樣就將我們的 sshd 多放行一個埠口囉!
  2. 使用 knockd 服務來不定時的啟動你的防火牆;

    在上面的設定中,我們多了一個 port 2222 的 ssh 連線監聽埠口,那麼,需要針對整個 Internet 放行嘛? 個人建議是不要啦!畢竟真的很容易被攻擊!那麼,如果你需要連線時,該怎麼連線到 port 2222 呢?都沒有放行防火牆不是? 這個時候,你可以使用 knockd 這個服務來處理。

    1. 什麼是 knockd 呢?

      其實 knockd 是用來動態支援防火牆 iptables 的模組,它的運作方式很簡單,就是載入 knockd 之後, 它會持續偵測 3 個 port,當用戶按照正確的順序去敲擊這三個 port 時, knockd 就會去放行某條防火牆規則! 而當經過一段時間之後,這個新增的防火牆規則就會被取消!有點像底下的模樣:

      上圖中,在伺服器的環境中,knockd 會持續監視 port 1, 2, 3 這 3 個埠口,如果有用戶可以按照順序來敲擊這 3 個埠口時, knockd 會進行如下的動作:

      如上圖,knockd 會針對用戶端的 IP 搭配 knockd 的設定,動態的將 iptables 的規則加入到防火牆的支援當中! 此時,用戶可以在規定的時間內 (大多在 10 分鐘內) 連線到 Server 當中喔!

      如上圖,敲擊過後一段時間 (大約在 10 分鐘內), knockd 會主動的取消這一條新增的防火牆規則。 不過已經連線的用戶無須擔心,因為防火牆規則可以倚靠 ESTABLISHED 的規則來持續連線成功的! 只是如果斷線,那就會失去連線的能力,得要重新敲擊 knockd 才行喔!

    2. 開始安裝 knockd 啦!官網在底下,不過,建議直接前往 nux 的網站下載給 CentOS 7 安裝的 RPM 就好了!
      # wget http://li.nux.ro/download/nux/dextop/el7/x86_64/knock-server-0.7-2.el7.nux.x86_64.rpm
      # wget http://li.nux.ro/download/nux/dextop/el7/x86_64/knock-0.7-2.el7.nux.x86_64.rpm
      # yum install ./knock-*
      # rpm -ql knock-server
      /etc/knockd.conf           <==設定檔所在處
      /etc/rc.d/init.d/knockd    <==服務啟動的腳本,舊的 systemV 的設定方式
      /etc/sysconfig/knockd      <==額外參數
      /usr/sbin/knockd           <==實際的執行檔!
      /usr/share/doc/knock-server-0.7
      /usr/share/doc/knock-server-0.7/COPYING
      /usr/share/doc/knock-server-0.7/ChangeLog
      /usr/share/doc/knock-server-0.7/README.md
      /usr/share/doc/knock-server-0.7/TODO
      /usr/share/man/man1/knockd.1.gz
      

      畢竟 knock 存在時間比較久,目前這個網友協助建立的套件,還是沿用 systemV 的設定,不是使用新的 systemd 的設定。 不過 CentOS 7 對兩者都有支援!還是可以運作無誤的!

    3. 針對 ssh 的 port 2222 進行放行的動作:

      接下來,請修改 knockd 的設定檔,修改的內容也是挺單純的!大致上,就是修改成一個可以自己獨立存在的登錄檔, 然後修改一下我們的埠口序號即可:

      # vim /etc/knockd.conf
      [options]
              UseSyslog
              logfile = /var/log/knockd.log
      
      [opencloseSSH]
              sequence      = 2100:tcp,2200:udp,2300:tcp
              seq_timeout   = 15
              tcpflags      = syn
              start_command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
              cmd_timeout   = 30
              stop_command  = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
      
      # 參數的大致意義如下:
      # [options]:	 與 knockd 環境有關的設定資料;
      # UseSyslog:	 與 knockd 有關的登入資料使用 syslogd 放置到 /var/log/messages
      # [opencloseSSH] 開啟與關閉 SSH 防火牆的設定項目;
      # sequence:	 knockd 偵測的埠口與封包協定 (tcp/udp)
      # seq_timeout:	 需要在幾『秒鐘』內,連續上面的埠口接觸 (sequence 之設定);
      # tcpflags:	 來源封包所需帶有的封包標誌,一般來說, UDP 封包不會有 ack ,
      #		 所以上述的預設標籤與封包,其實是無法吻合的,所以需要修改。
      # start_command: 若連續接觸所有的埠口,則 knockd 開始後續的指令;
      # stop_command:	 若用戶端斷線了,那麼就執行後續的指令
      # cmd_timeout:	 若設定 stop_command 則需此設定,訂定開始與結束防火牆的時間。
      
    4. 開始執行 knockd 服務,同時未來開機也會自動啟動它:
      # 立刻啟動:
      # systemctl start knockd
      
      # 開機就啟動
      # systemctl enable knockd
      # systemctl status knockd
      ● knockd.service - SYSV: Knock is a port-knocking server/client.
         Loaded: loaded (/etc/rc.d/init.d/knockd; bad; vendor preset: disabled)
         Active: active (running) since 日 2019-07-07 00:21:46 CST; 14s ago
           Docs: man:systemd-sysv-generator(8)
       Main PID: 24800 (knockd)
         CGroup: /system.slice/knockd.service
                 └─24800 /usr/sbin/knockd -d
      
       7月 07 00:21:46 station10-101.gocloud.vm systemd[1]: Starting SYSV: Knock is a port-knocking server/client....
       7月 07 00:21:46 station10-101.gocloud.vm knockd[24800]: starting up, listening on eth0
       7月 07 00:21:46 station10-101.gocloud.vm knockd[24796]: Starting knockd: [  OK  ]
       7月 07 00:21:46 station10-101.gocloud.vm systemd[1]: Started SYSV: Knock is a port-knocking server/client..
      
      # LANG=C chkconfig --list
      knockd          0:off   1:off   2:on    3:on    4:on    5:on    6:off
      netconsole      0:off   1:off   2:off   3:off   4:off   5:off   6:off
      network         0:off   1:off   2:on    3:on    4:on    5:on    6:off
      
      這個時候伺服器端就已經處理好了!接下來就等著看看用戶端如何敲擊使用這個防火牆的小外掛囉!
    5. Linux 用戶端敲擊伺服器的方案:

      如果你的用戶端是 Linux 的話,使用剛剛的網址去下載非 knock-server 的那個 rpm 檔案,並且安裝即可。 那如果用戶端是 windows 呢?就得要重新跑到官網去抓正確的軟體了。先來看看 Linux 用戶端怎麼做吧!

      # 在用戶端進行如下的動作:
      # wget http://li.nux.ro/download/nux/dextop/el7/x86_64/knock-0.7-2.el7.nux.x86_64.rpm
      # yum install ./knock-0.7-2.el7.nux.x86_64.rpm
      # knock -v 172.16.10.101 2100:tcp 2200:udp 2300:tcp
      hitting tcp 172.16.10.101:2100
      hitting udp 172.16.10.101:2200
      hitting tcp 172.16.10.101:2300
      # 有的時候,上述的指令可能要多做幾次,這樣比較容易成功!
      
      # 來到 Server 端,查看一下防火牆有沒有增加一條規則呢?
      # iptables-save | grep 2222
      -A INPUT -s 172.16.60.100/32 -p tcp -m tcp --dport 2222 -j ACCEPT
      
      # 回到用戶端,嘗試用 port 2222 登入一下
      # ssh -p 2222 username@172.16.10.101
      
    6. Windows 端敲擊伺服器的方法:先前往底下的網站下載 windows 所需要的軟體: 快速建立執行方式如下:
      1. 將下載的檔案,在 knock-win32\knock-win32-port\Release\ 裡面的 knock 檔案複製;
      2. 到 C:\>Windows\System32\ 底下,貼上這個檔案
      3. 打開 cmd 命令提示字元,並輸入『 knock --help 』查看可執行否?
      4. 在桌面上面,打開文字編輯器,輸入底下的文字:
        knock -v 172.16.10.101 2100:tcp 2200:udp 2300:tcp
        pause
        
      5. 存檔成為 knock-to.bat ,記得副檔名是 .bat 喔!而且檔名不能只是 knock.bat,否則會產生無窮迴圈...
      6. 直接點兩下滑鼠敲擊這個檔案!就可以敲擊 Server 了!
      7. 接下來,使用 pietty 或 putty 等連線軟體來連線到 Server 即可!

    如此一來,我們的 Server 在正常的情況下,就不會被莫名其妙的人所攻擊偵測到 port 2222 的放行, 而且還能夠在連續敲擊某些埠口之後,就能夠順利的開啟防火牆,這樣的跳板當然就會變得更加安全妥當囉!

  3. 使用 fail2ban 讓你的服務有限制的放行錯誤行為:

    通常人算不如天算,某些情況下,你的 ssh 還是可能被攻擊的!所以,如果能夠設定『當重複猜測密碼幾次都是失敗的, 那就將這個 IP 暫時丟進黑名單拒絕登入』時,你的系統將會更加的妥當安全。如何達成呢?就透過 fail2ban 吧! 這個 fail2ban 還挺有趣的!它不只能管 ssh 喔!還可以管理任何需要輸入密碼的服務哩!

    1. 安裝 fail2ban 吧:
      # yum install epel-release
      # yum repolist
      Loaded plugins: fastestmirror, langpacks
      Loading mirror speeds from cached hostfile
       * base: ftp.twaren.net
       * epel: mirror01.idc.hinet.net
       * extras: ftp.twaren.net
       * updates: ftp.twaren.net
      repo id                 repo name                                            status
      base/7/x86_64           CentOS-7 - Base                                      10,019
      epel/x86_64             Extra Packages for Enterprise Linux 7 - x86_64       13,283
      extras/7/x86_64         CentOS-7 - Extras                                       419
      updates/7/x86_64        CentOS-7 - Updates                                    2,231
      repolist: 25,952
      
      # yum-config-manager --disable epel
      # yum repolist
      Loaded plugins: fastestmirror, langpacks
      Loading mirror speeds from cached hostfile
       * base: ftp.twaren.net
       * extras: ftp.twaren.net
       * updates: ftp.twaren.net
      repo id              repo name                    status
      base/7/x86_64        CentOS-7 - Base              10,019
      extras/7/x86_64      CentOS-7 - Extras               419
      updates/7/x86_64     CentOS-7 - Updates            2,231
      repolist: 12,669
      # 事實上, EPEL 並不是隨時都啟用啦!暫時關閉比較妥當!
      
      # yum --enablerepo=epel install fail2ban
      
    2. fail2ban 大部分的設定檔都在 /etc/fail2ban/ 目錄下,預設值都在 /etc/fail2ban/jail.conf 裡面,不過,這個檔案不要亂改! 官方文件是建議新增 jail.local 這個檔案,並將要修改的項目增加在裡頭就好。現在,我們就來針對幾個項目進行設定一下:
      1. 內部網路 127.0.0.0/8, 120.114.140.0/24, 120.114.141.0/24, 120.114.142.0/24 忽略限制
      2. 錯誤登入後的抵擋時間為 3600 秒
      3. 使用 iptables 不用 firewalld 服務
      4. 目前僅針對 sshd 作為限制,且同時針對 port 22, 2222 來設計
      詳細的設定項目是這樣的:
      # vim /etc/fail2ban/jail.local
      [DEFAULT]
      ignoreip = 127.0.0.0/8 120.114.140.0/24 120.114.141.0/24 120.114.142.0/24
      bantime  = 3600
      banaction = iptables-multiport
      
      [sshd]
      enabled = true
      port = ssh,2222
      
      # systemctl start  fail2ban
      # systemctl enable fail2ban
      # systemctl status fail2ban
      ● fail2ban.service - Fail2Ban Service
         Loaded: loaded (/usr/lib/systemd/system/fail2ban.service; enabled; vendor preset: disabled)
         Active: active (running) since 日 2019-07-07 13:46:19 CST; 7s ago
           Docs: man:fail2ban(1)
       Main PID: 304 (fail2ban-server)
         CGroup: /system.slice/fail2ban.service
                 └─304 /usr/bin/python2 -s /usr/bin/fail2ban-server -s /var/run/fail2ban/fail2ban.sock -p /var/run/fail2ban/fail2ban.pid -x -b
      
       7月 07 13:46:19 station10-101.gocloud.vm systemd[1]: Starting Fail2Ban Service...
       7月 07 13:46:19 station10-101.gocloud.vm fail2ban-client[301]: 2019-07-07 13:46:19,623 fail2ban.server         [302]: INFO    Starting Fail2ban v0.9.7
       7月 07 13:46:19 station10-101.gocloud.vm fail2ban-client[301]: 2019-07-07 13:46:19,623 fail2ban.server         [302]: INFO    Starting in daemon mode
       7月 07 13:46:19 station10-101.gocloud.vm systemd[1]: Started Fail2Ban Service.
      
      # fail2ban-client status
      Status
      |- Number of jail:      1
      `- Jail list:   sshd
      
      # fail2ban-client status sshd
      Status for the jail: sshd
      |- Filter
      |  |- Currently failed: 0
      |  |- Total failed:     0
      |  `- Journal matches:  _SYSTEMD_UNIT=sshd.service + _COMM=sshd
      `- Actions
         |- Currently banned: 0
         |- Total banned:     0
         `- Banned IP list:
      
      就這樣,很簡單的就將我們的 sshd 服務設定了登入次數限制的防火牆機制!雖然還是很可能短時間被大量攻擊 (就是 10 分鐘內), 不過,至少總是一個限制的功能!

參考資料

  • 單純使用 iptables 的轉址,讓外部可以透過 port 5050 連線到你系統上的 port 22