Kong系列(三)——Kong插件[IP Restriction]使用

Kong plugin 說明

Kong的官網中可以看到目前針對不同的需求提供了很多的插件,大部分都是開源版本,只有少部分是企業版

Kong的插件本身就是基於NGINX的一些屬性,來做一些擴展,比如通過IP進行限流,通過IP來限制黑白名單等等,在實際業務中可能需要用到的擴展

Plugin IP Restriction 簡單說明

IP Restriction就是通過設置IP白名單和黑名單,根據客戶端IP來對一些請求進行攔截和防護,通過插件的源碼可以看到其工作原理是,通過NGINX的變量binary_remote_addr以及設置的黑名單和白名單來進行限制,先看黑名單再看白名單,就是說白名單優先級更高一些,如果同時設置一個IP即在黑名單又在白名單列表,那麼這個IP是可以進行訪問的,代碼如下:

-- E:\work\projects\kong\kong\plugins\ip-restriction\handler.lua
function IpRestrictionHandler:access(conf)
  IpRestrictionHandler.super.access(self)
  local block = false
  local binary_remote_addr = ngx.var.binary_remote_addr

  if not binary_remote_addr then
    return responses.send_HTTP_FORBIDDEN("Cannot identify the client IP address, unix domain sockets are not supported.")
  end

  if conf.blacklist and #conf.blacklist > 0 then
    block = iputils.binip_in_cidrs(binary_remote_addr, cidr_cache(conf.blacklist))
  end

  if conf.whitelist and #conf.whitelist > 0 then
    block = not iputils.binip_in_cidrs(binary_remote_addr, cidr_cache(conf.whitelist))
  end

  if block then
    return responses.send_HTTP_FORBIDDEN("Your IP address is not allowed")
  end
end

配置

配置插件的方式比較簡單,插件可以設置到service上,可以設置到route上,0.13之前的也可以設置到api上,還有一些其他的組件也可以設置

我是設置到route上的,如果大家有其他需求可以到官網上查看文檔,點擊跳轉官網

添加

curl -X POST http://localhost:8001/routes/b01067c3-2537-45c8-be41-a80082c8bff3/plugins \
--data "name=ip-restriction" \
--data "config.whitelist=172.17.83.176, 172.17.83.177"

給ID爲b01067c3-2537-45c8-be41-a80082c8bff3增加ip-restriction插件

修改

curl -X PATCH http://localhost:8001/routes/b01067c3-2537-45c8-be41-a80082c8bff3/plugins \
--data "name=ip-restriction" \
--data "config.whitelist=172.17.83.177"

查看

curl -i --url http://localhost:8001/plugins

# 結果
{
    "total": 1,
    "data": [{
        "created_at": 1531446541000,
        "config": {
            "whitelist": ["172.17.83.177"]
        },
        "id": "ed6d0fb3-1dd6-4bc8-8712-75d338c2bda9",
        "name": "ip-restriction",
        "enabled": true,
        "route_id": "b01067c3-2537-45c8-be41-a80082c8bff3"
    }]
}

刪除

curl -X DELETE --url http://localhost:8001/plugins/bf95c7e8-bcd2-460a-a18b-bd1a309d7578/

想象中是配置完成之後,應該立馬就可以使用了。
然後我分別從兩個服務器上進行了測試,期望的結果應該是除了172.17.83.177這臺服務器之外,應該都不可以用。
但是發起結果其實並不是這個樣子,而是所有的都可以繼續訪問。
排查發現,我的Kong節點是部署在NGINX之後,通過NGINX反向代理到Kong節點的,我的NGINX反向代理節點部署在172.17.83.177。
所以說最終無論從哪兒訪問,而Kong看到的節點就是177,所以最終發現無論從哪臺服務器訪問都是可以的,這顯然是不符合我們的預期,經過查找資料找到解決方案,我們下節就重點描述此部分。

複雜配置

解決方案

首先說一下上節中提到的問題,讀IP Restriction源碼可以發現,他是用ngx.var.binary_remote_addr來識別出客戶端IP地址的,我們從NGINX可以瞭解到,這個地址通過代理之後,就已經發生了變化,這就是導致我們獲得的地址都是我們代理的地址,所以就失去了原本的意義。

所以我們顯然要做的就是將ngx.var.binary_remote_addr變爲客戶端的地址就能解決問題。

通過查找資料發現kong的版本中的變化,已經如何獲得客戶端真實的IP地址。

在0.11版本之後,Kong會設置X-Forwarded-For頭,所以在這裏會記錄下所有的請求信息,所以我們也要在代理中也進行設置X-Forwarded-For頭,這樣才能保證Kong中可以得到最原始的信息,所以解決這個問題的方法就是依靠NGINX 的ngx_http_realip_module,該模塊已經在Kong的官方發行包中。此模塊X-Forwarded-For根據我們配置的規則正確解析請求頭。

Kong公開的配置文件,我們可以看到(trusted_ips,real_ip_header 和real_ip_recursive)屬性,它們抽象出NGINX模塊的同名指令,通過設置這些屬性,我們就可以得到客戶端的真實地址

