Hero Image
Shell 腳本學習筆記

Shell 腳本學習筆記 執行算術運算 val=`expr $a + $b` 運算符 符號 說明 示例 ! 非運算 [ ! false ] -o 或運算 [ $a -lt 20 -o $b -gt 20 ] -a 與運算 [ $a -lt 20 -a $b -gt 20 ] = 相等檢測 [ $a = $b ] != 不相等檢測 [ $a != $b ] -z 字串長度是否為 0,為 0 則回傳 true [ -z $a ] -n 字串長度不為 0,不為 0 回傳 true [ -n $a ] str 檢測字串是否為空,不為空回傳 true [ $a ] -b 檢測檔案是否是區塊裝置檔 [ -b $file ] -c 檢測檔案是否是字元裝置 .. -d 檢測檔案是否為目錄 [ -d $file ] -f 檢測檔案是否為一般檔案 [ -f $file ] -r 檢測檔案是否可讀 .. -w 檢測檔案是否可寫 .. -x 檢測檔案是否可執行 .. -s 檢測檔案是否為空 .. -e 檢測檔案是否存在 .. 特殊變數 變數 含義 $0 目前腳本的檔名 $n 傳遞給腳本或函式的參數,n 表示第幾個參數 $# 傳遞給腳本或函式的參數個數 $* 傳遞給腳本或函式的所有參數,所有參數視為一個詞,例如 “1 2 3” $@ 傳遞給腳本或函式的所有參數,每個參數視為一個詞,用雙引號包含,例如 “1” “2” “3” $? 上個命令的退出狀態,或函式的回傳值 $$ 目前 Shell 行程 ID;對 Shell 腳本而言,就是該腳本所在的行程 ID POSIX 程式退出狀態 狀態碼 含義 0 命令成功退出 > 0 在重導向或單詞展開期間(~、變數、命令、算術展開以及單詞切割)失敗 1 - 125 命令不成功退出,各命令自行定義特定退出值含義 126 命令找到但檔案無法執行 127 命令未找到 > 128 命令因收到信號而終止 輸入輸出重導向 命令 說明 command > file 將輸出重導向到 file command > file 將輸出以追加的方式重導向到 file n > file 將檔案描述符為 n 的檔案重導向到 file n » file 將檔案描述符為 n 的檔案以追加的方式重導向到 file n >& m 將輸出檔案 m 和 n 合併 n <& m 將輸入檔案 m 和 n 合併 « tag 將開始標記 tag 和結束標記 tag 之間的內容作為輸入 檔案包含 使用 . 或 source 包含檔案

Hero Image
openresty+redis 攔截高頻訪問 IP

