搜尋此網誌

顯示具有 工具介紹 標籤的文章。 顯示所有文章
顯示具有 工具介紹 標籤的文章。 顯示所有文章

2016年9月8日 星期四

[工具介紹] 利用 collectd + InfluxDB + Grafana 監測系統效能

傳統上用來監測網路流量、系統效能數據的常用工具多以 RRD 為基本的技術,這類工具包含 MRTG 與 Cacti。RRD 有幾個明顯的缺點,其中一個就是資料的解析度會隨著時間的過去而降低。

例如在 Cacti 中,過去 24 小時的流量圖表是長這樣:

image

但是一周前的 24 小時的流量圖表就變成這般可怕的模樣:

image

當你不幸地需要歷史數據資料時,別說要如何進行分析了,光拿出這種圖表就需要莫大的勇氣。

在講究高清不失真的這個年代,以 RRD 為基礎的工具已經漸漸不符合我們所需。

目前比較常見用來記錄時間數據的工具,應以 ELK stack 為首。ELK stack 以 Elasticsearch 為核心,可用來分析各式各樣的資料。這個特性是它的強項,不過在某些時候卻也變成它的弱點 (過於複雜)。而我今天要介紹的 collectd + InfluxDB + Grafana,正是以處理時間數據為主,所以在製作與時間數據相關的圖表時會更為簡化。
Grafana 本身類似 ELK 的 Kibana,主要作為圖表呈現的部分。而資料來源的部分,除了 InfluxDB 之外,像是 Graphite、OpenTSDB、甚至是 Elasticsearch 也都可以支援。在此架構中,collectd 會負責收集作業系統的效能數據,並將之”餵進” InfluxDB,最後再透過 Grafana 的圖表予以呈現。

在今天的範例中,我將示範如何在 CentOS 7 中利用 collectd + InfluxDB + Grafana 建置一個呈現系統效能數據的儀表板。需特別注意的是,為了簡化範例的說明,所有的套件皆安裝在同一個系統 (IP 位址為 192.168.111.241) 下。而在實務上,這三個套件通常應分散在不同的系統上。

安裝 InfluxDB

  1. 透過官方 RPM Repo 安裝 InfluxDB 套件
  2. cat<<EOF>/etc/yum.repos.d/influxdb.repo
    [influxdb]
    name = InfluxDB Repository - RHEL $releasever
    baseurl = https://repos.influxdata.com/rhel/\$releasever/\$basearch/stable
    enabled = 1
    gpgcheck = 1
    gpgkey = https://repos.influxdata.com/influxdb.key
    EOF yum install influxdb -y systemctl enable influxdb.service systemctl start influxdb.service
  3. 配置防火牆
  4. cat<<EOF>/etc/firewalld/services/influxdb.xml
    <?xml version="1.0" encoding="utf-8"?>
    <service>
      <short>InfluxDB</short>
      <description>InfluxDB is an open source time series database with no external dependencies. It's useful for recording metrics, events, and performing analytics.</description>
      <port protocol="tcp" port="8083"/>
      <port protocol="tcp" port="8086"/>
    </service>
    EOF chmod 600 /etc/firewalld/services/influxdb.xml firewall-cmd --add-service=influxdb --permanent firewall-cmd --reload
  5. 透過指令 influx 進入 influxdb 命令列模式,並輸入下列命令創建管理者帳密
  6. create user admin with password 'password' with all privileges;  
    exit
  7. 利用瀏覽器連結 influxdb 管理介面,網址是 http://192.168.111.241:8083,應可看到類似下列畫面
    0001

安裝 Grafana

  1. 透過官方 RPM Repo 安裝 Grafana 套件
  2. cat<<EOF>/etc/yum.repos.d/grafana.repo
    [grafana]
    name=grafana
    baseurl=https://packagecloud.io/grafana/stable/el/6/\$basearch
    repo_gpgcheck=1
    enabled=1
    gpgcheck=1
    gpgkey=https://packagecloud.io/gpg.key https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana
    sslverify=1
    sslcacert=/etc/pki/tls/certs/ca-bundle.crt
    EOF yum install grafana –y systemctl enable grafana-server.service systemctl start grafana-server.service
  3. 配置防火牆
  4. cat<<EOF>/etc/firewalld/services/grafana.xml
    <?xml version="1.0" encoding="utf-8"?>
    <service>
      <short>Grafana</short>
      <description>Grafana provides a powerful and elegant way to create, explore, and share dashboards and data with your team and the world.</description>
      <port protocol="tcp" port="3000"/>
    </service>
    EOF chmod 600 /etc/firewalld/services/grafana.xml firewall-cmd --add-service=grafana --permanent firewall-cmd --reload

 

設定 influxdb collectd plugin

  1. 安裝 collectd 套件
  2. yum install collectd -y
  3. 修改檔案 /etc/influxdb/influxdb.conf,將 [[collectd]] 區塊的
    enabled = false
    改為
    enabled = true
    bind-address = ":25826" # the bind address
    database = "collections" # Name of the database that will be written to
    retention-policy = ""
    batch-size = 5000 # will flush if this many points get buffered batch-pending = 10 # number of batches that may be pending in memory batch-timeout = "10s"
    read-buffer = 0 # UDP read buffer size, 0 means to use OS default typesdb = "/usr/share/collectd/types.db" #enabled = false
  4. 重新啟動服務
  5. systemctl restart influxdb.service
  6. 配置防火牆
  7. cat<<EOF>/etc/firewalld/services/collectd.xml
    <?xml version="1.0" encoding="utf-8"?>
    <service>
      <short>collectd</short>
      <description>collectd daemon for InfluxDB</description>
      <port protocol="udp" port="25826"/>
    </service>
    EOF firewall-cmd --add-service=collectd --permanent firewall-cmd --reload
  8. 透過指令 influx 進入 influxdb 命令列模式,並輸入下列命令創建資料庫與存取帳密
  9. CREATE DATABASE collections
    CREATE USER collectdrw WITH PASSWORD 'readwrite'
    GRANT ALL ON collections to collectdrw
    CREATE USER collectdread WITH PASSWORD 'readonly'
    GRANT READ ON collections TO collectdread
    exit

設定 collectd for influxdb

這個動作需在需收集效能數據的各主機上分別執行。
  1. 設定 collectd 服務所需的外掛。在此範例中我們沒有另外新增數據收集的外掛,僅增加了將資料送往 InfluxDB 的外掛。
  2. cat<<EOF>/etc/collectd.d/network.conf
    Hostname    "grafana"
    FQDNLookup  false
    LoadPlugin  network
    LoadPlugin  uptime
    <Plugin network>
      Server "192.168.111.241" "25826"
    </Plugin>
    EOF
    systemctl enable collectd.service
    systemctl start collectd.service
  3. 回到 influxdb 的管理介面,重整後在 Database 下拉式選單中應可看到多了一個名為 collections 的資料庫。
    0002
  4. 選取 collections 後,在 Query Templates 中選取 Show Measurements。
    0003
  5. 按下 Enter 後,應可看到類似下面的結果,表示 collections 中已有 cpu_value 等數據。
    0004
  6. 接著,我們輸入 SHOW TAG KEYS FROM "cpu_value" 並按下 Enter,應可看到類似下列畫面。結果顯示 cpu_value 包含四個 key,分別為 host、instance、type 以及 type_instance。
    0005
  7. 接著,我們輸入 SHOW TAG VALUES FROM "cpu_value" WITH KEY = "host" 並按下 Enter,應可看到類似下列畫面。結果顯示 host 這個 key 的內容僅包含 grafana,也就是我們目前唯一已經設定過的 collectd 服務主機名稱。
    0006
  8. 接著,我們輸入 SHOW TAG VALUES FROM "cpu_value" WITH KEY = "host" 並按下 Enter,應可看到類似下列畫面。結果顯示 InfluxDB 已確實收到來自於 collectd 服務的效能數據。
    0007

設定 Grafana Dashboard

  1. 利用瀏覽器連結 Grafana 管理介面,網址是 http://192.168.111.241:3000,應可看到類似下列畫面
    0008
  2. 使用預設帳密 admin / admin 登入系統。
  3. 點選左上角的圖示後會出現主要選單,點選 admin 後可以修改基本資訊與密碼。
    0009
  4. 同樣利用主選單的 Data Sources 選項,進入後按下右上角的 "+Add data source" 按鈕。
  5. 分別填入下列資訊,其中 Url 欄位因為我們選取 direct 的存取方式,所以不能使用預設的 http://localhost:8086,而必須指定為 http://192.168.111.241:8086
    • Name: Collectd
    • Default: 選取與否皆可
    • Type: InfluxDB
    • Url: http://192.168.111.241:8086
    • Access:direct
    • Http Auth: 兩個選項皆不用選取
    • Database: collections
    • User: collectdread
    • Password: readonly
    • Default group by time: 保留空白
  6. 填寫完畢後按下 "Add" 按鈕。如果 Data Source 可以正常連結的話會出現類似下列的成功畫面,否則會出現錯誤訊息。
    0010
  7. 利用主選單的 Dashboards > + New 開始新增儀表板。
  8. 利用 Manage dashboard 的 Settings 功能可修改儀表板的相關設定。
    0011
  9. 在此範例中我們僅設定名稱即可,將之設定為 System Information,設定完畢後按下右上方的 X 即可回到 Dashboard 的畫面。
    0013
  10. 點選左上方的綠色區塊後會出現 ROW 功能選單,選取 Add Panel > Graph。
    0012
  11. Graph 有多個頁籤設定,其中 Metrics 可用來配置資料相關設定。首先,我們須將 Panel data source 改為 Collectd。
  12. 此時我們會發現 Metrics A 內容有所改變,點選後出現圖形化編輯選項。
    0015
  13. Grafana 的圖形化編輯功能相當方便,不過為了方便說明,所以我們透過 Toggle Edit Mode 進入直接編輯模式。
    0016
  14. 輸入下列內容
    SELECT last("value") FROM "load_shortterm" WHERE "host" = 'grafana' AND $timeFilter GROUP BY time($interval) fill(null)
    0017
  15. 透過 "+ Add query" 按鈕另外新增兩個 Metrics,內容分別為
    SELECT last("value") FROM "load_midterm" WHERE "host" = 'grafana' AND $timeFilter GROUP BY time($interval) fill(null)
    SELECT last("value") FROM "load_longterm" WHERE "host" = 'grafana' AND $timeFilter GROUP BY time($interval) fill(null)
    0018
  16. 切換至 General 頁籤,將 Panel 的標題修改為 Load。
    0019
  17. 切換到 Legend 頁籤,勾選 Min、Max、Avg 以及 Current。
    0020
  18. 設定完畢後按下右方的 X 即可回到 Dashboard 畫面,然後按下 CTRL+S 將 Dashboard 予以儲存。
  19. 之後就可以透過 Dashboard 下拉式選單快速連結至我們剛剛新增的 System Information Dashboard 了。
    0021

透過幾個簡單的設定,我們就建立了一個系統負載的數據圖表。

下一步

Grafana 除了圖形類 (Graph) 的報表之外,另有提供表格式 (Table) 、單一數值  (Singlestat) 與其他種類的報表。
此外,我們的範例是針對特定的單一主機來進行配置。但是我們通常不會只有管理一台主機,此時就可以透過 Dashboard 的 Templating 機制設定一個變數用來代表主機名稱,然後將 Metrics 的條件指定為此一變數,而非原先固定的主機名稱。如此一來,Dashboard 就會提供一個所有主機名稱的下拉式選單,方便我們觀看個別主機的相關數據。

盡管如此,我們會發現自己要從無到有刻出一個合用且美觀的報表其實也不是那麼簡單的事情。幸好 Grafana  有提供儀表板匯出/匯入的功能,而且有善心人士已經將一些常見的效能數據儀表板匯出並提供下載 (清單在此)。不過在選用這些儀表板時,需特別注意的是它使用的資料來源是哪一種 (InfluxDB、Graphtie 或是其他的資料來源),以免發生牛頭不對馬嘴的狀況。

2016年9月4日 星期日

[工具介紹] 以 Apache myfixip 模組支援 Proxy Protocol

我在前一篇文章中提到,HAProxy 推出 Proxy Protocol 用以解決在代理伺服器架構下無法取得用戶端 IP 位址的困境。在這篇文章中,我將以 HAProxy 搭配 Apache + myfixip 模組,實際展示如何透過 Proxy Protocol 解決用戶端 IP 位址的問題。
本次展示的架構如下 :

其中代理伺服器使用 CentOS 7 的 HAProxy 1.5.14 套件,而 Web 服務器使用 CentOS 7 的 Apache 套件。
我們先在 Web 服務器放置內容如下的 PHP 腳本,而在 PHP 中,$_SERVER["HTTP_X_FROWARDED_FOR"] 這個變數就代表 HTTP 檔頭 X-Forwarded-For。
<?php
echo nl2br("X-Forwarded-For: " . $_SERVER["HTTP_X_FORWARDED_FOR"] . PHP_EOL);
echo nl2br("Remote Address: " . $_SERVER["REMOTE_ADDR"] . PHP_EOL);

用戶端直接連結 Web 服務器

Direct
當用戶端直接連往 Web 服務器 (192.168.10.12) 上的 PHP 腳本後,可以看到如下的輸出:
HTTP-X-Forwarded-For:
Remote Address: 192.168.10.10
在這種單純的情況下,直接使用 REMOTE_ADDR 變數即可取得用戶端的 IP 位址。

用戶端透過 HAProxy 的  HTTP 模式連往 Web 服務器

HTTP Mode
我們在 HAProxy 使用下列設定:
listen http-proxy
bind 0.0.0.0:80
mode http
option httpchk
option forwardfor
balance source
server web-1 192.168.10.12:80 weight 1 check
當用戶端透過代理伺服器 (192.168.10.11) 連往 Web 服務器 (192.168.10.12) 上的 PHP 腳本後,可以看到如下的輸出:
X-Forwarded-For: 192.168.10.10
Remote Address: 192.168.10.11
在這種情況下,儘管 REMOTE_ADDR 變數顯示為代理伺服器的 IP 位址,但是使用 HTTP_X_FROWARDED_FOR 變數依舊可取得用戶端的 IP 位址。

用戶端透過 HAProxy 的  HTTP 模式連往 Web 服務器

TCP Mode
我們將 HAProxy 的設定修改成如下:
listen http-proxy
bind 0.0.0.0:80
mode tcp
balance source
server web-1 192.168.10.12:80 weight 1 check
當用戶端透過代理伺服器 (192.168.10.11) 連往 Web 服務器 (192.168.10.12) 上的 PHP 腳本後,可以看到如下的輸出:
X-Forwarded-For:
Remote Address: 192.168.10.11
在這種情況下,不存在 HTTP_X_FROWARDED_FOR 變數,而 REMOTE_ADDR 顯示的也是代理伺服器 IP 位址。也就是程式無法正確獲得用戶端 IP 位址。

用戶端透過支援 Proxy Protocol 的設定連往 Web 服務器

Proxy Protocol
因為 HAProxy 本身就內建支援 Proxy Protocol,所以不需安裝額外的套件,只要把設定修改成如下即可:
listen http-proxy
bind 0.0.0.0:80
mode tcp
balance source
server web-1 192.168.10.12:80 weight 1 check send-proxy
至於 Apache 的部分,因為 Apache 目前尚未內建 Proxy Protocol 的支援,所以我們需要安裝另外的模組。在此,我以 myfixip 模組為例,相關步驟如下:
yum install git gcc httpd-devel
cd /tmp
git clone https://github.com/ggrandes/apache24-modules.git
cd apache24-modules
apxs –i –c mod_myfixip.c
新增檔案 /etc/httpd/conf.modules.d/00-myfixip.conf,內容如下
LoadModule myfixip_module modules/mod_myfixip.so
<IfModule mod_myfixip.c>
   RewriteIPResetHeader off
   RewriteIPAllow 192.168.10.0/24 127.0.0.1
</IfModule>
systemctl restart httpd
需特別注意的是因為 Proxy Protocol 會改變代理伺服器與 Web 服務器之間的溝通方式,所以需要兩邊同時啟用,否則將無法正常運作,甚至可能導致無法存取服務。
當用戶端再次透過代理伺服器 (192.168.10.11) 連往 Web 服務器 (192.168.10.12) 上的 PHP 腳本後,可以看到如下的輸出:
X-Forwarded-For:
Remote Address: 192.168.10.10
在這種情況下,即使經過代理伺服器的作用,但是 REMOTE_ADDR 變數依舊可以正確地顯示出用戶端的 IP 位址。

透過 myfixip 模組,可以讓 Apache 支援 Proxy Protocol,有效地取得用戶端 IP 位址。
除了 HAProxy 之外,如果你使用了 AWS ELB 的 TCP 或 SSL 模式,透過 myfixip 擴充 Apache 使其支援 Proxy Protocol 也同樣會有很大的助益。比較麻煩的是,目前 AWS 的管理介面並不支援 Proxy Protocol 的設定,而必須使用命令列模式控制 Proxy Protocol 的開啟或關閉。有需要的讀者可參考官方文件

2014年1月10日 星期五

[工具介紹] Apache 模組 mod_security - 以阻擋造訪路徑攻擊為例

前一篇文章中,我提到網站應用程式的安全問題,光靠定期更新並不能有效加以解決。透過 Secure SDLC 與 Secure Coding 的施作,才能有效提昇系統的整體安全性。而在前兩篇文章中,我提到了這幾天遠通電信網站被發現的一個安全漏洞 - 造訪路徑攻擊,也提到了一些防護手法。
但是回到現實面,我們知道很多時候該做的事情就是沒辦法進行,所以文章中提到的防護手法不一定能夠順利實施。原因有很多,不過歸到底通常都出在人身上。正所謂樹多有枯枝,人多有白痴。那怕只是一個程式設計師的一時鬼遮眼,都有可能讓系統產生莫大的安全漏洞。針對這種狀況,通常建議的作法就是採用 Virtual Patching,白話一點來說就是導入 WAF (Web Application Firewall)。不過再次回到現實面的現實面,導入 WAF 的金錢成本相當高,通常沒有準備七位數是沒有廠商會理你的。
好消息是沒錢也有沒錢的作法,其中 Apache 的 mod_security 模組正是一個免費的好用工具。在這次的文章中,我將示範如何利用 mod_security 來阻擋造訪路徑攻擊,環境為 CentOS 6。在進行前必須提醒讀者,因為示範過程將會改變網站的存取行為,因此請不要直接在正式的環境模擬
為了完成這次的範例,我先在網站上放置了一個 PHP 程式 – get_file_content.php,其內容為
<?php
  $content = file_get_contents($_GET['path']);
  echo nl2br($content);
?>
這個程式的功能很簡單,就是讓使用者透過 path 變數指定某個檔案名稱,然後程式就會將檔案內容完整顯示出來。所以當我們呼叫 get_file_content.php?path=../../../../../../etc/passwd 時,應該會看到這個熟悉的畫面
image
接下來,就讓我們一步步完成今天的範例吧。
  • 安裝 EPEL Repository
    EPEL 提供了 mod_security 的 RPM 套件,可以大幅簡化安裝與日後更新的動作,所以我選擇透過 EPEL 來安裝 mod_security。請先在 https://fedoraproject.org/wiki/EPEL 下載並安裝適合的版本。
  • 安裝 mod_security
    指令是
    yum -y install mod_security
  • 新增造訪路徑攻擊字串的規則
    mod_security 的規則設定檔放在目錄 /etc/httpd/modsecurity.d/activated_rules 內,檔案名稱必須以 .conf 結尾。請在這個目錄產生一個稱為 testing.conf 的檔案,並在檔案內加入下列內容
    SecRule REQUEST_URI "../" "phase:1,t:urlDecode,log,deny,status:503,id:1000"
  • 重新讀取 apache 設定檔
    指令是
    service httpd reload
  • 重新載入網址
    此時畫面應該會出現錯誤訊息 (Code=503)
    image
  • 查看阻擋記錄
    我們可以在日誌檔 /var/log/httpd/modsec_audit.log 看到類似下列的訊息
    --48927b7c-A--
    [10/Jan/2014:01:24:19 +0800] Us7bQ8CoAHcAAEwAH-AAAAAB 192.168.10.11 51517 192.168.10.10 80
    --48927b7c-B--
    GET /get_file_content.php?path=../../../../../../etc/passwd HTTP/1.1
    Host: 192.168.10.10
    Connection: keep-alive
    Cache-Control: max-age=0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36 OPR/18.0.1284.68
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
    --48927b7c-F--
    HTTP/1.1 503 Service Temporarily Unavailable
    Content-Length: 402
    Connection: close
    Content-Type: text/html; charset=iso-8859-1
    
    --48927b7c-E--
    
    --48927b7c-H--
    Message: Access denied with code 503 (phase 1). Pattern match "../" at REQUEST_URI. [file "/etc/httpd/modsecurity.d/activated_rules/testing.conf"] [line "1"] [id "1000"]
    Action: Intercepted (phase 1)
    Stopwatch: 1389288259978368 472 (- - -)
    Stopwatch2: 1389288259978368 472; combined=48, p1=43, p2=0, p3=0, p4=0, p5=5, sr=0, sw=0, l=0, gc=0
    Response-Body-Transformed: Dechunked
    Producer: ModSecurity for Apache/2.7.3 (http://www.modsecurity.org/).
    Server: Apache/2.2.15 (CentOS)
    Engine-Mode: "ENABLED"
    
    --48927b7c-Z--
  • 下載 OWASP 規則
    透過一個簡單的規則,我們就可以避免造訪路徑攻擊。儘管如此,這個規則其實功能有限,因為它只檢查了 Request 字串,萬一是透過 POST (也就是 Content Body) 傳遞攻擊字串,那可就一點也派不上用場了。
    撰寫規則這種事,最好還是讓給專業的來。目前 mod_security 提供兩組規則,其中一組要錢,另外一組由 OWASP 所維護的規則則免費使用。在此我們選用 OWASP 的免費規則,指令如下
    cd /etc/httpd/modsecurity.d
    rm –f activated_rules/testing.conf
    wget https://github.com/SpiderLabs/owasp-modsecurity-crs/tarball/master -O owasp-modsecurity-crs.master.tgz
    tar xvfz owasp-modsecurity-crs.master.tgz
  • 安裝所需規則
    我們下載的檔案內包含許多規則,一般而言,我們不會啟用全部的規則。在此範例中,我們僅會啟用示範所需的規則。指令如下
    cd activated_rules/
    cp ../SpiderLabs-owasp-modsecurity-crs-f4d33c4/modsecurity_crs_10_setup.conf.example modsecurity_crs_10_setup.conf
    ln –s ../SpiderLabs-owasp-modsecurity-crs-f4d33c4/base_rules/modsecurity_crs_42_tight_security.conf modsecurity_crs_42_tight_security.conf
  • 重新讀取 apache 設定檔
    指令是
    service httpd reload
  • 重新載入網址
    此時畫面應該會出現錯誤訊息 (Code=403)
    image
    雖然錯誤訊息有所不同,但是一樣達到阻擋造訪路徑攻擊的目的。
  • 新增其他規則
    等等,萬一駭客輸入的攻擊字串不包含 ../,而是直接輸入 /etc/passwd 呢?令人驚訝的是,呼叫 get_file_content.php?path=/etc/passwd 依舊會直接顯示出檔案內容。因為 /etc/passwd 實在太過重要,所以 OWASP 提供了其他的規則以避免這類攻擊字串,指令如下
    ln –s ../SpiderLabs-owasp-modsecurity-crs-f4d33c4/base_rules/modsecurity_40_generic_attacks.data modsecurity_40_generic_attacks.data
    ln –s ../SpiderLabs-owasp-modsecurity-crs-f4d33c4/base_rules/modsecurity_crs_40_generic_attacks.conf modsecurity_crs_40_generic_attacks.conf
  • 再次重新讀取 apache 設定檔
    指令是
    service httpd reload
  • 再次測試
    此時呼叫 get_file_content.php?path=/etc/passwd 應該也會看到錯誤訊息了
    image
透過 mod_security 與 OWASP 提供的規則,我們就可以輕鬆避免遭受造訪路徑的攻擊。好啦,輕鬆兩字是騙人的,儘管 mod_security 解決了金錢成本的問題,但是導入 WAF 時所可能遭遇的其他問題,還是需要好好處理才能真正發揮 mod_security 的效用

2013年12月25日 星期三

[工具介紹] Apache 模組 mod_log_forensic

Forensic-Science-S_2132330b雖然這幾年 GA (Google Analytics) 等網站流量分析機制逐漸取代了傳統上利用網站日誌來分析網站流量的作法,但是分析網站日誌依舊是系統/網站管理員不可忽視的一個重要工作。以一般的網站服務器而言,主要會提供兩種日誌,一種是存取日誌 (Access Log),另外一種是錯誤日誌 (Error Log)。除了分析網站流量,系統/網站管理員還可以透過分析網站日誌來了解是否發生過攻擊網站的行為 (像是SQL 注入攻擊) 。儘管這些日誌有很大的用處,但是因為這些日誌一直以來都是網站流量分析工具所依賴的資料來源,所以其能儲存的格式與資料也就不太容易改變,也就是說不能應付其他較為特殊的應用。
Apache HTTP 服務器除了上述兩種日誌,另外還提供了一種稱之為 Forensic 的日誌,只是在預設的情況下這個功能是被關閉的。跟原本的日誌相比, Forensic 日誌有下列特點:
  • 每一個請求 (Request) 會有兩筆記錄,一筆是在開始處理請求前,另外一筆則是請求處理結束後。此一特點可以用來分析是否有不正常中斷的請求,這類不正常的請求可能是因為程式或設定的錯誤,也有可能是因為遭受到有心份子的攻擊。如果沒有開啟 Forensic 日誌,這類行為往往僅會在錯誤日誌留下類似 Segement Fault 的訊息,對於釐清問題並沒有太大的幫助
  • Forensic 日誌的格式是固定的,不像存取日誌或錯誤日記可以自定需要記錄的格式與欄位。
  • Forensic 記錄的內容比原本的存取日誌還要詳細,包含標頭的內容也會一併予以記錄,其中甚至包含 Cookie 等可能包含機密資料的標頭。因此對於 Forensic 日誌檔必須予以嚴加保護,以避免有心份子取得不適當的資料。
接下來,我們先看一下在 CentOS 6 的環境下,如何開啟 Forensic 日誌的功能:
  1. 修改 Apache 設定檔,預設為 /etc/httpd/conf/httpd.conf
    載入 mod_log_forensic 模組,也就是將
    #LoadModule log_forensic_module modules/mod_log_forensic.so
    這行改成
    LoadModule log_forensic_module modules/mod_log_forensic.so      
    並設定 Forensic 日誌檔所在位置
    加上下列設定
    ForensicLog logs/forensic_log
    其中 logs/forensic_log 表示儲存的檔案路徑。
  2. 重新載入 Apache 的設定
    指令是
    service httpd reload
設定完成後,我們可以試著讀取網站上的網頁,如果一切正確的話應該可以在 logs/forensic_log 這個檔案內看到類似下列的資訊:
+24178:52ba84d3:0|GET /wordpress/ HTTP/1.1|Host:192.168.0.119|Connection:keep-alive|Cache-Control:max-age=0|Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8|User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36|Accept-Encoding:gzip,deflate,sdch|Accept-Language:zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4|Cookie:wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_fc7f48cee0e7fa37753a2c3c863a3dce=superhero%257C1388125383%257Cdfb3d0e44bc16b8e120138195b9aa229; wp-settings-time-1=1387952591
+24179:52ba84d3:0|GET /wordpress/wp-includes/css/admin-bar.min.css?ver=3.7.1 HTTP/1.1|Host:192.168.0.119|Connection:keep-alive|Cache-Control:max-age=0|Accept:text/css,*/*;q=0.1|If-None-Match:"30e3-3220-4e95d2605fe80"|If-Modified-Since:Tue, 22 Oct 2013 23%3a56%3a26 GMT|User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36|Referer:http%3a//192.168.0.119/wordpress/|Accept-Encoding:gzip,deflate,sdch|Accept-Language:zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4|Cookie:wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_fc7f48cee0e7fa37753a2c3c863a3dce=superhero%257C1388125383%257Cdfb3d0e44bc16b8e120138195b9aa229; wp-settings-time-1=1387952591
-24179:52ba84d3:0
-24178:52ba84d3:0
我們可以發現每一行記錄的開頭都是一串數字,這是用來識別每一個請求的代號。而每一個代號應該會有兩筆記錄,前面分別加上符號 + 或 –。+ 號表示請求開始處理前的紀錄,而 - 號表示請求處理結束後的紀錄如果我們發現特定的請求代號只有 + 號的紀錄,而沒有 - 號的記錄,表示此一請求為不正常的中斷。此時,我們就可以透過日誌內的完整資訊來分析此一請求的各項資訊。
雖然找出不正常中斷請求的原理很簡單,但是如果要靠人工來找出 Forensic 日誌檔中有問題的紀錄,實在是一個不可能的任務。好在 Apache HTTP 服務器提供了一個簡單的腳本 check_forensic,可以用來幫助我們找出 Forensic 日誌檔中有問題的紀錄。不過在 CentOS 6 Apache HTTP 服務器的 RPM 安裝檔當中,並沒有包含此一腳本,所以我們需要自行下載 tarball 並取得當中的腳本 check_forensic 腳本。如果 Forensic 日誌檔沒有任何錯誤,執行腳本 check_forensic 並不會顯現任何資訊。反之,如果有任何不正常中斷的請求,check_forensic 將會列出有問題的紀錄,類似下列結果:
[root@web httpd]# ./check_forensic forensic_log
+24177:52ba84d3:0|GET /wordpress/wp-content/themes/twentythirteen/fonts/genericons.css?ver=2.09 HTTP/1.1|Host:192.168.0.119|Connection:keep-alive|Cache-Control:max-age=0|Accept:text/css,*/*;q=0.1|If-None-Match:"3087-57d7-4e001b3bdeb80"|If-Modified-Since:Tue, 25 Jun 2013 22%3a03%3a42 GMT|User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36|Referer:http%3a//192.168.0.119/wordpress/|Accept-Encoding:gzip,deflate,sdch|Accept-Language:zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4|Cookie:wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_fc7f48cee0e7fa37753a2c3c863a3dce=superhero%257C1388125383%257Cdfb3d0e44bc16b8e120138195b9aa229; wp-settings-time-1=1387952591
[root@web httpd]#
Apache 的 Forensic 日誌檔提供系統/網站管理員有別於標準存取與錯誤日誌的資訊,不管在面對有心或是無意的異常存取行為,都是一項有力的工具。

2013年9月23日 星期一

[工具介紹] 利用 knockd 實現複雜的敲門機制

funny-bear-knocking-door去年我介紹過如何利用 iptables 的 recent 模組來保護如 ssh 之類的網路服務。今天我要介紹的這個服務 – knockd,也可以達成類似的目的。不過不同於 iptables 的作法,knockd 可以輕鬆支援用多個網路埠的組合當做敲門的鑰匙,而且這些網路埠並不會對敲門動作的封包有所回應,可以達到更高的隱密性。此外,如果要保護的服務一多,將保護機制獨立在 iptables 的規則設定之外,也可以簡化 iptables 的複雜度,以避免設定錯誤的情況發生。
在今天的範例中,我們需要兩台 Linux 主機,其中一台當做利用 knockd 機制保護 ssh 服務的主機 (名稱為 knockd.cyril.idv,IP 位址為 192.168.199.103),另外一台用戶端電腦則是用來測試是否可以正常連上受保護的 ssh 服務 (名稱為 client.cyril.idv,IP位址為 192.168.199.102)。採用的 Linux 版本為 32 位元的 CentOS 6.4。為了測試方便,我們在兩台電腦的 /etc/hosts 各加上下列設定:
192.168.199.102 client.cyril.idv client
192.168.199.103 knockd.cyril.idv knockd
接下來,我們就一步步完成今天的範例。

第一階段 安裝與設定 knockd 服務
此階段的操作皆在 ssh 服務主機 (knockd.cyril.idv) 上進行。
  1. 加入對應的 repo
    新增檔案 /etc/yum.repos.d/nux-misc.repo 並加入下列內容
    [nux-misc]
    name=Nux Misc
    baseurl=http://li.nux.ro/download/nux/misc/el6/i386/
    enabled=0
    gpgcheck=1
    gpgkey=http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
  2. 安裝 knock 服務
    指令是
    [root@knockd ~]# yum --enablerepo=nux-misc install knock-server –y
  3. 將目前執行中的防火牆規則導入至檔案
    指令是
    [root@knockd ~]# iptables-save > /tmp/iptables.sav
  4. 修改防火牆規則檔案
    直接修改上述檔案,將內容改為
    # Generated by iptables-save v1.4.7 on Mon Aug 17 21:32:51 2012
    *filter
    :INPUT DROP [0:0]
    :FORWARD ACCEPT [0:0]
    :OUTPUT ACCEPT [149:22436]
    -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
    -A INPUT -p icmp -j ACCEPT
    -A INPUT -i lo -j ACCEPT
    -A FORWARD -j REJECT --reject-with icmp-host-prohibited
    COMMIT
    # Completed on Mon Dec 17 21:32:51 2012
    除了將 INPUT 的預設動作由 ACCEPT 改為 DROP 之外,也移除掉原本 ssh 服務 (TCP Port 22) 的開放規則。
  5. 匯入新的防火牆規則
    指令是
    [root@knockd ~]# iptables-restore < /tmp/iptables.sav
  6. 移除防火牆規則檔案
    指令是
    [root@knockd ~]# rm –f /tmp/iptables.sav
  7. 確認目前的防火牆規則
    指令是
    [root@knockd ~]# iptables-save
    應可以看到類似下列的訊息:
    # Generated by iptables-save v1.4.7 on Mon Aug 17 21:42:57 2012
    *filter
    :INPUT DROP [17:1672]
    :FORWARD ACCEPT [0:0]
    :OUTPUT ACCEPT [149:22436]
    -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
    -A INPUT -p icmp -j ACCEPT
    -A INPUT -i lo -j ACCEPT
    -A FORWARD -j REJECT --reject-with icmp-host-prohibited
    COMMIT
    # Completed on Mon Dec 17 21:42:57 2012
  8. 將目前的防火牆規則寫入系統設定
    指令是
    [root@knockd ~]# service iptables save
  9. 設定 knockd
    knock 服務預設的設定檔是 /etc/knockd.conf,修改成如下:
    [options]
            UseSyslog
    
    [opencloseSSH]
            sequence      = 4567:tcp,5678:tcp,6789:tcp
            seq_timeout   = 15
            tcpflags      = syn
            start_command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport ssh -j ACCEPT      
            cmd_timeout   = 10
            stop_command  = /sbin/iptables -D INPUT -s %IP% -p tcp --dport ssh -j ACCEPT
  10. 設定 knock 服務自動啟動
    指令是
    [root@knockd ~]# chkconfig knockd on
  11. 啟動 knock 服務
    指令是
    [root@knockd ~]# service knockd start

第二階段 安裝 knock 用戶端程式
雖然 knock 使用的敲門技術可以透過許多工具來產生所需的敲門封包,但是使用 knock 內建的工具可以簡化指令的操作。不過如果要進行比較複雜的敲門動作 (也就是送出比較複雜的封包),那就還是必須借助更複雜的封包產生工具了。
此階段的操作皆在 ssh 用戶端電腦 (client.cyril.idv) 上進行。
  1. 加入對應的 repo
    新增檔案 /etc/yum.repos.d/nux-misc.repo 並加入下列內容
    [nux-misc]
    name=Nux Misc
    baseurl=http://li.nux.ro/download/nux/misc/el6/i386/
    enabled=0
    gpgcheck=1
    gpgkey=http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
  2. 安裝 knock 用戶端工具
    指令是
    [root@client ~]# yum install --enablerepo=nux-misc knock –y

第三階段 測試 knock 機制
  1. 直接嘗試連結 ssh 服務
    指令是
    [root@client ~]# ssh knockd
    我們發現指令一直沒有回應,必須透過 Ctrl-C 才能跳脫。
  2. 使用敲門機制
    指令是
    [root@client ~]# knock knockd 4567:tcp 5678:tcp 6789:tcp && ssh knockd
    此時我們應該可以透過 ssh 服務正常連結至主機 knockd (192.168.199.103),並看到類似下列訊息:
    Last login: Mon Aug 17 21:40:42 2012 from client.cyril.idv
    [root@knockd ~]#
  3. 檢查 knock 服務的日誌
    knock 服務預設會將日誌寫入 /var/log/secure 這個檔案中,在 ssh 服務主機 (192.168.199.103) 我們可以看到類似下列的訊息
    Aug 17 21:45:31 knockd knockd: waiting for child processes...
    Aug 17 21:45:31 knockd knockd: shutting down
    Aug 17 21:45:31 knockd knockd: starting up, listening on eth0
    Aug 17 21:45:42 knockd knockd: 192.168.199.102: opencloseSSH: Stage 1
    Aug 17 21:45:42 knockd knockd: 192.168.199.102: opencloseSSH: Stage 2
    Aug 17 21:45:42 knockd knockd: 192.168.199.102: opencloseSSH: Stage 3
    Aug 17 21:45:42 knockd knockd: 192.168.199.102: opencloseSSH: OPEN SESAME
    Aug 17 21:45:42 knockd knockd: opencloseSSH: running command: /sbin/iptables -A INPUT -s 192.168.199.102 -p tcp --dport ssh -j ACCEPT
    Aug 17 21:45:42 knockd sshd[2472]: Accepted publickey for root from 192.168.199.102 port 52920 ssh2
    Aug 17 21:45:42 knockd sshd[2472]: pam_unix(sshd:session): session opened for user root by (uid=0)
    Aug 17 21:45:52 knockd knockd: 192.168.199.102: opencloseSSH: command timeout
    Aug 17 21:45:52 knockd knockd: opencloseSSH: running command: /sbin/iptables -D INPUT -s 192.168.199.102 -p tcp --dport ssh -j ACCEPT
透過 knock 所提供的敲門機制,我們可以建立起複雜的敲門機制。以上述的例子而言,我們必須依序連接 3 個 TCP 埠,才能順利連結 ssh 服務。除了 TCP 埠,我們也可以選擇 UDP 埠。而因為對 knock 機制而言,其實只是在接受到敲門訊息後呼叫特定的指令,所以除了 iptables 的指令外,也可以用來呼叫其他指令,以達成不同的管理目的。

2013年9月17日 星期二

[工具介紹] 利用 glusterfs 達成目錄即時自動同步與高可用性

glusterfs-logo去年我介紹過利用 lsyncd 這個套件來達成目錄的同步備援,今天則要利用 glusterfs 來達成類似的效果。雖說類似,但是 lsyncd 與 glusterfs 相比有下列差別:
  • lsyncd 監測目錄的變動,並定期 (或達到一定的異動次數) 將異動同步到另外一個目錄。因為 lsyncd 採用 inotify 監測技術,所以如果目錄內的檔案或子目錄一多,就可能會產生無法監測的問題。
  • lsyncd 本身並沒有支援高可用性,必須利用其它機制 (如 heartbeat) 來達成高可用性。
  • 被同步的目錄通常僅能當做備援,無法作為負載平衡之用。不但在資源使用上較為浪費,擴充性也比較差。
嚴格來說,拿 lsyncd 與 glusterfs 相比並不是一件公平且合理的事情,因為 glusterfs 本身是一個完整的分散式檔案系統,所以在功能上本就遠比 lsyncd 這類工具來的強大許多。用最簡單的說法,glusterfs 可以將許多不同的儲存空間整合在一起,變成一個分散式的虛擬儲存空間。glusterfs 所管理的虛擬儲存空間除了可以具備分散的特性 (distributed, 也就是將不同的檔案存放在不同的儲存空間) 之外,glusterfs 還提供了複製 (replicated, 也就是同一個檔案存放在兩個以上的儲存空間) 以及分條 (stripped, 也就是將一個檔案打散在多個不同的儲存空間) 的選項。這些選項可以單獨使用,也可以一起套用。今天我要跟大家分享的就是透過複製特性來達成目錄的自動同步與高可用性。
範例需要三台 Linux 系統,其中兩台作為 glusterfs 的服務端,另外一台則作為 glusterfs 的用戶端,所使用的環境皆為 CentOS 6.4。相關架構如下:
glusterfs
為了方便起見,我們讓這三台 Linux 之間可以透過主機名稱進行連結,所以我們在 /etc/hosts 設定如下:
192.168.199.100 server1.cyril.idv server1
192.168.199.101 server2.cyril.idv server2
192.168.199.102 client.cyril.idv client
接下來就讓我們一起一步步完成今天的範例吧。

第一階段:glusterfs 服務端的基本安裝
此階段的步驟必須在 server1 與 server2 上皆予以執行。
  1. 安裝 glusterfs 的 repo
    指令為
    wget http://download.gluster.org/pub/gluster/glusterfs/3.3/LATEST/EPEL.repo/glusterfs-epel.repo -O /etc/yum.repos.d/glusterfs-epel.repo
    註1:雖然目前 glusterfs 的最新版本為 3.4,但是 GA 版本仍為 3.3,所以在這個範例中我選擇使用 3.3 的 repo。
    如果系統已經安裝 EPEL 的 repo,裡面就包含了 glusterfs 3.2 的相關 RPM 套件。但仍建議停用 EPEL 內的 glusterfs 套件,而改採用上述的套件。
  2. 安裝 glusterfs 的服務器套件
    指令為
    yum install glusterfs-server -y
  3. 設定 glusterfs 服務開機後自動啟動
    指令為
    chkconfig glusterd on
  4. 啟動 glusterfs 服務
    指令為
    service glusterd start
  5. 修改防火牆設定
    在 glusterfs 官方文件中提到應該開啟 TCP 111, 24007, 24008, 24009 以及之後的數個埠號,至於要開到幾個埠號,則跟 brick 數量有關。以這個例子而言,應該開啟 TCP 111, 24007, 24008, 24009, 24010, 24011 等埠號。
    修改完後記得確認防火牆規則是否已經生效。指令為
    iptables –L -n
    執行後應可以看到類似下列資訊
    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination
    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED
    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0      
    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:22
    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:111
    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:24007
    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:24008
    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:24009
    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:24010
    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:24011
    REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-host-prohibited
    Chain FORWARD (policy ACCEPT)
    target     prot opt ource               destination
    REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-host-prohibited
    Chain OUTPUT (policy ACCEPT)     
    target     prot opt source               destination

第二階段:glusterfs 服務端的服務設定
此階段的指令僅需在 server1 予以執行即可。
  1. 將 server2 加入可信任的儲存池 (Trusted Stroage Pool)
    指令為
    [root@server1 ~]gluster peer probe server2.cyril.idv
    執行後應可看到下列訊息
    Probe successful
  2. 確認信任關係
    指令為
    [root@server1 ~]gluster peer status
    執行後應可看到類似下面的訊息
    Number of Peers: 1
    
    Hostname: server2.cyril.idv
    Uuid: f44e01c6-e889-432a-8056-cf7174421324
    State: Peer in Cluster (Connected)
  3. 建立 Volume
    在 glusterfs 的架構中,每一個 volume 就代表了單獨的虛擬檔案系統。建立 volume 的指令為
    [root@server1 ~]gluster volume create datavol replica 2 transport tcp server1.cyril.idv:/data server2.cyril.idv:/data
    執行後應可看到下列訊息
    Creation of volume datavol has been successful. Please start the volume to access data.
    註2:glusterfs 會自動建立不存在的 /data 目錄。此外,在建立 volume 時,我們不需要特別指定虛擬檔案系統的屬性 (像是分散與否),glusterfs 會自動根據相關參數 (如 brick 數) 決定此一虛擬檔案系統的屬性。
  4. 啟動 Volume
    指令為
    [root@server1 ~]gluster volume start datavol
    執行後應可看到下列訊息
    Starting volume datavol has been successful
  5. 確認服務之間的連線已經正常連結
    指令為
    [root@server1 ~]netstat –tap | grep glusterfsd
    執行後應可看到類似下列的訊息
    tcp        0      0 *:24009                     *:*                         LISTEN      2343/glusterfsd
    tcp        0      0 server1.cyril.idv:24009     server2.cyril.idv:exp2      ESTABLISHED 2343/glusterfsd
    tcp        0      0 server1.cyril.idv:24009     server1.cyril.idv:1020      ESTABLISHED 2343/glusterfsd
    tcp        0      0 server1.cyril.idv:24009     server2.cyril.idv:1020      ESTABLISHED 2343/glusterfsd
    tcp        0      0 server1.cyril.idv:24009     server1.cyril.idv:1023      ESTABLISHED 2343/glusterfsd
    tcp        0      0 localhost:1020              localhost:24007             ESTABLISHED 2343/glusterfsd
  6. 查詢 volume 的狀態
    指令為
    [root@server1 ~]gluster volume info datavol
    執行後應可看到類似下列訊息
    
    Volume Name: datavol
    Type: Replicate
    Volume ID: 185ef7c6-7c7f-461d-8690-eec24a1a4c38
    Status: Started
    Number of Bricks: 1 x 2 = 2
    Transport-type: tcp
    Bricks:
    Brick1: server1.cyril.idv:/data
    Brick2: server2.cyril.idv:/data
    其中 Type 資訊表示這是一個複製的 (Replicate) 虛擬檔案系統。
  7. 為了增加安全性,我們可以限制只有 client 電腦的 IP 才能連上此一虛擬檔案系統
    指令為
    [root@server1 ~]gluster volume set datavol auth.allow 192.168.199.102
    執行後應可看到下列訊息
    Set volume successful
  8. 再次確認 volume 的狀態
    指令同樣為
    [root@server1 ~]gluster volume info datavol
    執行後應可看到類似下列訊息
    
    Volume Name: datavol
    Type: Replicate
    Volume ID: 185ef7c6-7c7f-461d-8690-eec24a1a4c38
    Status: Started
    Number of Bricks: 1 x 2 = 2
    Transport-type: tcp
    Bricks:
    Brick1: server1.cyril.idv:/data
    Brick2: server2.cyril.idv:/data
    Options Reconfigured:
    auth.allow: 192.168.199.102

第三階段:glusterfs 用戶端的安裝
  1. 安裝 glusterfs 的 repo
    指令為
    [root@client ~]wget http://download.gluster.org/pub/gluster/glusterfs/3.3/LATEST/EPEL.repo/glusterfs-epel.repo -O /etc/yum.repos.d/glusterfs-epel.repo
  2. 安裝 glusterfs 用戶端的套件
    指令為
    [root@client ~]yum install glusterfs-client -y
  3. 建立掛載點
    指令為
    [root@client ~]mkdir /mnt/glusterfs/data –p
  4. 掛載虛擬檔案系統
    指令為
    [root@client ~]mount.glusterfs server1.cyril.idv:/datavol /mnt/glusterfs/data
  5. 確認掛載的結果
    指令為
    [root@client ~]mount –t fuse.glusterfs
    執行後應可看到類似下列訊息
    server1.cyril.idv:/datavol on /mnt/glusterfs/data type fuse.glusterfs (rw,default_permissions,allow_other,max_read=131072)
  6. 設定開機自動掛載
    修改 /etc/fstab,加上下列設定
    server1.cyril.idv:/datavol /mnt/glusterfs/data glusterfs defaults 0 0
  7. 重新開機
    指令為
    [root@client ~]sync; shutdown –r now
  8. 確認開機自動掛載是否成功
    指令同樣為
    [root@client ~]mount –t fuse.gluserfs
    執行後應可看到類似下列訊息
    server1.cyril.idv:/datavol on /mnt/glusterfs/data type fuse.glusterfs (rw,default_permissions,allow_other,max_read=131072)
  9. 測試檔案的操作
    指令為
    [root@client ~]echo "Hello GlusterFS" >> /mnt/glusterfs/data/test0
  10. 確認檔案的同步複製
    我們可以同時在 server1 與  server2 的 /data 下看到 test0 這個檔案,而且內容皆為 "Hello GlusterFS"。
至此,我們已經完成利用 glusterfs 建立目錄即時同步的架構。接下來我們將透過更多的例子,看看此一架構可以提供何種程度的高可用性。
註3:如果我們直接在 server1 或 server2 上針對檔案系統 (也就是 /data) 進行異動的操作 (如新增檔案),將無法對達到同步的效果,甚至連用戶端也無法接受到這些異動。

第四階段:高可用性測試
  1. 關閉 server1
    指令為
    [root@server1 ~]sync;shutdown –h now
  2. 修改檔案內容
    指令為
    [root@client ~]echo "Hello GlusterFS Replication" >> /mnt/glusterfs/data/test0
    雖然 server1 已經關機,但是我們依舊可以對原本掛載的目錄進行讀寫,其讀寫的目的地會自動變成 server2。
  3. 確認 server2 上檔案 test0 的內容
    指令為
    [root@server2 ~]# cat /data/test0
    執行後應可看到下列訊息
    Hello GlusterFS
    Hello GlusterFS Replication
  4. 開啟 server1
  5. 確認 server1 上檔案 test0 的內容
    指令為
    [root@server1 ~]# cat /data/test0
    執行後應可看到下列訊息
    Hello GlusterFS
    Hello GlusterFS Replication
    註4:glusterfs 具備自動修復 (heal) 的功能,如果目錄下的資料較多,自動修復所需的時間也會跟著增長。在自動修復期間雖然服務端的檔案可能會有不一致的現象發生,但是 glusterfs 會確保用戶端只會看到最新的檔案。
  6. 再次關閉 server1
    指令同樣為
    [root@server1 ~]sync;shutdown –h now
  7. 卸載用戶端上的目錄掛載
    指令為
    [root@client ~]umount /mnt/glusterfs/data
  8. 再次掛載目錄
    指令為
    [root@client ~]mount -a
    執行後應可看到下列訊息
    Mount failed. Please check the log file for more details.      
    註5:掛載失敗的原因在於 server1 已經關機,所以用戶端自然無法進行連結。雖然 glusterfs 在掛載後的讀寫具備自動切換的能力,但是掛載時的動作卻無法自動切換。
  9. 修改掛載的參數,使其支援備用掛載主機
    修改 /etc/fstab,將原本
    server1.cyril.idv:/datavol /mnt/glusterfs/data glusterfs defaults 0 0
    修改為
    server1.cyril.idv:/datavol /mnt/glusterfs/data glusterfs defaults,backupvolfile-server=server2.cyril.idv 0 0
  10. 再次掛載目錄
    指令為
    [root@client ~]mount –a
  11. 確認掛載結果
    指令為
    [root@client ~]mount –t fuse.glusterfs      
    執行後應可看到下列訊息
    server2.cyril.idv:/datavol on /mnt/glusterfs/data type fuse.glusterfs (rw,default_permissions,allow_other,max_read=131072)
    註6:在此我們可以看到掛載主機已經從 server1 改為 server2了。透過此一參數,可以避免 glusterfs 在掛載目錄時因為單一主機失效而掛載失敗。
透過 glusterfs,我們可以輕鬆建立一個可自動同步且具備高可用性的目錄。更棒的是,如果未來我們發現這個目錄有空間不足或效能低落的情況發生,只要加上更多的 brick,就可以直接在線上進行擴充的工作,不用再擔心因為資料轉移所造成的過多時間花費與停機時間了。

2012年11月7日 星期三

[工具介紹] 利用 iptables 的敲門機制保護 ssh 服務

DoorKnock之前我介紹過利用 fail2ban 來封鎖嘗試使用暴力破解登入 ssh 的有心份子,但是如果你有嚴重的潔癖,看到滿滿的登入錯誤訊息就渾身不舒服想要更進一步降低此一風險,還有一些措施可以施作,其中最常見的建議就是不要使用預設的埠號 (TCP:22)。改用其他埠號雖然看似很有效,但是一旦遇到埠號掃描的攻擊行為並被有心份子知道實際使用的埠號,其實就跟使用預設埠號沒啥兩樣。如果我們可以隱藏實際使用的埠號,並於特殊情況下才予以現形,那就不用擔心埠號掃描的攻擊方式了。

我們可以利用 iptables 內建的 recent module 來達成此一目標,想要連結 ssh 服務的一方必須先對某個特定的埠號進行"敲門"動作,iptables 才會對此 IP 位址開放 ssh 服務所使用的埠號。以下我們就透過實際的例子來加以說明,實作的環境是 CentOS 6:

  1. 修改 ssh  服務所使用的埠號
    ssh 服務的設定檔為 /etc/ssh/sshd_config,將
    #Port 22
    改為
    Port 2222
    其中 2222 表示我們想要 sshd 服務所使用的埠號。
  2. 重新啟動 ssh 服務
    指令為
    service sshd restart
  3. 匯出 iptables 所使用的設定
    指令為
    iptables-save > /tmp/iptables
    如果你使用其他工具 (如webmin) 來設定 iptables,那麼就不需要進行 iptables 規則的匯入與匯出,可以直接加入所需規則。
  4. 修改 iptables 的規則
    在 /tmp/iptables 適當的位置加入用紅色標示的兩行規則 (請務必加在 default policy 之前):
    -A INPUT -p tcp -m state --state NEW -m tcp --dport 3306 -j ACCEPT      
    -A INPUT -p tcp --dport 54321 -m recent --set --name SSH-PHASE1
    -A INPUT -p tcp --dport 2222 -m recent --rcheck --seconds 5 --name SSH-PHASE1 -j ACCEPT

    -A INPUT -j REJECT --reject-with icmp-host-prohibited
    其中 54321 是我們用來敲門的埠號,而 2222 則是 ssh 服務實際上所使用的埠號。同時記得刪除 ssh 服務的預設規則
    -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
  5. 匯入 iptables 的規則
    指令是
    iptables-restore < /tmp/iptables
  6. 測試新的規則
    從另外一台主機執行下列指令
    telnet ip_address 54321; ssh ip_address –p 2222
    其中 ip_address 請換成前一台主機的 IP 位址。順利的話,應該可以看到 ssh 的登入提示符號。如果我們在 telnet 與 ssh 兩個指令之間停頓超過 5 秒,將無法連結至 ssh 服務,也就是說每次敲門後的開放只保留 5 秒,5 秒過後又自動加以關閉。如以一來,即使利用埠號掃描攻擊也很難發現 ssh 服務真正使用的埠號。
  7. 刪除規則暫存檔
    指令是
    rm –f /tmp/iptables
  8. 將規則寫入 iptables 的啟動設定
    如果測試無誤,我們就可以把規則寫入 iptables 的啟動設定,以確保每次啟動 iptables 時都能套用此一設定。指令是
    service iptables save

透過兩行簡單的iptables 規則設定,我們大幅減少 ssh 服務埠號被偵測出的可能性,對 ssh 服務的保護將更加完善。最後,即使使用了 iptables 的敲門機制來保護 ssh 的服務埠, fail2ban 依舊有其必要性,理由如下:

  • fail2ban 不只可用來保護 ssh 服務,還可以用來保護包含 ftp 與 pop3 在內的各項網路服務。
  • 通常 iptables 規則中總會有些例外 IP 位址可以對系統暢行無阻,雖說這些 IP 位址多為內部使用,但是別忘了內部使用的 IP 位址並不等於絕對安全。
  • 避免日後有人不小心移除了相關設定。相信我,這種糗事發生的機會比你想像中還來的高出許多。
  • 一旦知道這個"敲門"用的埠號後,此一機制同樣可能遭受暴力攻擊。什麼人會知道這個敲門埠號?你猜對了,正是離職員工!

2012年11月6日 星期二

[工具介紹] 利用 lsyncd 達成 Linux 下的目錄"即時"同步

windows-8-transfer7數位資料對一個企業的重要性,我想已經不用我在此多加贅述。而這些數位化的資料,除了存在於資料庫當中,其實還有不少是以檔案形式存在著。對於資料庫,通常我們除了定期備份之外,還會考慮到備援的機制 (如常見的主從式架構),以確保資料庫的高可用性。但是對於檔案呢?往往可能就沒有那麼受到重視了。備份或許有之,但是是否有也良好的備援機制呢?檔案的備援有多種可能性,從分散式儲存機制 (如 DRBD),到分散式檔案系統 (如 GFS2, Hadoop Distributed File System),選擇性可說是五花八門。儘管選擇相當多,但是這些機制通常都必須在建立系統前就一併加以規劃,而且受到支援平台的限制。有些機制甚至在使用上也與傳統的檔案系統不盡相同,再再提高了管理人員與系統開發人員的進入門檻。對於大部分的系統而言,可能原先已經存在許多檔案並存放在"一般"的檔案系統 (如 ext3) 之內,有什麼好的方法可以讓這些系統無痛地達成備援的機制?
對 Linux 系統而言,基本上我們只要達成目錄的同步再加上如 heartbeat 的監測機制,就可以達到檔案的備援機制。而要達到目錄的同步,最簡單也是最常見的一種方式就是透過排程定期呼叫 rsync 將主目錄內的檔案同步至備援主機的目錄。這方法雖然很簡單,也確實可行,但是他有一個主要的問題,那就是當目錄結構一大,比對與同步的時間將會拖的很長。如此一來,表示排程間隔的時間不能太短,也就是兩個目錄所出現的時間差距也會隨之增加。而今天我要介紹的這個工具 – lsyncd,雖然也是透過 rsync 進行同步,但是 lsyncd 透過監測目錄的寫入動作,僅針對變動的部份進行同步,可以大幅提高同步的效率。而 lsyncd 的同步觸發條件預設為間隔 20 秒或累積 1000 次的寫入事件,而且這兩個參數還可以依據實際的狀況加以調整。不可否認,lsyncd + rsync 很難達成真正的即時同步,但是對於大部分的狀況,數秒的差距應該是可以被接受的。而因為 lsyncd 只需安裝在主目錄的系統上,所以對於備援主機的限制較少,甚至是不同的檔案系統都沒關係
在 Linux 下面,通常可以找到 lsyncd 的套件直接加以安裝,只是版本上可能會有所不同。以 CentOS 為例,可以在 EPEL 這個 repository 中找到 lsyncd 的套件,版本為 2.0.4。前面提到,只有主目錄的主機需要安裝 lsyncd,而用來當做備援的主機是不需要安裝的。安裝後,必須自行建立一個設定檔,檔案路徑雖然沒有什麼限制,但是我的建議是使用 /etc/lsyncd.conf 以便管理。在 /etc/lsyncd.conf 檔內輸入下列內容:
settings = {   
   logfile = "/var/log/lsyncd.log",    
   statusFile = "/var/run/lsyncd.status",    
   nodaemon = false,    
   log = all    
}

sync {   
   default.rsync,    
   source = "/data",    
   target = "192.168.1.2:/data",    
   rsyncOps = {"-avz", "--delete"},    
   delay = 10    
}

其中 source 與 target 請分別改成你想要同步的主目錄及備援主機的目錄。設定完畢之後,透過指令lsyncd /etc/lsyncd.conf
就可以啟動 lsyncd 的同步功能了。在使用 lsyncd 時,有下列幾點事情需要注意:
  1. 因為新版的 rsync 已經預設使用 ssh 當做傳輸的方式,所以可以直接設定使用 rsync 即可。如果 rsync 版本過舊,可以將 sync 方式由 rsync 改為 rsyncssh。也就是說,我們必須確保主目錄的主機可以直接透過 ssh 登入備援主機,而不需要輸入密碼。除了利用 rsync + ssh 的方式,也可以透過 rsync 的 target,如此一來就不需要 ssh 了。
  2. lsyncd 預設不會同步檔案的所有權與權限,因此我們透過 rsyncOps 這個參數來改變 lsyncd 呼叫 rsync 的行為。
  3. 透過 delay = 10 這個設定,我們要求 lsyncd 將同步間隔時間由預設的 20 秒改為 10 秒。
  4. 我們必須確保所有的寫入動作發生於主目錄的主機上,而在一般的情況下,這應該是不成問題的。但是如果我們自找麻煩地在主目錄掛載了其他主機的目錄,那麼這些掛載而來目錄的寫入動作,將無法被 lsyncd 監測到,所以也就無法同步
  5. lsyncd 透過 inotify 作為監測目錄寫入的機制,而 inotify 可監測的數量則受到 fs.inotify.max_user_watches 這個參數的限制。如果 lsyncd 欲監測的目錄數量超過系統的上限,並不會出現錯誤訊息,而是多餘的目錄直接被忽略。為了避免此一現象的發生,我們在啟動 lsyncd 後可以先透過指令
    sysctl fs.inotify.max_user_watches
    確認系統的設定值。之後在 /var/run/lsyncd.status 中找到類似下列的訊息
    Inotify watching 34567 directories
    其中 34567 表示 lsyncd 供監測了 34567 個目錄。如果兩個數字很接近,請增加系統所允許的上限值以便讓 lsyncd 完整監測並同步所有的目錄。
    透過下列指令我們可以將 inotify 可監測的數量上限變更為 40000
    echo 40000 > /proc/sys/fs/inotify/max_user_watches
    如果我們希望每次開機後此一設定值都自動變更為 40000,請修改 /etc/sysctl.conf,加上下列設定
    fs.inotify.max_user_watches = 40000
  6. CentOS 所安裝的 lsyncd 並沒有提供啟動腳本,因此請在 /etc/rc.local 加上設定
    /usr/bin/lsyncd /etc/lsyncd.conf
    如此一來,開機時 lsyncd 才會自動執行。
雖然 lsyncd 無法真正達成目錄的"即時"同步,但是在可接受的誤差範圍內,lsyncd 有著安裝簡單與適用範圍廣泛的優點,對於檔案備援來說絕對是一個令人心動的選擇。

2012年9月11日 星期二

[工具介紹] 利用 ossec + ossim 管理端點的安全

之前我介紹過 Splunk ,用來作為日誌集中管理與分析的工具相當好用 (除了免費版本有討厭的 500MB 限制)。但是 Splunk 是一個一般型的分析工具,如果我們只想處理有關資安相關的資訊,不但需要自己設定各項所需的資料來源,更必須自己利用查詢指令找出感興趣的資訊,甚至轉換成所需的報表。事實上,市面上針對資安相關資訊的彙整與處理已經有一類專門的產品,統稱為 SIEM。今天我要跟各位分享兩個工具的整合利用,其中的 ossim,正是 opensource 形式的 SIEM。
在我使用 ossim 之前,原本只是使用 ossec 來做為主機 (端點) 的 IDS 機制 (HIDS)。ossec 同樣是 opensource 形式的軟體,支援多種作業系統,主要提供主機的完整性檢查、日誌檔監測與惡意程式 (Rootkit) 掃描的功能。如果發現有疑似攻擊的行為發生,ossec 還可以做出回應,例如封鎖攻擊來源的 IP 位址。ossec 採用主從式 (Client-Server) 的架構,主要透過命令列方式加以設定。而事件的判斷結果統一集中在 ossec 伺服器 (Server) 上,同樣必須透過指令的方式加以查看。
作為一個專業的管理人員,當然不能接受這麼不方便的事情。好險,有 ossim 的存在。嚴格來說,  ossim 並不是單是  ossec 的圖型化操作介面,更不是日誌管理系統。ossim 整合了許多 opensource 的資安/管理工具,並以 ISO 檔 (或專屬設備) 的形式發佈,讓管理者可以很快建立起一個完整的 SIEM 系統。ossim 整合的工具包含了
  • 流量管理: ntop, netflow
  • 入侵偵測: snort, ossec
  • 弱點掃描: openvas, nessus
  • 可用性: nagios3
  • 變更管理: arpwatch, P0f, Pads
  • 無線網路管理: kismet
除了工具的整合,  ossim 還可以透過 syslog 的機制,分析來自其他設備或軟體的資訊。而付費版本的 ossim,更提供了日誌加密封存的功能,可作為日後訴訟用的佐證。
今天,我要跟各位分享如何利用 ossec + ossim 做出一個完整的主機入侵偵測系統 (HIDS)。前面提到 ossec 採用主從式的架構,所以 ossim 就是 ossec 的伺服器 (Server),而其他我們想要管理的主機就是 ossec 的客戶端 (Client)。我以 CentOS 作為客戶端的範例,至於其他作業系統的客戶端,設定的過程也沒有太大的差異。
  1. 下載 ISO 檔
    下載網址是 http://communities.alienvault.com/community/#downloads 。目前的版本是 4.0,僅提供 64 位元的版本。
  2. 安裝 OSSIM
    OSSIM 以 Debian 為基礎,提供完整的圖型化安裝介面,在此就不一一截圖了。
  3. 連結 OSSIM 的網頁
    連結網址是 https://ossim.ip/ossim ,其中 ossim.ip 請替換成安裝 OSSIM 時所指定的 IP 位址。
  4. 新增管理的網路區段
    點選 "Assets" –> "Assets" –> "Networks" –> "New"。 
    0002
  5. 輸入管理網路區段的資訊
    有兩個主要欄位需要填寫,分別是 Name 與 CIDRs,填寫完畢後按下 "Update" 即完成新增的動作。如果你有多個管理的網路區段,請記得重複此一新增動作。
    請注意到畫面上有一個 "Asset value" 的選項,在 ossim 中,每一個資產 (Asset) 都有其價值,這個價值是用於事件風險的計算公式之中。
    0003-2
  6. 掃描主機
    雖然我們也可以用手動的方式將主機資訊一一加入,但是第一次設定時我還是建議用掃描的方式新增主機,以簡化輸入的過程。
    點選 "Assets" –> "Assets Discovery"。利用右邊的樹狀圖選取想要掃描的網路區段,如有需要可同時選取多個網路區段。
    0003
  7. 開始掃描
    選取完畢並設定好相關參數後,按下 "Start Scan" 開始進行掃描。
    0004
  8. 等待掃描結果
    掃描完畢後, ossim 會列出所有搜尋到的主機及其相關資訊。在想要新增的主機後進行勾選的動作,勾選完畢後按下 "Update database values"。
    0005
  9. 儲存掃描結果
    儲存前可以指定是否要為這些主機建立一個新的群組,或者也可以指定主機的價值 (Asset value)。設定完畢後按下 "Update" 即完成新增的動作。
    0006
  10. 登入 ossim 的系統主控端,進行 ossec 客戶端的管理
    其實管理客戶端的動作大多也可以在 ossim 的管理介面上完成,但是為了展現我們打指令的功力,還是介紹原本 ossec 所使用的方式。
    啟動用戶端管理程式,指令是
    /var/ossec/bin/manage_agents      
    使用選項 A 來新增主機。0007
  11. 輸入客戶端電腦的名稱 (Name) 與 IP 位址 (IP Address)
    名稱主要作為識別之用,並不會影響新增的結果。而 IP 位址必須填入伺服器所 "看到" 的客戶端 IP 位址。除非客戶端會透過轉址 (NAT) 來對伺服器進行連結,否則一般直接填上客戶端的 IP 位址即可。
    設定完畢後進行確認 (y) 即完成新增的動作。 0008
  12. 取得客戶端的 "鑰匙"
    ossec 作為一個安全軟體,當然不可能隨意接收其他電腦傳送過來的訊息。所以每個想要傳送訊息的客戶端,都必須擁有一個獨一無二的鑰匙。在前一個動作完成時,ossec 就幫我們建立好該客戶端的鑰匙了。我們可以透過指令 E 取得客戶端的鑰匙,而客戶代號 009 則為前一個動作進行時系統所自動產生的。第三個紅色框框內的文字就是這個客戶端的鑰匙,請完整複製下來並備用。
    0009
  13. 安裝 ossec (客戶端)
    CentOS 可以從 AtomiCorp 的 repository 中取得 ossec 套件。指令是
    wget -q -O - https://www.atomicorp.com/installers/atomic |sh
    yum install ossec-hids-client ossec-hids
    chkconfig ossec-hids on
  14. 修改設定檔,指定伺服器的 IP 位址 (客戶端)
    指令是
    vi /var/ossec/etc/ossec.conf
    修改檔案開頭的 <server-ip> 參數。
  15. 匯入鑰匙 (客戶端)
    輸入指令
    /var/ossec/bin/maange_client
    使用選項 I 匯入鑰匙,並將剛剛複製的鑰匙文字完整貼上。 確認資料無誤後,輸入 y 完成匯入鑰匙的動作。
    0010
  16. 關閉匯入工具 (客戶端)
    使用選項 Q 。
    0011
  17. 啟動服務 (客戶端)
    指令是
    service ossec-hids start
  18. 確認客戶端已經正常連結
    我們回到 ossim 的管理介面,點選 "Analysis" –> "Detection" –> "HIDS"。如果客戶端狀態出現 "Active",表示客戶端已經正常連結至 ossec 的伺服器。如果無法正常連結,可以檢查 ossec 的紀錄檔,位置是 /var/ossec/logs/ossec.log。
    0012
  19. 其他操作
    一開始我提到原本 ossec 僅提供指令的方式來管理客戶端,而 ossim 則將此功能整合至管理介面當中。例如點選畫面右上方的 "Agents" 連結,就可以看到目前所有的客戶端列表,而且還可以對其進行操作 (如執行完整性檢查) 或是查看之前所產生的相關訊息。透過 ossim ,不但可以簡化 ossec 的管理作業,所有 ossec 所產生的訊息也將被 ossim 統一處理,更加提供我們對於資訊安全的防護能力。
    0013
透過幾個簡單的動作,我們就建立起一個可運作的 HIDS 機制。但是不管採用 opensource 還是付費的軟體,這些動作都只是第一步而已。真正的挑戰在於如何調整出一個濃纖合度的設定,在減少假警報 (False positive) 的同時,也不會漏失真正需要注意的資安事件 (False negative)。如此一來,才能真正發揮 SIEM 的功效,而不是成了一個中看不中用的花瓶。

About