ngx_http_realip_module會更新NGINX的$remote_addr變量來包含客戶端IP。

IP限制插件使用ngx.var.binary_remote_addr,該變量也由realip模塊更新。

在0.11之前,X-Forwarded-For來自任何來源的Kong請求頭(默認情況下)。

從0.11開始,Kong默認不會信任來自任何來源的請求頭,所以需要通過我們的設置來完成信任。

在我們使用反向代理或者負載均衡時,需要設置Kong提供的屬性(trusted_ips,real_ip_header 和real_ip_recursive),以及我們自己的NGINX反向代理或者負載均衡時要設置的請求頭[X-Forwarded-For],下面就分成兩部分設置來解決此問題。

NGINX 反向代理配置

這一步比較簡單,就是要將客戶端的IP地址收集到請求頭X-Forwarded-For中,不管有多少層代理,這個請求頭中會記錄整個請求的聲明週期,就是講每次的IP地址都放在X-Forwarded-For中,然後使用逗號進行分隔,所以最終Kong只需要在取第一項即可,重點的配置如下:

location ~ /(\w+) {
 proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Scheme $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://172.17.83.176:8000;
    expires -1;
}

重點就是 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

通過docker啓動kong命令設置nginx-kong.conf

  • 啓動命令
docker run -d --name kong \
-e "KONG_TRUSTED_IPS=0.0.0.0/0,::/0" \
-e "KONG_REAL_IP_HEADER=X-Forwarded-For" \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=192.168.1.94"  \
-e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
-e "KONG_PROXY_ACCESS_LOG=logs/proxy_access.log"  \
-e "KONG_ADMIN_ACCESS_LOG=logs/admin_access.log" \
-e "KONG_PROXY_ERROR_LOG=logs/proxy_error.log" \
-e "KONG_ADMIN_ERROR_LOG=logs/admin_error.log" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
-p 8000:8000  \
-p 8443:8443  \
-p 8001:8001  \
-p 8444:8444  \
kong:latest

劃重點

-e “KONG_TRUSTED_IPS=0.0.0.0/0,::/0”
-e “KONG_REAL_IP_HEADER=X-Forwarded-For”

trusted_ips和real_ip_header是kong需要的一些參數,這裏有些參數最終會映射到nginx-kong.conf文件中,可以從kong的配置文件中可以看到其類型

trusted_ips表示信任的地址,如果是所有的地址都信任,可以配置爲0.0.0.0/0,::/0
real_ip_header表示的是

-- E:\work\projects\kong\kong\templates\kong_defaults.lua
return [[
prefix = /usr/local/kong/
log_level = notice
proxy_access_log = logs/access.log
proxy_error_log = logs/error.log
admin_access_log = logs/admin_access.log
admin_error_log = logs/error.log
plugins = bundled
custom_plugins = NONE
anonymous_reports = on

proxy_listen = 0.0.0.0:8000, 0.0.0.0:8443 ssl
admin_listen = 127.0.0.1:8001, 127.0.0.1:8444 ssl
nginx_user = nobody nobody
nginx_worker_processes = auto
nginx_optimizations = on
nginx_daemon = on
mem_cache_size = 128m
ssl_cert = NONE
ssl_cert_key = NONE
client_ssl = off
client_ssl_cert = NONE
client_ssl_cert_key = NONE
ssl_cipher_suite = modern
ssl_ciphers = ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
admin_ssl_cert = NONE
admin_ssl_cert_key = NONE
upstream_keepalive = 60
headers = server_tokens, latency_tokens
trusted_ips = NONE
real_ip_header = X-Real-IP
real_ip_recursive = off
client_max_body_size = 0
client_body_buffer_size = 8k
error_default_type = text/plain

database = postgres
pg_host = 127.0.0.1
pg_port = 5432
pg_database = kong
pg_user = kong
pg_password = NONE
pg_ssl = off
pg_ssl_verify = off
cassandra_contact_points = 127.0.0.1
cassandra_port = 9042
cassandra_keyspace = kong
cassandra_timeout = 5000
cassandra_ssl = off
cassandra_ssl_verify = off
cassandra_username = kong
cassandra_password = NONE
cassandra_consistency = ONE
cassandra_lb_policy = RoundRobin
cassandra_local_datacenter = NONE
cassandra_repl_strategy = SimpleStrategy
cassandra_repl_factor = 1
cassandra_data_centers = dc1:2,dc2:3
cassandra_schema_consensus_timeout = 10000

db_update_frequency = 5
db_update_propagation = 0
db_cache_ttl = 0
db_resurrect_ttl = 30

dns_resolver = NONE
dns_hostsfile = /etc/hosts
dns_order = LAST,SRV,A,CNAME
dns_stale_ttl = 4
dns_not_found_ttl = 30
dns_error_ttl = 1
dns_no_sync = off

lua_socket_pool_size = 30
lua_ssl_trusted_certificate = NONE
lua_ssl_verify_depth = 1
lua_package_path = ./?.lua;./?/init.lua;
lua_package_cpath = NONE
]]

注意:

這裏面的參數,都可以在使用docker啓動時,通過-e的方式傳遞進去,前提是要在這個參數的名字之前加上前綴KONG_*,並且全部是大寫

  • nginx-kong.conf

我們使用docker啓動後,nginx-kong.conf的內容已經設置爲我們命令中指定的參數

charset UTF-8;

error_log syslog:server=kong-hf.mashape.com:61828 error;

error_log logs/proxy_error.log notice;


client_max_body_size 0;
proxy_ssl_server_name on;
underscores_in_headers on;

lua_package_path './?.lua;./?/init.lua;;;';
lua_package_cpath ';;';
lua_socket_pool_size 30;
lua_max_running_timers 4096;
lua_max_pending_timers 16384;
lua_shared_dict kong                5m;
lua_shared_dict kong_cache          128m;
lua_shared_dict kong_db_cache_miss 12m;
lua_shared_dict kong_process_events 5m;
lua_shared_dict kong_cluster_events 5m;
lua_shared_dict kong_healthchecks   5m;
lua_shared_dict kong_rate_limiting_counters 12m;
lua_socket_log_errors off;

init_by_lua_block {
    kong = require 'kong'
    kong.init()
}

init_worker_by_lua_block {
    kong.init_worker()
}


upstream kong_upstream {
    server 0.0.0.1;
    balancer_by_lua_block {
        kong.balancer()
    }
    keepalive 60;
}

server {
    server_name kong;
    listen 0.0.0.0:8000;
    listen 0.0.0.0:8443 ssl;
    error_page 400 404 408 411 412 413 414 417 494 /kong_error_handler;
    error_page 500 502 503 504 /kong_error_handler;

    access_log logs/proxy_access.log;
    error_log logs/proxy_error.log notice;

    client_body_buffer_size 8k;

    ssl_certificate /usr/local/kong/ssl/kong-default.crt;
    ssl_certificate_key /usr/local/kong/ssl/kong-default.key;
    ssl_protocols TLSv1.1 TLSv1.2;
    ssl_certificate_by_lua_block {
        kong.ssl_certificate()
    }

    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA3


    real_ip_header     X-Forwarded-For;
    real_ip_recursive  on;
    set_real_ip_from   0.0.0.0/0;                       
    set_real_ip_from   ::/0;

    location / {
        set $upstream_host               '';
        set $upstream_upgrade            '';
        set $upstream_connection         '';
        set $upstream_scheme             '';
        set $upstream_uri                '';
        set $upstream_x_forwarded_for    '';
        set $upstream_x_forwarded_proto  '';
        set $upstream_x_forwarded_host   '';
        set $upstream_x_forwarded_port   '';

        rewrite_by_lua_block {         
            kong.rewrite()               
        }                              

        access_by_lua_block {          
            kong.access()              
        }                                       

        proxy_http_version 1.1;
        proxy_set_header   Host              $upstream_host;
        proxy_set_header   Upgrade           $upstream_upgrade;
        proxy_set_header   Connection        $upstream_connection;
        proxy_set_header   X-Forwarded-For   $upstream_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $upstream_x_forwarded_proto;
        proxy_set_header   X-Forwarded-Host  $upstream_x_forwarded_host;
        proxy_set_header   X-Forwarded-Port  $upstream_x_forwarded_port;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_pass_header  Server;
        proxy_pass_header  Date;
        proxy_ssl_name     $upstream_host;
        proxy_pass         $upstream_scheme://kong_upstream$upstream_uri;

        header_filter_by_lua_block {
            kong.header_filter()
        }        

        body_filter_by_lua_block {
            kong.body_filter()
        }            

        log_by_lua_block {  
            kong.log()                                                 
        }                                          
    }

    location = /kong_error_handler {      
        internal;
        content_by_lua_block { 
            kong.handle_error()
        }                                                
    }                                                        
}                                 

server {                      
    server_name kong_admin;
    listen 0.0.0.0:8001;
    listen 0.0.0.0:8444 ssl;         

    access_log logs/admin_access.log;
    error_log logs/admin_error.log notice;                                                                                                                                                                                                   

    client_max_body_size 10m;
    client_body_buffer_size 10m;   
    ssl_certificate /usr/local/kong/ssl/admin-kong-default.crt;
    ssl_certificate_key /usr/local/kong/ssl/admin-kong-default.key;
    ssl_protocols TLSv1.1 TLSv1.2;    

    ssl_session_cache shared:SSL:10m;       
    ssl_session_timeout 10m;                
    ssl_prefer_server_ciphers on;           
    ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA3

    location / {                            
        default_type application/json;      
        content_by_lua_block {              
            kong.serve_admin_api()          
        }                    
    }                                  

    location /nginx_status {           
        internal;                      
        access_log off;                
        stub_status;                   
    }                                           

    location /robots.txt {     
        return 200 'User-agent: *\nDisallow: /';            
    }                                                          
}                                                

原創不易,如果你覺得這篇文章對你有所幫助,請他喝杯咖啡吧
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章