<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    Nginx 配置指令的執行順序(十)

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    運行在 post-rewrite 階段之后的是所謂的 preaccess 階段。該階段在 access 階段之前執行,故名preaccess.

     

        標準模塊 ngx_limit_req 和 ngx_limit_zone 就運行在此階段,前者可以控制請求的訪問頻度,而后者可以限制訪問的并發度。這里我們僅僅和它們打個照面,后面還會有機會專門接觸到這兩個模塊。

     

        前面反復提到的標準模塊 ngx_realip 其實也在這個階段注冊了處理程序。有些讀者可能會問:“這是為什么呢?它不是已經在 post-read 階段注冊處理程序了嗎?”我們不妨通過下面這個例子來揭曉答案:

        server {
            listen 8080;
     
            location /test {
                set_real_ip_from 127.0.0.1;
                real_ip_header X-Real-IP;
     
                echo "from: $remote_addr";
            }
        }

    與先看前到的例子相比,此例最重要的區別在于把 ngx_realip 的配置指令放在了 location 配置塊中。前面我們介紹過,Nginx 匹配 location 的動作發生在 find-config 階段,而 find-config 階段遠遠晚于 post-read階段執行,所以在 post-read 階段,當前請求還沒有和任何 location 相關聯。在這個例子中,因為ngx_realip 的配置指令都寫在了 location 配置塊中,所以在 post-read 階段,ngx_realip 模塊的處理程序沒有看到任何可用的配置信息,便不會執行來源地址的改寫工作了。

     

        為了解決這個難題,ngx_realip 模塊便又特意在 preaccess 階段注冊了處理程序,這樣它才有機會運行location 塊中的配置指令。正是因為這個緣故,上面這個例子的運行結果才符合直覺預期:

        $ curl -H 'X-Real-IP: 1.2.3.4' localhost:8080/test
        from: 1.2.3.4

    不幸的是,ngx_realip 模塊的這個解決方案還是存在漏洞的,比如下面這個例子:

        server {
            listen 8080;
     
            location /test {
                set_real_ip_from 127.0.0.1;
                real_ip_header X-Real-IP;
     
                set $addr $remote_addr;
                echo "from: $addr";
            }
        }

    這里,我們在 rewrite 階段將 $remote_addr 的值保存到了用戶變量 $addr 中,然后再輸出。因為 rewrite階段先于 preaccess 階段執行,所以當 ngx_realip 模塊尚未在 preaccess 階段改寫來源地址時,最初的來源地址就已經在 rewrite 階段被讀取了。上例的實際請求結果證明了我們的結論:

        $ curl -H 'X-Real-IP: 1.2.3.4' localhost:8080/test
        from: 127.0.0.1

    輸出的地址確實是未經改寫過的。Nginx 的“調試日志”可以進一步確認這一點:

        $ grep -E 'http script (var|set)|realip' logs/error.log
        [debug] 32488#0: *1 http script var: "127.0.0.1"
        [debug] 32488#0: *1 http script set $addr
        [debug] 32488#0: *1 realip: "1.2.3.4"
        [debug] 32488#0: *1 realip: 0100007F FFFFFFFF 0100007F
        [debug] 32488#0: *1 http script var: "127.0.0.1"

    其中第一行調試信息

        [debug] 32488#0: *1 http script var: "127.0.0.1"

    是 set 語句讀取  便是 $remote_addr 當時讀出來的值。

     

        而第二行調試信息

        [debug] 32488#0: *1 http script set $addr

    則顯示我們對變量 $addr 進行了賦值操作。

     

        后面兩行信息

        [debug] 32488#0: *1 realip: "1.2.3.4"
        [debug] 32488#0: *1 realip: 0100007F FFFFFFFF 0100007F

    是 ngx_realip 模塊在 preaccess 階段改寫當前請求的來源地址。我們看到,改寫后的新地址確實是期望的1.2.3.4. 但很明顯這個操作發生在 $addr 變量賦值之后,所以已經太遲了。

     

        而最后一行信息

        [debug] 32488#0: *1 http script var: "127.0.0.1"

    則是 echo 配置指令在輸出時讀取變量 $addr 時產生的,我們看到它的值是改寫前的來源地址。

     

        看到這里,有的讀者可能會問:“如果 ngx_realip 模塊不在 preaccess 階段注冊處理程序,而在rewrite 階段注冊,那么上例不就可以工作了?”答案是:不一定。因為 ngx_rewrite 模塊的處理程序也同樣注冊在 rewrite 階段,而前面我們在 (二) 中特別提到,在這種情況下,不同模塊之間的執行順序一般是不確定的,所以 ngx_realip 的處理程序可能仍然在 set 語句之后執行。

     

        一個建議是:盡量在 server 配置塊中配置 ngx_realip 這樣的模塊,以避免上面介紹的這種棘手的例外情況。

     

        運行在 preaccess 階段之后的則是我們的另一個老朋友,access 階段。前面我們已經知道了,標準模塊ngx_access、第三方模塊 ngx_auth_request 以及第三方模塊 ngx_lua 的 access_by_lua 指令就運行在這個階段。

     

        access 階段之后便是 post-access 階段。從這個階段的名字,我們也能一眼看出它是緊跟在 access 階段后面執行的。這個階段也和 post-rewrite 階段類似,并不支持 Nginx 模塊注冊處理程序,而是由 Nginx 核心自己完成一些處理工作。post-access 階段主要用于配合 access 階段實現標準 ngx_http_core 模塊提供的配置指令 satisfy 的功能。

     

        對于多個 Nginx 模塊注冊在 access 階段的處理程序,satisfy 配置指令可以用于控制它們彼此之間的協作方式。比如模塊 A 和 B 都在 access 階段注冊了與訪問控制相關的處理程序,那就有兩種協作方式,一是模塊 A 和模塊 B 都得通過驗證才算通過,二是模塊 A 和模塊 B 只要其中任一個通過驗證就算通過。第一種協作方式稱為 all 方式(或者說“與關系”),第二種方式則被稱為 any 方式(或者說“或關系”)。默認情況下,Nginx 使用的是 all 方式。下面是一個例子:

        location /test {
            satisfy all;
     
            deny all;
            access_by_lua 'ngx.exit(ngx.OK)';
     
            echo something important;
        }

    這里,我們在 /test 接口中同時配置了 ngx_access 模塊和 ngx_lua 模塊,這樣 access 階段就由這兩個模塊一起來做檢驗工作。其中,語句 deny all 會讓  則總是允許訪問。當我們通過 satisfy 指令配置了 all 方式時,就需要access 階段的所有模塊都通過驗證,但不幸的是,這里 ngx_access 模塊總是會拒絕訪問,所以整個請求就會被拒:

        $ curl localhost:8080/test
        <html>
        <head><title>403 Forbidden</title></head>
        <body bgcolor="white">
        <center><h1>403 Forbidden</h1></center>
        <hr><center>nginx</center>
        </body>
        </html>

    細心的讀者會在 Nginx 錯誤日志文件中看到類似下面這一行的出錯信息:

        [error] 6549\#0: *1 access forbidden by rule

    然而,如果我們把上例中的 satisfy all 語句更改為 satisfy any

        location /test {
            satisfy any;
     
            deny all;
            access_by_lua 'ngx.exit(ngx.OK)';
     
            echo something important;
        }

    結果則會完全不同:

        $ curl localhost:8080/test
        something important

    即請求反而最終通過了驗證。這是因為在 any 方式下,access 階段只要有一個模塊通過了驗證,就會認為請求整體通過了驗證,而在上例中,ngx_lua 模塊的 access_by_lua 語句總是會通過驗證的。

     

        在配置了 satisfy any 的情況下,只有當 access 階段的所有模塊的處理程序都拒絕訪問時,整個請求才會被拒,例如:

        location /test {
            satisfy any;
     
            deny all;
            access_by_lua 'ngx.exit(ngx.HTTP_FORBIDDEN)';
     
            echo something important;
        }

    此時訪問 /test 接口才會得到 403 Forbidden 錯誤頁。這里,post-access 階段參與了 access 階段各模塊處理程序的“或關系”的實現。

     

        值得一提的是,上面這幾個的例子需要 ngx_lua 0.5.0rc19 或以上版本;之前的版本是不能和 satisfy any 配置語句一起工作的。

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全