Apache 取得透過 Reverse Proxy (Varnish) 的 Client 真正 IP (mod_rpaf)

feather 之前介紹 [FreeBSD]high performance caching reverse proxy: Varnish (安裝架設篇) 來當 Web 前端 Reverse Proxy,也有 load balance 的功能,不過碰到這樣的環境,後端 Apache Server 只會抓到 Reverse Proxy IP 來當作 log 紀錄,而無法正確取得 Client 端 IP,Varnish 官網 FAQ 有提到 log 檔案得的解決方法,不過在程式方面,要大量的修改,假設今天 Apache 跑10個 Virtual Host ,不就要去改10個網站程式,背後或許是一些大型 open source 的 Project,改起來相當不容易,也很費工夫。Darkhero 提供了 reverse proxy add forward module for Apache (mod_rpaf) 模組,只要裝上這模組,Apache 就不必動到其它設定就可以正確紀錄 log 檔案,且程式都不必修改,就可以得到正確 IP 了。 FreeBSD Ports 安裝方式:
cd /usr/ports/www/mod_rpaf2/
make install clean
修改 httpd.conf (FreeBSD: /usr/local/etc/apache22/httpd.conf)
LoadModule rpaf_module        libexec/apache22/mod_rpaf.so
將上面 unmask,最後面加上:
RPAFenable On
RPAFsethostname On
RPAFproxy_ips xxx.xxx.xxx.xxx 127.0.0.1
RPAFheader X-Forwarded-For

[FreeBSD]high performance caching reverse proxy: Varnish (安裝架設篇)

varnish-logo-red-64 在上禮拜跟 DarkHero 兄聊到 How To Build a Scalable Web Site (3/6) 的上課講義,互相討論了 MySQL Load balance 以及 http reverse proxy 的方式,以前自己有用 HAProxy 當作 Web 平衡負載,順便紀錄了 HAProxy FreeBSD 安裝方式,這次要來介紹今天重點:Varnish Cache Server,在近幾年流行的 Caching 機制,大家會想到 Squid,只要您設定良好的 Squid 參數,它一定運作的非常穩定,然而它的核心依然是 forward proxy,要架設成 Reverse Proxy 還必需要設定一些參數才可以達到,是有一定的困難性,然而 Varnish Cache Server 底層就是高效能 caching reverse proxy,也因為 Squid 是 1980 年發展出來的,程式架構過於老舊,可以參考 ArchitectNotes 瞭解這部份詳情。也許您會問到 Varnish 可以架設成 forward proxy 嗎?答案是可以的,但是您也許不會這麼做,因為它需要 DNS 技術,以及需要一個非常大且複雜的 Varnish VCL(Varnish Configuration Language) file。 1. 今天要介紹如何在 FreeBSD 系統安裝,在介紹之前,系統必須先安裝好 apache,這樣才可以正確啟動,利用 ports 安裝:
cd /usr/ports/www/varnish/
make install clean
2. 修改 /etc/rc.conf
# varnishd
varnishd_enable="YES"
varnishd_listen=":80"
varnishd_admin=":81"
varnishd_backend=":5566"
varnishd_config="/usr/local/etc/varnish/default.vcl"
上面設定意思是說 Varnish listen on port 80,傳送 traffic 到後端 5566 port,管理連接埠是 81,也可以使用指令方式:
varnishd -a :80 -b localhost:8080 -T localhost:6082
Varnishd listen on port 80,and forwarding traffic to a web server listen on localhost port 8080. It also turns on the management interface on port 6082.
3. 修改 default.vcl (Varnish Configuration Language) VCL 檔案告訴 Varnishd 正確的處理每個 request processing,包含在接受到 request 之前所處理的行為 vcl_recv(),另外還有 vcl_hit()、vcl_miss() 等…,都是用來處理 cache 如果存在或者是不存在時的情境 request。FreeBSD 預設放在 /usr/local/etc/varnish/default.vcl。打開此檔案,您會看到:
backend default {
   .host = "127.0.0.1";
   .port = "80";
}
您只要把 host = “127.0.0.1” 改成你後端要連接的 ip 或者是 host,這樣 Varnish 會 forward traffic 到您的 web server。接下來只要啟動 apache 跟 Varnish 就算是初步架設完成。
/usr/local/etc/rc.d/apache22 restart
/usr/local/etc/rc.d/varnishd restart
Varnish_01 大家可以看到 61.*.*.* 連到本機 80 port,接下來 Varnish 在開啟隨機 57475 port 連接到 Web Server 5566 port。 Q:如何讓 apache 紀錄正確的 Client IP 到 log 檔案呢? 1. 打開 Vcl config 檔案,寫入 Varnish configuration:
sub vcl_recv {
  # Add a unique header containing the client address
  remove req.http.X-Forwarded-For;
  set    req.http.X-Forwarded-For = client.ip;
  # [...]
}
2. 開啟 apache httpd.conf 加入此行:
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" varnishcombined
Varnish_02 Q:如何讓 PHP 程式紀錄正確的 Client IP? 可以參考底下程式碼就可以完全抓到 Proxy 後面真正使用者IP,否則您的 Web 只會抓到 Reverse Proxy 的 IP Address。
function getIP() {
  if (validip($_SERVER["HTTP_CLIENT_IP"])) {
    return $_SERVER["HTTP_CLIENT_IP"];
  }
  foreach(explode(",",$_SERVER["HTTP_X_FORWARDED_FOR"]) as $ip) {
    if (validip(trim($ip))) {return $ip;}
  }
  if (validip($_SERVER["HTTP_X_FORWARDED"])) 
  {
    return $_SERVER["HTTP_X_FORWARDED"];
  }
  else if (validip($_SERVER["HTTP_FORWARDED_FOR"])) 
  {
    return $_SERVER["HTTP_FORWARDED_FOR"];
  } 
  else if (validip($_SERVER["HTTP_FORWARDED"])) 
  {
    return $_SERVER["HTTP_FORWARDED"];
  } 
  else if (validip($_SERVER["HTTP_X_FORWARDED"])) 
  {
    return $_SERVER["HTTP_X_FORWARDED"];
  } 
  else 
  {
    return $_SERVER["REMOTE_ADDR"];
  }
}