openresty+redis 攔截高頻訪問 IP init_by_lua_block { redis = require "redis" client = redis.connect('127.0.0.1', 6379) } server { listen 8080; location / { access_by_lua_file /usr/local/nginx/conf/lua/block.lua; proxy_pass http://192.168.1.102:8000; } } -- Redis-based IP rate limiting / blocking for OpenResty (ngx_lua) -- NOTE: -- This script assumes a global `client` variable is used/stored. -- Make sure `redis` module is available and `client` is initialized somewhere. local function isConnected() return client:ping() end local function createRedisConnection() return redis.connect("127.0.0.1", 6379) end -- 如果發生 redis 連線失敗,將停止攔截(直接放行) if pcall(isConnected) then -- already connected (or ping succeeded) else -- not connected; try reconnect if pcall(createRedisConnection) then -- 斷開重連:會導致每次訪問都需要重連 redis -- 訪問量大時建議:關閉重連邏輯(pcall 不執行),直接 ngx.exit 放行/終止 client = createRedisConnection() else ngx.exit(ngx.OK) end end local ttl = 60 -- 監測週期(秒) local bktimes = 30 -- 在監測週期內達到觸發攔截的訪問量 local block_ttl = 600 -- 觸發攔截後攔截時間(秒) local ip = ngx.var.remote_addr local ipvtimes = client:get(ip) if ipvtimes then if ipvtimes == "-1" then -- blocked return ngx.exit(403) else local last_ttl = client:ttl(ip) -- ngx.say("key exist.ttl is ", last_ttl) if last_ttl == -1 then client:set(ip, 0) client:expire(ip, ttl) -- ngx.say("ttl & vtimes recount") return ngx.exit(ngx.OK) end local vtimes = tonumber(client:get(ip)) + 1 if vtimes < bktimes then client:set(ip, vtimes) client:expire(ip, last_ttl) -- ngx.say(ip, " view ", vtimes, " times") return ngx.exit(ngx.OK) else -- ngx.say(ip, " will be block next time.") client:set(ip, -1) client:expire(ip, block_ttl) return ngx.exit(ngx.OK) end end else -- key does not exist client:set(ip, 1) -- ngx.say(ip, " view 1 times") client:expire(ip, ttl) return ngx.exit(ngx.OK) end

Hero Image
設定 Haproxy 以防止 DDOS 攻擊

設定 Haproxy 以防止 DDOS 攻擊 TCP syn flood attacks vi /etc/sysctl.conf # Protection from SYN flood net.ipv4.tcp_syncookies = 1 net.ipv4.conf.all.rp_filter = 1 net.ipv4.tcp_max_syn_backlog = 1024 Slowloris like attacks defaults option http-server-close timeout http-request 5s timeout connect 5s timeout client 30s timeout server 10s timeout tunnel 1h 限制每一個 user 的連線數量 普通用戶瀏覽網站的網頁,或是從網站下載東西時,瀏覽器一般會建立 5-7 個 TCP 鏈接。當一個惡意 client 打開了大量 TCP 連線時,耗費大量資源,因此我們必須要限制同一個用戶的連線數量。 但如果有很多使用者,是從某一個私有網段,透過 NAT 的方式連線到 Server 時,且實際上我們也不知道,到底哪一個會是 NAT 的轉址後的 IP,不知道該將哪個 IP 設定為白名單,這樣的限制就會造成問題,因此我們認為實際的環境,這樣的設定應該要保留不處理。 以下是一個設定的範例,最重要的地方是在 frontend ft_web 區塊的設定。 global stats socket ./haproxy.stats level admin defaults option http-server-close mode http timeout http-request 5s timeout connect 5s timeout server 10s timeout client 30s listen stats bind 0.0.0.0:8880 stats enable stats hide-version stats uri / stats realm HAProxy Statistics stats auth admin:admin frontend ft_web bind 0.0.0.0:8080 # Table definition stick-table type ip size 100k expire 30s store conn_cur # Allow clean known IPs to bypass the filter tcp-request connection accept if { src -f /etc/haproxy/whitelist.lst } # Shut the new connection as long as the client has already 10 opened tcp-request connection reject if { src_conn_cur ge 10 } tcp-request connection track-sc1 src # Split static and dynamic traffic since these requests have different impacts on the servers use_backend bk_web_static if { path_end .jpg .png .gif .css .js } default_backend bk_web # Dynamic part of the application backend bk_web balance roundrobin cookie MYSRV insert indirect nocache server srv1 192.168.1.2:80 check cookie srv1 maxconn 100 server srv2 192.168.1.3:80 check cookie srv2 maxconn 100 # Static objects backend bk_web_static balance roundrobin server srv1 192.168.1.2:80 check maxconn 1000 server srv2 192.168.1.3:80 check maxconn 1000 限制每個 user 產生新連線的速率 Limiting the connection rate per user 惡意的使用者會在短時間內建立很多連線,但如果產生新連線的速度太高,就會消耗掉過多的資源服務一個使用者。

Hero Image
Nginx 請求處理流程你了解嗎?

Nginx 請求處理流程你了解嗎? 11 個處理階段 1)NGX_HTTP_POST_READ_PHASE: 接收到完整的 HTTP 標頭後處理的階段,位於 URI 重寫之前。實際上很少有模組會註冊在該階段,預設情況下會被跳過。 2)NGX_HTTP_SERVER_REWRITE_PHASE: 在 URI 與 location 匹配前修改 URI 的階段,用於重新導向。該階段執行 server 區塊內、location 區塊外的重寫指令。在讀取請求標頭的過程中,nginx 會根據 host 及埠號找到對應的虛擬主機設定。 3)NGX_HTTP_FIND_CONFIG_PHASE: 根據 URI 尋找匹配的 location 設定項階段,使用重寫後的 URI 來查找對應的 location。需要注意的是該階段可能會被執行多次,因為也可能有 location 級別的重寫指令。 4)NGX_HTTP_REWRITE_PHASE: 上一階段找到 location 後再次修改 URI,屬於 location 級別的 URI 重寫階段,也可能會被執行多次。 5)NGX_HTTP_POST_REWRITE_PHASE: 防止重寫 URL 後導致的死循環,屬於 location 重寫的下一階段,用來檢查上階段是否有 URI 重寫,並根據結果跳轉到合適的階段。 6)NGX_HTTP_PREACCESS_PHASE: 下一階段之前的準備,屬於存取權限控制的前一階段。一般也用於存取控制,例如限制存取頻率、連線數等。 7)NGX_HTTP_ACCESS_PHASE: 讓 HTTP 模組判斷是否允許請求進入 Nginx 伺服器的存取控制階段,例如基於 IP 白名單/黑名單、使用者名稱密碼等的權限控制。 8)NGX_HTTP_POST_ACCESS_PHASE: 存取控制的後一階段,根據上一階段的執行結果進行處理,向使用者送出拒絕服務的錯誤碼,用來回應上一階段的拒絕。 9)NGX_HTTP_TRY_FILES_PHASE: 為存取靜態檔案資源而設置,try_files 指令的處理階段。如果沒有設定 try_files 指令,該階段會被跳過。 10)NGX_HTTP_CONTENT_PHASE: