Category Archives

6 Articles

使用lua-nginx-module緩存JSONP

0   148 轉為簡體

最近給暢言加上了單點登錄,也就是可以用網站的帳號來在暢言上發射評論。暢言會通過你設置的一個接口來獲得用戶名,頭像等。但是我發現隊友暢言會頻繁請求這個接口,有時候會達到單個用戶一秒鐘好幾次??

雖然在php層有redis,壓力不會很大,但是這樣頻繁的請求還是會中防CC的策略,影響用戶正常的瀏覽。所以我決定在CDN上做個緩存。

Read More

ipip.tk地理位置查詢

0   1602 轉為簡體

基於OpenResty ,MaxMind GeoIP數據庫和從bgp.he.net生成的ASN數據庫,因為沒有經緯度的需求所以沒有顯示。下文有源代碼的鏈接,如果需要可以自行修改加上經緯度或者將輸出變為JSON等。

使用方法

查詢當前IP地理位置

$ curl https://ipip.tk/
x.x.x.x
Country, City
ASN number

查詢當前IP

$ curl https://ipip.tk/ip
x.x.x.x

查詢指定IP的地理位置

$ curl https://ipip.tk/74.125.203.199
74.125.203.199
United States, Mountain View
AS15169 Google Inc.

查詢域名的地理位置

$ curl https://ipip.tk/www.google.com.hk
74.125.203.199
United States, Mountain View
AS15169 Google Inc.

查詢域名的IP

$ curl https://ipip.tk/www.google.com.hk/ip
74.125.203.199

查詢域名的IP和CNAME(如果存在)

$ curl https://ipip.tm/www.google.com.hk/dns
www-wide.l.google.com 74.125.203.199

源代碼

在這裡https://gist.github.com/fffonion/44e5fb59e2a8f0efba5c1965c6043584

需要ngx_http_geoip_module, echo-nginx-module, lua-nginx-module,安裝libgeoip,並將maxmind的舊版geoip數據庫放在/usr/share/GeoIP

通過bgp.he.net生成ASN數據庫

Read More

用 OpenResty 寫了一個 SNI 代理

0   2218 轉為簡體

功能類似於dlundquist/sniproxy

推薦 OpenResty 加上 stream 模塊和 ngx_stream_lua_module 模塊。在 1.9.15.1 上測試通過。

示例配置:

A Lua table sni_rules should be defined in the init_worker_by_lua_block directive.

The key can be either whole host name or regular expression. Use . for a default host name. If no entry is matched, connection will be closed.

The value is a table containing host name and port. If host is set to nil, the server_name in SNI will be used. If the port is not defined or set to nil, 443 will be used.

Rules are applied with the priority as its occurrence sequence in the table. In the example above, twitter.com will match the third rule rather than the fourth.

If the protocol version is less than TLSv1 (eg. SSLv3, SSLv2), connection will be closed, since SNI extension is not supported in these versions.

nginx 批量配置同步

0   61033 轉為簡體

在編譯了lua-nginx-module的nginx上,可以方便地使用shared dict特性,在不reload配置文件的情況下實現配置同步。

由於shared dict使用一塊共享內存,因此所有worker均可讀寫,也就不存在一致性的問題。

使用shared dict

Read More

nginx/openresty的一些記錄

28   24353 轉為簡體

日誌

屏蔽user-agent並屏蔽日誌

不屏蔽user-agent(允許其訪問),但屏蔽日誌

按uri屏蔽日誌(可以和上面的按user-agent用同一個變量來同時過濾uri和user-agent)

 

Header

按mime type設置緩存時間

防攻擊

簡單的無狀態cookie challenge(需要lua-nginx-module)

crawlers塊中可以手動填寫要屏蔽的IP

將其中的s改成隨機字符串+時間戳可以變成有狀態版本(需使用redis/memcached/shared memory存儲生成的隨機字符串)

將set-cookie改成通過js生成cookie可以變成javascript challenge,注意要在js里加上瀏覽器上下文判斷,如var cookie=location.protocol?cookie:””; 或者DOM操作

這裡有個更高級的輸驗證碼的示例

其他

植入cookie

需要注意的是使用ngx.time()產生秒級的時間,用來做隨機數種子可能會衝突,因此建議加上另外的隨機變量(如下面的例子用的是客戶端的ip) 可以使用ngx.now()產生毫秒精度時間

Lua用setmetatable返回默認值為function時的暗坑

0   113094 轉為簡體

想在api服務器里實現一個acl的功能,對某些請求(需要登錄,需要檢查appkey,需要限制頻次等)做限制,對某些起始狀態(比如登陸)或者終結狀態(比如報錯)的請求放行。

因為lua里木有switch case,因此通過一個acl_list的table去查找規則,因為需要限制的請求種類比較多,就把rule_check_token當成默認值了,一看是是這麼寫的:

然而卻會在local token= xxxx那一行報stack overflow,想了半天也發現哪裡有無限遞歸,因為query傳進來的是http請求的query string解析出的鍵值對錶。

把query打印出來一看,發現竟然是這個模塊本身……

仔細看了文檔才知道,原來__index後面的值是一個function時,lua會調用這個function去獲得不存在的鍵,並且第一個參數是模塊本身(即_M,一個table)。在這個例子里:

  1. 調用一個非默認規則的api
  2. lua調用了rule_check_token
  3. 參數query被傳入了_M本身
  4. 運行到local token= xxxx這一行
  5. 這個table里(_M)又沒有token這個鍵
  6. 回到2

所以就死循環了

 

所以要好好看文檔

 

解決方法是可以套一個function

 

貼一個打印table的工具,方便調試:

可以打印出如下形式:

使用openresty請自行改寫成local function和ngx.say的形式