function validip($ip) {
  if (!empty($ip) && ip2long($ip)!=-1) {
    $reserved_ips = array (
      array('10.0.0.0','10.255.255.255'),
      array('127.0.0.0','127.255.255.255'),
      array('169.254.0.0','169.254.255.255'),
      array('172.16.0.0','172.31.255.255'),
      array('192.168.0.0','192.168.255.255'),
    );
    foreach ($reserved_ips as $r) {
      $min = ip2long($r[0]);
      $max = ip2long($r[1]);
      if ((ip2long($ip) >= $min) && (ip2long($ip) <= $max)) return false;
    }
    return true;
  } 
  else 
  {
    return false;
  }
}[/code]
Q:rotate Varnish log file every day?
打開 /etc/newsyslog.conf,加入底下兩行
/var/log/varnish.log        640     7   *   @T00    JB  /var/run/varnishlog.pid
/var/log/varnishncsa.log    640     7   *   @T00    JB  /var/run/varnishncsa.pid
每天12點進行 log 備份,使用 gzip 壓縮 log 檔案。

[Vim] 想套用 *.php syntax 顏色於 *.ros

vim_header Vim 是一套強大的編輯器,它分佈於各大 UNIX systems,安裝好一套 UNIX 系統,預設就是 Vi 編輯器(FreeBSD 預設是 ee),相當好用,他也支援各種語言的 syntax,讓您在編輯檔案能夠看到各種不同顏色,在 FreeBSD 底下可以去看 /usr/local/share/vim/vim64/syntax/ 該資料夾支援各種語言,例如 PHP、Ruby、css、html、java、C/C++…等,假設今天我們想要 .ros 的副檔名需要用 php.vim syntax 來開啟,就必須做底下設定: 執行底下:
mkdri ~/.vim
vi ~/.vim/filetype.vim 
寫入 filetype.vim 資訊 if version < 600 syntax clear elseif exists("b:current_syntax") finish endif augroup filetypedetect au! BufRead,BufNewFile *.ros setfiletype php augroup END[/code] ps. on freebsd 7.1-RELEASE-p11 vim version 6.4.9 reference: Vim 套用 Markdown syntax vi 設定

[FreeBSD] DenyHosts 設定在 hosts.allow 注意事項

denyhosts DenyHosts 是一套用 Python 跟 shell script 寫出來的 open source base on Linux or FreeBSD (/var/log/secure on Redhat, /var/log/auth.log on Mandrake, FreeBSD, etc…),用來阻擋 SSH Server 被攻擊,之前寫一篇 FreeBSD 安裝設定教學,有一點沒有注意到,就是打開 /etc/hosts.allow,注意要把 ALL : ALL : allow 放到最後一行,跟 iptables 設定原理是一樣的,會從第一條規則開始比對,如果比對成功,下面的 rule 就會略過比對了,參考英文說明:
Start by allowing everything (this prevents the rest of the file from working, so remove it when you need protection). The rules here work on a “First match wins” basis.
/etc/hosts.deny 已經被 FreeBSD 棄用,所以必須把 allow 跟 deny 的 rule 都寫到 hosts.allow 檔案裡面才是正確的
#
# DenyHosts file: /etc/hosts.deniedssh
sshd : /etc/hosts.deniedssh : deny
sshd : ALL : allow

# Start by allowing everything (this prevents the rest of the file
# from working, so remove it when you need protection).
# The rules here work on a "First match wins" basis.
# move bottom by appleboy 2010.03.11
ALL : ALL : allow

[FreeBSD] Fanout and Fanterm Tool to run commands on multiple machines

在 Linux 或 FreeBSD 系統底下,如何下達 command 來達到多方控管機器,在網路上找到 Fanout and fanterm 這兩套軟體,可以實現我想要的目的,在 FreeBSD 有把 sysutils/fanout 進到 ports 裡面,安裝方式很簡單,就直接 make install clean 就可以了,用法也很簡易:
Usage:
  /usr/local/bin/fanout "MACHINES" "commands and parameters to run on each machine"
  /usr/local/bin/fanout -h      #Show this help
  /usr/local/bin/fanout --noping        # Do not ping before running command
當然事情沒有這麼簡單,因為是透過 SSH 方式去對其他機器下 command,所以作者都把預設 port 設定成 22,沒有完整考慮到其他環境,管理重要的 Server 怎麼會設定 22 阿,一定會改掉的阿,後來檢查一下 /usr/local/bin/fanout 這隻程式,並非編譯過的 binary 檔案,所以打開來看,就是利用 bash shell script 所寫的程式,網路上 Google 也找到一篇 Does anyone know how to specify a ssh port number with fanout? 文章,但是最後我是自己把 bash 改成支援可調整 port,也就是 MACHINES 可以放入:ip:portt or hostname:port patch 檔案:需要的就拿去 patch 就可以用了
--- /usr/local/bin/fanout   2010-01-08 15:06:47.000000000 +0800
+++ fanout  2010-01-08 15:00:04.000000000 +0800
@@ -53,13 +53,22 @@
 STARTTIME=`date`
 for ONETARGET in $TARGETS ; do
    case $ONETARGET in
-   *@*)                                    #user@machine form
+   *@*)                                    #user@machine:port form
        ONEUSER="-l ${ONETARGET%%@*}"
-       ONEMACH=${ONETARGET##*@}
-       HEADER="==== As ${ONETARGET%%@*} on $ONEMACH ===="
+       SERVER=${ONETARGET##*@}
+       HOST=`echo $SERVER | awk -F: '{ printf $1 }'`
+       PORT=`echo $SERVER | awk -F: '{ printf $2 }'`
+       if [ "$PORT" != "" ]; then
+           ONEPORT="-p ${PORT}"
+       else
+           ONEPORT=""
+       fi
+       ONEMACH=${HOST}
+       HEADER="==== As ${ONEUSER} on $ONEMACH port: ${PORT}===="
        ;;
    *)                                  #just machine form
        ONEUSER=""
+       ONEPORT=""
        ONEMACH=$ONETARGET
        HEADER="==== On $ONEMACH ===="
        ;;
@@ -74,7 +83,7 @@
    if [ "$PING" = "NO" ] || ping -c 3 $ONEMACH >/dev/null 2>/dev/null ; then
        echo Starting $ONETARGET >/dev/stderr               #Machine is reachable
        #Show machine name header, show command output, indented two spaces, save all to a temp file.
-       ( echo $HEADER ; ssh -n $ONEUSER $ONEMACH "$*" | sed -e 's/^/  /' ; echo ) >$TMPFILE &
+       ( echo $HEADER ; ssh $ONEPORT -n $ONEUSER $ONEMACH "$*" | sed -e 's/^/  /' ; echo ) >$TMPFILE &
    else
        echo $ONETARGET unavailable >/dev/stderr            #Machine not responding
        echo $HEADER ; echo "==== Machine unreachable by ping" ; echo >$TMPFILE

[UNIX] Vim 命令圖解說明

Vim 是一套在 Windows 或者是 UNIX 系統上面非常強大的編輯器,在 vgod’s blog 看到一篇:『給程式設計師的Vim入門圖解說明』,原作者提供了 pdf 以及 png 檔案讓大家下載阿,相當方便,看到這篇就馬上印出來貼在自己的桌子前面,圖解內容也相當清楚,很適合剛學習 UNIX 系統所需要的一些基本 Vim 指令,如何分割視窗,刪除行…等,另外在 Tsung’s Blog 看到 好文: Vim 食譜(CookBook、Recipes),提供了兩個好用連結:
  • Vim Recipes – Vim 由淺入深, 每篇都很短, 非常推薦此書~ (可線上看, 也可免費下載)
  • Vim Cookbook – Vim 食譜, 這篇專門紀錄常用、好用的密技.
都可以參考看看喔,對於學習 Vim 會有相當大的幫助,底下兩張是從 vgod’s blog 轉錄過來的。 vim-cheat-sheet-full vim-cheat-sheet-diagram

[FreeBSD] freebsd kernel: fxp0: link state changed to DOWN / UP

這一個禮拜被機器弄的頭昏腦脹,突然網路斷掉,然後不知不覺中又好了,接下來是斷斷續續,時好時壞,所以去檢查看一下 /var/log/message 訊息,log 檔案吐出很多底下的資訊:
Dec 9 21:17:02 freebsd kernel: fxp0: link state changed to DOWN Dec 9 21:17:03 freebsd kernel: fxp0: link state changed to UP Dec 9 21:17:10 freebsd kernel: fxp0: link state changed to DOWN Dec 9 21:17:12 freebsd kernel: fxp0: link state changed to UP
在網路上 FreeBSD mail list 查到一篇:『Interface Status changes to UP and Down』,裡面提到通常是硬體的問題,換過 switch 以及網路線,測試還是有問題,網路會斷斷續續,後來就用主機板上面另外兩個網孔測試,網路就不會時好時壞了,由於時常更改 /etc/rc.conf 裡面的 ip 設定,要重新啟動網路卡介面,這樣會造成遠端 ssh 斷線,解決方式請用下面指令: How do I restart network service over ssh session?
/etc/rc.d/netif restart && /etc/rc.d/routing restart
底下一些常用 FreeBSD 網路指令:
#關閉網卡
ifconfig network-interface down
#啟動網卡
ifconfig network-interface up
#觀看尚未啟動的網卡
ifconfig -d
#觀看已啟動網卡
ifconfig -u
# FreeBSD Update / restart routing tables / service
/etc/rc.d/routing restart
Reference FreeBSD: How To Start / Restart / Stop Network and Routing Service FreeBSD IP Alias: Setup 2 or More IP address on One NIC

[FreeBSD] port committer 快速建立 pkg-plist

FreeBSD 系統裡,最常使用就是管理安裝 ports,之前寫過一篇如何 commit update ports :『[FreeBSD] send-pr porter committer 需要注意事項』,根據 FreeBSD Porter’s Handbook 裡頭,寫到 pkg-plist 檔案內容是根據 ports 所產生的檔案列表,可以參考 Automated package list creation 這篇來快速產生,而我自己把該篇寫成 shell script 來直接產生,再來利用 diff 的方式來看看有無需要修改或者是增加,底下就是 shell script 內容:
#!/usr/local/bin/bash
###############################################
#
# Date:	    2009.11.18
# Author:   appleboy ( appleboy.tw AT gmail.com)
# Web:	    http://blog.wu-boy.com
# Ref:	    http://www.freebsd.org/doc/en/books/porters-handbook/plist-autoplist.html
#
###############################################

PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin
export PATH

if [ "$#" -lt "2" ]; then
    echo "please give two argument"
    echo "example $0 /tmp/dir1 /tmp/dir2"
    exit
fi

#
# configure system parameters

HOME=$1
TARGET=$2
TMPDIR="/var/tmp"

#
# configure end

if [ ! -d "$HOME" ]; then
    echo "${HOME} is not directory"
    exit
fi

if [ "$TARGET" != "" ] && [ ! -d "$TARGET" ]; then
    echo "$TARGET will be created"
    mkdir -p $TARGET
fi

#
# clean ports file

cd $HOME && make clean

#
# get port name 

PORTNAME=$(make -V PORTNAME)

#
# Before create port directory, please delete it.
# Next, create a temporary directory tree into which your port can be installed, and install any dependencies.
rm -rf ${TMPDIR}/${PORTNAME}
if [ ! -d "${TMPDIR}/${PORTNAME}" ]; then
    echo "${TMPDIR}/${PORTNAME} will be created"
    mkdir -p ${TMPDIR}/${PORTNAME}
fi

mtree -U -f $(make -V MTREE_FILE) -d -e -p ${TMPDIR}/${PORTNAME}
make depends PREFIX=${TMPDIR}/$PORTNAME

#
# Store the directory structure in a new file.

cd ${TMPDIR}/${PORTNAME} && find -d * -type d | sort > ${TARGET}/OLD-DIRS

#
# If your port honors PREFIX (which it should) you can then install the port and create the package list.

cd $HOME && make install PREFIX=${TMPDIR}/${PORTNAME}
cd ${TMPDIR}/${PORTNAME} && find -d * \! -type d | sort > ${TARGET}/pkg-plist

#
# You must also add any newly created directories to the packing list.

cd ${TMPDIR}/${PORTNAME} && find -d * -type d | sort | comm -13 ${TARGET}/OLD-DIRS - | sort -r | sed -e 's#^#@dirrm #' >> ${TARGET}/pkg-plist

echo "Please check  ${TARGET}/pkg-plist file"
用法大概是: ./create_pkg.sh /root/phpbb3 /root/test /root/phpbb3 是你修改 ports 的資料夾 /root/test 是 pkg-plist 新的資料夾