Kong API網關使用筆記

背景

Kong是基於OpenResty的開源網關,其將API相關信息配置到postgresql或者是cassandra。通過lua擴展模塊,擴展網關平臺的功能。

本文記錄着筆者在對Kong網關的具體使用過程。包括Kong部署,Kong組件使用,Kong插件使用。其他Kong相關的內容,可以到官網上查閱。

關注微信公衆號:戰渣渣
回覆關鍵字“kong”獲取測試代碼

Kong部署

Kong的部署方式有很多種,這裏採取的是基於Docker的部署過程。

postgresql部署

Docker部署Postgresql9.6

  • 創建網絡
docker network create kong-net
  • 啓動數據庫
docker run -d --name kong-database \
               --network=kong-net \
               -p 5432:5432 \
               -e "POSTGRES_USER=mykong" \
               -e "POSTGRES_DB=mykong" \
               -e "POSTGRES_PASSWORD=mykong" \
               postgres:9.6

數據庫高可用性,可將數據庫做雙機熱備。筆者沒有實際需求,下面是收集的一些資料並未執行,待到以後有需求再做執行

流複製+雙機熱備方案

  • 配置postgresql的主從流複製
    https://www.cnblogs.com/cxy486/p/5164612.html
  • 利用pgpool-II中間件實現高可用性
    https://www.iteye.com/blog/iwin-2108807
    https://www.xiaomastack.com/2019/08/16/postgresql%E9%9B%86%E7%BE%A4/
  • 利用keepalived+pgpoll-II完成雙機熱備切換
    https://www.cnblogs.com/songyuejie/p/4561089.html

部署kong

創建kong所需要數據庫
創建數據庫時指定的用戶名,密碼,數據庫等,需要使用KONG_前綴的參數使用。
其他環境變量說明

docker run --rm \
     --network=kong-net \
     -e "KONG_DATABASE=postgres" \
     -e "KONG_PG_HOST=kong-database" \
     -e "KONG_PG_USER=mykong" \
     -e "KONG_PG_PASSWORD=mykong" \
     -e "KONG_PG_DATABASE=mykong" \
     kong:2.0.1 kong migrations bootstrap

使用kong最新版本 2.0.1

節點1:172.17.23.14

docker run -d --name kong  \
    --network=kong-net \
    -e "KONG_DATABASE=postgres" \
    -e "KONG_PG_HOST=kong-database" \
    -e "KONG_PG_USER=mykong" \
    -e "KONG_PG_PASSWORD=mykong" \
    -e "KONG_PG_DATABASE=mykong" \
    -e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
    -e "KONG_ADMIN_LISTEN_SSL=0.0.0.0:8444" \
    -e "KONG_PROXY_ACCESS_LOG=/usr/local/kong/logs/proxy_access.log"     \
    -e "KONG_ADMIN_ACCESS_LOG=/usr/local/kong/logs/admin_access.log"     \
    -e "KONG_PROXY_ERROR_LOG=/usr/local/kong/logs/proxy_error.log"     \
    -e "KONG_ADMIN_ERROR_LOG=/usr/local/kong/logs/admin_error.log" \
    -e "KONG_TRUSTED_IPS=0.0.0.0/0,::/0"     \ # 使用IP-restriction插件時,保證真實IP地址的傳遞
    -e "KONG_REAL_IP_HEADER=X-Forwarded-For" \ # 使用IP-restriction插件時,保證真實IP地址的傳遞
    -p 8000:8000 \
    -p 8443:8443 \
    -p 8001:8001 \
    -p 8444:8444 \
    kong:2.0.1

節點2:172.17.23.17

docker run -d --name kong  \
    -e "KONG_DATABASE=postgres" \
    -e "KONG_PG_HOST=172.17.23.14" \
    -e "KONG_PG_USER=mykong" \
    -e "KONG_PG_PASSWORD=mykong" \
    -e "KONG_PG_DATABASE=mykong" \
    -e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
    -e "KONG_ADMIN_LISTEN_SSL=0.0.0.0:8444" \
    -e "KONG_PROXY_ACCESS_LOG=/usr/local/kong/logs/proxy_access.log"     \
    -e "KONG_ADMIN_ACCESS_LOG=/usr/local/kong/logs/admin_access.log"     \
    -e "KONG_PROXY_ERROR_LOG=/usr/local/kong/logs/proxy_error.log"     \
    -e "KONG_ADMIN_ERROR_LOG=/usr/local/kong/logs/admin_error.log" \
    -e "KONG_TRUSTED_IPS=0.0.0.0/0,::/0"     \ # 使用IP-restriction插件時,保證真實IP地址的傳遞
    -e "KONG_REAL_IP_HEADER=X-Forwarded-For" \ # 使用IP-restriction插件時,保證真實IP地址的傳遞
    -p 8000:8000 \
    -p 8443:8443 \
    -p 8001:8001 \
    -p 8444:8444 \
    kong:2.0.1

配置NGINX

Kong部署了集羣后,使用NGINX做負載均衡,爲提高可用性,防止進羣中某臺服務宕機,NGINX對服務啓用健康檢查。

NGINX本身就具有被動檢查,如下配置:

upstream kong_cluster{
    server 172.17.23.14:80  max_fails=1 fail_timeout=10s;
}

server {
    listen 80;
    server_name example.com; 
    location / {
      proxy_pass         http://kong_cluster;
    }
}

但是我這裏使用的主動健康檢查。

主動健康檢查需要安裝三方插件,這裏使用的是淘寶開發的第三方模塊,upstream_check模塊,需要重新編譯NGINX。

我的操作系統中本身已經有了nginx1.10。所以我要重新編譯一下,將三方模塊編譯進去
參考資料

# git clone http://github.com/yaoweibin/nginx_upstream_check_module
  • 重新下載NGINX1.16.1
# 下載NGINX  1.16.1
wget http://nginx.org/download/nginx-1.16.1.tar.gz
## 因爲我的操作系統中本身已經安裝了nginx,所以我要重新安裝,查看原來安裝的命令,並把擴展的check模塊編譯到nginx中
# 查看原安裝的命令
nginx -V 
# ubuntu系統下,安裝nginx依賴包
# zlib依賴包
sudo apt-get install zlib1g-dev
# openssl依賴包
sudo apt-get install openssl libssl-dev
# 安裝pcre依賴包
sudo apt-get install libpcre3 libpcre3-dev
# 安裝 libxml2/libxslt
sudo apt-get install libxslt1-dev
sudo apt-get install libxml2-dev
# 安裝GD依賴包
sudo apt-get install libgd-dev
# 安裝 GeoIP依賴包
sudo apt-get install libgeoip-dev
# 重新安裝下載的nginx1.16.1
tar zxvf nginx-1.16.1.tar.gz
cd nginx-1.16.1
# 下載三方模塊並打補丁  根據nginx版本選定補丁
patch -p1 < /root/install/nginx_upstream_check_module/check_1.16.1+.patch
# 補丁打完之後,安裝nginx
./configure \
--with-cc-opt='-g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2' \
--with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now' \
--prefix=/usr/share/nginx \
--conf-path=/etc/nginx/nginx.conf \
--http-log-path=/var/log/nginx/access.log \
--error-log-path=/var/log/nginx/error.log \
--lock-path=/var/lock/nginx.lock \
--pid-path=/run/nginx.pid \
--http-client-body-temp-path=/var/lib/nginx/body \
--http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
--http-proxy-temp-path=/var/lib/nginx/proxy \
--http-scgi-temp-path=/var/lib/nginx/scgi \
--http-uwsgi-temp-path=/var/lib/nginx/uwsgi \
--with-debug \
--with-pcre-jit \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-http_auth_request_module \
--with-http_addition_module \
--with-http_dav_module \
--with-http_geoip_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_image_filter_module \
--with-http_v2_module \
--with-http_sub_module \
--with-http_xslt_module \
--with-stream \
--with-stream_ssl_module \
--with-mail \
--with-mail_ssl_module \
--with-threads \
--with-pcre \
--add-module=/root/install/nginx_upstream_check_module
# 編譯安裝
make
# 如果是已經安裝過了,只需要make
# 然後將生成nginx拷貝到原來的目錄
cp objs/nginx /usr/sbin/nginx

部署nginx

# 配置https
upstream kong_cluster{
    # 最簡單的輪詢方式
    server 172.17.23.17:8000;
    server 172.17.23.14:8000;
    
    # 配置三方模塊主動檢查健康狀態,檢查TCP,只要確保Kong服務沒有斷掉即可,實際的服務健康檢查使用Kong插件檢查
    # 其他配置參考 http://tengine.taobao.org/document_cn/http_upstream_check_cn.html
    check interval=3000 rise=2 fall=3 timeout=1000 type=tcp;
}

server {
    listen 443 ssl;
    server_name example.com; # 更換自己的域名信息

    ssl_certificate   cert/214533299050973.pem;  # 更換成自己的證書信息
    ssl_certificate_key  cert/214533299050973.key; # 更換成自己的證書信息
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;

    # https 因爲安全原因,需要禁用 gzip
    # 但是在一些場景下,不需要禁用
    # gzip off;
    gzip on;
    # 設置允許壓縮的頁面最小字節數
    gzip_min_length 1000;
    gzip_buffers 4 16k;
    gzip_http_version 1.1;
    # 1~9,默認爲1,數值越大,壓縮率越高,CPU佔用越多,時間越久
    gzip_comp_level 3;
    gzip_vary on;
    # 禁用對 IE 6 使用 gzip 壓縮
    gzip_disable "MSIE [1-6]\.";
    gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/javascript "application/javascript; charset=utf-8" application/xml application/xml+rss application/json "application/json; charset=utf-8" font/ttf font/otf image/svg+xml;

    # 設置最大允許的POST數據量,如果提交的文件超過這個值,會出現413錯誤
    client_max_body_size 20m;
    keepalive_timeout 15;

    # 不顯示 nginx 的版本號
    server_tokens off;

    # 設置請求頭的長度大小
    client_header_buffer_size 32k;
    large_client_header_buffers 4 32k;


    ## Individual nginx logs
    access_log  /var/log/nginx/kong_access.log;
    error_log   /var/log/nginx/kong_error.log;

    location ^~ /robots.txt {
        expires 30d;
        # add_header Content-Type text/plain;
        return 200 "User-Agent:  *\nDisallow:  /";
    }

    location ~ /(\w+) {
        # websocket 支持
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        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; # 真實地址傳遞,IP限制插件使用
        proxy_pass http://kong_cluster;
        expires -1;
    }

    # 主頁返回 404
    location = / {
        return 404;
    }
}

server {
   listen 80;
   server_name  example.com; # 更換自己的域名信息
   #告訴瀏覽器有效期內只准用 https 訪問
   add_header Strict-Transport-Security max-age=15768000;
   #永久重定向到 https 站點
   return 301 https://$server_name$request_uri;
}

重啓NGINX

systemctl restart nginx

將API配置到Kong

Kong常用組件說明

  • Service
    Service是定義的服務,通過Kong轉發後根據請求的協議,host,method,path匹配到實際的服務地址。
    
    Service可以與Route進行關聯,一個Service可以有很多Route, Route匹配後就會轉發到Service,
    處理過程中也會通過Plugin的處理,增加或者減少一些相應的Header或者其他信息
    
    Service可以是一個實際的地址,也可以是Kong內部提供的Upstream組件關聯,由Upstream將請求轉發到實際的服務。
  • Route:
    Route就是路由,實際就是我們通過定義一些規則來匹配客戶端的請求,每個路由都會關聯一個Service,
    並且Service可以關聯多個Route,當匹配到客戶端的請求時,每個請求都會被代理到其配置的Service中
    
    Route作爲客戶端的入口,通過將Route和Service的鬆耦合,可以通過hosts path等規則的配置,最終讓請求轉發到不同的Service。
    
    例如,我們規定api.example.com 和 api.service.com的登錄請求都能夠代理到123.11.11.11:8000端口上,那我們可以通過hosts和path來路由
    
    首先,創建一個Service s1,其相應的host和port以及協議爲http://123.11.11.11:8000
    然後,創建一個Route,關聯的Service爲s1,其hosts爲[api.service.com, api.example.com],path爲login
    最後,將域名api.example.com和api.service.com的請求轉到到我們的Kong集羣上,也就是我們上面一節中通過Nginx配置的請求地址
    那麼,當我們請求api.example.com/login和api.service.com/login時,其通過Route匹配,然後轉發到Service,最終將會請求我們自己的服務。
 
  • Upstream
    這是指您自己的API /服務位於Kong後面,客戶端請求被轉發到該服務器。
    
    相當於Kong提供了一個負載的功能,基於Nginx的虛擬主機的方式做的負載功能
    
    當我們部署集羣時,一個單獨的地址不足以滿足我們的時候,我們可以使用Kong的upstream來進行設置
    
    首先在service中指定host的時候,可以指定爲我們的upstream定義的hostname
    
    我們在創建upstream時指定名字,然後指定solts(暫時不確定具體作用),upstream可以進行健康檢查等系列操作。這裏先不開啓(還沒有研究)
    
    然後我們可以再創建target類型,將target綁定到upstream上,那麼基本上我們部署集羣時,也可以使用
  • Target
    target 就是在upstream進行負載均衡的終端,當我們部署集羣時,需要將每個節點作爲一個target,並設置負載的權重,當然也可以通過upstream的設置對target進行健康檢查。
    當我們使用upstream時,整個路線是 Route >> Service >> Upstream >> Target 
  • Consumer
    Consumer 可以代表一個服務,可以代表一個用戶,也可以代表消費者,可以根據我們自己的需求來定義
    
    可以將一個Consumer對應到實際應用中的一個用戶,也可以只是作爲一個Service的請求消費者
    
    Consumer具體可以在Plugin使用時再做深入瞭解
  • Plugin
    在請求被代理到上游API之前或之後執行Kong內的動作的插件。
    
    例如,請求之前的Authentication或者是請求限流插件的使用
    
    Plugin可以和Service綁定,也可以和Route以及Consumer進行關聯,也可以是全局的插件。

    常用的限流插件,IP限制插件等。
    

Kong使用

主要使用4個組件Service,Route,Upstream和Target

Target配置的就是服務節點,不過要將其關聯到到Upstream。

Upstream可以對Target做負載均衡和健康檢查等,通過設置name於Service關聯,Service匹配到請求時轉發到相應的Upstream。

Service根據業務設置不同的服務,Service和Route綁定獲取實際請求,通過protocol,host,port和path匹配請求,並將匹配到的請求轉發到Upstream。

Route是Kong中實際的入口點。主要根據protocols,hosts,methods,paths設置,匹配請求,將請求轉發到相應的服務上

我們按照這個順序,依次創建Service,Route,Upstream和Target

配置Service

Service還有一些其他參數,可根據具體需求定製。官網資料

  • 添加命令
curl -i -X POST \
    --url http://localhost:8001/services/ \
    --data 'name=example-service' \
    --data 'protocol=http' \
    --data 'host=api.example.service'
    
# host屬性在創建Upstream時使用
  • 添加結果
{
    "host": "api.example.service",
    "created_at": 1582626079,
    "connect_timeout": 60000,
    "id": "71b28160-15fc-4215-a68f-d0b607e9cb9f",
    "protocol": "http",
    "name": "example-service",
    "read_timeout": 60000,
    "port": 80,
    "path": null,
    "updated_at": 1582626079,
    "retries": 5,
    "write_timeout": 60000,
    "tags": null,
    "client_certificate": null
}
  • 修改命令
curl -i -X PATCH \
    --url http://localhost:8001/services/71b28160-15fc-4215-a68f-d0b607e9cb9f/ \
    --data 'name=test-kong' \
    --data 'protocol=http'  \
    --data 'host=test.kong.service'
  • 修改結果
{
    "host": "test.kong.service",
    "created_at": 1582626079,
    "connect_timeout": 60000,
    "id": "71b28160-15fc-4215-a68f-d0b607e9cb9f",
    "protocol": "http",
    "name": "test-kong",
    "read_timeout": 60000,
    "port": 80,
    "path": null,
    "updated_at": 1582626120,
    "retries": 5,
    "write_timeout": 60000,
    "tags": null,
    "client_certificate": null
}

配置Route

Route還有一些其他參數,可根據具體需求定製。官網資料

  • 添加命令

實際使用時,將hosts變更爲實際的域名

curl -i -X POST \
    --url http://localhost:8001/routes/ \
    --data 'protocols[]=http' \
    --data 'protocols[]=https' \
    --data 'methods[]=GET' \
    --data 'methods[]=POST' \
    --data 'methods[]=DELETE' \
    --data 'methods[]=PUT' \
    --data 'hosts[]=api.example.com' \
    --data 'paths[]=/v1/test_kong' \
    --data 'strip_path=false' \ # 此屬性代表是否清除原有的path,默認是清除,如果不需要清除需要再添加的時候指定爲false
    --data 'service.id=71b28160-15fc-4215-a68f-d0b607e9cb9f'
  • 添加結果
{
    "id": "c156de0e-4b08-43a3-bc2d-3f0cf5d5d00a",
    "path_handling": "v0",
    "paths": ["\/v1\/test_kong"],
    "destinations": null,
    "headers": null,
    "protocols": ["http", "https"],
    "methods": ["GET", "POST", "DELETE", "PUT"],
    "snis": null,
    "service": {
        "id": "71b28160-15fc-4215-a68f-d0b607e9cb9f"
    },
    "name": null,
    "strip_path": false,
    "preserve_host": false,
    "regex_priority": 0,
    "updated_at": 1582627872,
    "sources": null,
    "hosts": ["api.example.com"],
    "https_redirect_status_code": 426,
    "tags": null,
    "created_at": 1582627872
}

配置Upstream

  • 添加命令

這裏只配置主動檢查

curl -i -X POST \
    --url http://localhost:8001/upstreams/ \
    --data 'name=test.kong.service'  \
    --data 'algorithm=round-robin' \
    --data 'healthchecks.active.type=http' \
    --data 'healthchecks.active.http_path=/' \
    --data 'healthchecks.active.timeout=2' \
    --data 'healthchecks.active.healthy.successes=3' \
    --data 'healthchecks.active.healthy.interval=10' \
    --data 'healthchecks.active.unhealthy.interval=10' \
    --data 'healthchecks.active.unhealthy.http_failures=3'
  • 添加結果
{
    "created_at": 1582626649,
    "hash_on": "none",
    "id": "1d3638c6-d5a0-4bb5-907b-015c8daf7861",
    "algorithm": "round-robin",
    "name": "test.kong.service",
    "tags": null,
    "hash_fallback_header": null,
    "hash_fallback": "none",
    "hash_on_cookie": null,
    "host_header": null,
    "hash_on_cookie_path": "\/",
    "healthchecks": {
        "threshold": 0,
        "active": {
            "unhealthy": {
                "http_statuses": [429, 404, 500, 501, 502, 503, 504, 505],
                "tcp_failures": 0,
                "timeouts": 0,
                "http_failures": 3,
                "interval": 10
            },
            "type": "http",
            "http_path": "\/",
            "timeout": 2,
            "healthy": {
                "successes": 3,
                "interval": 10,
                "http_statuses": [200, 302]
            },
            "https_sni": null,
            "https_verify_certificate": true,
            "concurrency": 10
        },
        "passive": {
            "unhealthy": {
                "http_failures": 0,
                "http_statuses": [429, 500, 503],
                "tcp_failures": 0,
                "timeouts": 0
            },
            "healthy": {
                "http_statuses": [200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305, 306, 307, 308],
                "successes": 0
            },
            "type": "http"
        }
    },
    "hash_on_header": null,
    "slots": 10000
}

配置target

  • 添加命令
curl -i -X POST \
--url http://localhost:8001/upstreams/1d3638c6-d5a0-4bb5-907b-015c8daf7861/targets \
--data 'target=172.17.23.14:38001'
    
curl -i -X POST \
    --url http://localhost:8001/upstreams/1d3638c6-d5a0-4bb5-907b-015c8daf7861/targets \
    --data 'target=172.17.23.14:38002'

  • 添加結果
{
    "created_at": 1582627463.954,
    "upstream": {
        "id": "1d3638c6-d5a0-4bb5-907b-015c8daf7861"
    },
    "id": "66c83e7d-f006-43da-990b-d493a966fc66",
    "target": "172.17.23.14:38001",
    "weight": 100
}

{
    "created_at": 1582627517.872,
    "upstream": {
        "id": "1d3638c6-d5a0-4bb5-907b-015c8daf7861"
    },
    "id": "d9940f44-30ad-437a-aaab-836dfaf27b93",
    "target": "172.17.23.14:38002",
    "weight": 100
}
檢查配置kong是否可行
  • 檢查配置的請求
curl -X GET https://example.com/v1/test_kong

{"code": 200, "message": "test kong get success"}

curl -X POST https://example.com/v1/test_kong

{"code": 200, "message": "test post get success"}

curl -X DELETE https://example.com/v1/test_kong

{"code": 200, "message": "test kong delete success"}

curl -X PUT https://example.com/v1/test_kong

{"code": 200, "message": "test kong put success"}

  • 檢查配置的健康檢查

看到服務配置的東西一直在被檢查

[I 200225 19:03:10 web:2246] 200 GET / (172.17.23.14) 0.88ms
[I 200225 19:03:20 web:2246] 200 GET / (172.17.23.17) 1.34ms
[I 200225 19:03:20 web:2246] 200 GET / (172.17.23.14) 0.67ms
[I 200225 19:03:30 web:2246] 200 GET / (172.17.23.14) 0.88ms
[I 200225 19:03:30 web:2246] 200 GET / (172.17.23.17) 1.02ms
[I 200225 19:03:40 web:2246] 200 GET / (172.17.23.14) 0.88ms
[I 200225 19:03:40 web:2246] 200 GET / (172.17.23.17) 1.15ms

Kong插件使用

插件的作用範圍,從小到大可以是 Route,Service,Consumer,Global。

可以根據需求使用一些插件,比如做一些限流操作,IP白名單和IP黑名單等

下面是Kong官方就提供的開源版可用的插件

Rate Limiting 限流插件

限流插件,限制在指定時間內可以訪問的次數,可以是幾秒內,幾分鐘內,幾小時,幾天等。
插件可以設置在Service,Route或者全局,如果啓用Consumer,則根據Consumer來計算限制時間,否則根據客戶端IP地址

給定service添加插件

curl -X POST http://localhost:8001/services/71b28160-15fc-4215-a68f-d0b607e9cb9f/plugins \
    --data "name=rate-limiting"  \
    --data "config.second=2" \
    --data "config.hour=10000" \
    --data "config.policy=local" \
    --data "config.limit_by=ip"

添加結果

{
    "created_at": 1582637075,
    "config": {
        "minute": null,
        "policy": "local",
        "month": null,
        "redis_timeout": 2000,
        "limit_by": "consumer",
        "hide_client_headers": false,
        "second": 5,
        "day": null,
        "redis_password": null,
        "year": null,
        "redis_database": 0,
        "hour": 10000,
        "redis_port": 6379,
        "redis_host": null,
        "fault_tolerant": true
    },
    "id": "9f8d08c9-4bdf-4912-b4dd-f96b32e38019",
    "service": {
        "id": "71b28160-15fc-4215-a68f-d0b607e9cb9f"
    },
    "enabled": true,
    "protocols": ["grpc", "grpcs", "http", "https"],
    "name": "rate-limiting",
    "consumer": null,
    "route": null,
    "tags": null
}

測試效果

def test_rate_limiting():
    url = "https://example.com/v1/test_kong"
    def _request(idx):
        print("request {} start by : {}".format(idx, datetime.datetime.now()))
        print("request {} result >>>".format(idx))
        resp = requests.get(url)
        print("request {} status_code {} ".format(idx, resp.status_code))
        print("request {} json {} ".format(idx, resp.json()))
        print("request {} result <<<".format(idx))
        print("request {} end by : {}".format(idx, datetime.datetime.now()))

    for i in range(0, 8):
        t = threading.Thread(target=_request, args=(i, ))
        t.start()

執行結果

request 3 status_code 200
request 3 json {'message': 'test kong get success', 'code': 200}
request 3 result <<<
request 3 end by : 2020-02-25 21:45:18.566325
request 6 status_code 429
request 6 json {'message': 'API rate limit exceeded'}
request 5 status_code 429
IP Restriction

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

IP Restriction插件可以作用於Service, Route, Consumer, 也可以作用於全局。
我們現在將其作用於Service上

curl -X POST http://localhost:8001/services/71b28160-15fc-4215-a68f-d0b607e9cb9f/plugins \
--data "name=ip-restriction" \
--data "config.whitelist=172.17.23.14" \
--data "config.whitelist=172.17.23.17"

添加結果

{
    "created_at": 1582980903,
    "config": {
        "whitelist": ["172.17.23.14", "172.17.23.17"],
        "blacklist": null
    },
    "id": "c0364fe4-2d43-451e-9330-b45964cf1483",
    "service": {
        "id": "71b28160-15fc-4215-a68f-d0b607e9cb9f"
    },
    "enabled": true,
    "protocols": ["grpc", "grpcs", "http", "https"],
    "name": "ip-restriction",
    "consumer": null,
    "route": null,
    "tags": null
}

測試結果

{"message":"Your IP address is not allowed"}
Zipkin

Zipkin是分佈式跟蹤系統,我們使用者插件的目的是來監控我們的API耗時情況,尤其是可以根據這些結果來跟蹤相應的性能消耗。

所以我們先部署Zipkin服務,直接使用Docker安裝。

zipkin具體信息,查看官網

部署Zipkin
docker run -d -p 9411:9411 openzipkin/zipkin
添加插件

zipkin插件可以作用於Service, Route, Consumer, 也可以作用於全局。
我們現在將其作用於Service上

curl -X POST http://localhost:8001/services/71b28160-15fc-4215-a68f-d0b607e9cb9f/plugins \
    --data "name=zipkin"  \
    --data "config.http_endpoint=http://172.17.23.14:9411/api/v2/spans" \
    --data "config.sample_ratio=1" \   # 全部採樣
    --data "config.include_credential=true"

添加結果

{
    "created_at": 1582812902,
    "config": {
        "sample_ratio": 1,
        "http_endpoint": "http:\/\/172.17.23.14:9411\/api\/v2\/spans",
        "include_credential": true,
        "default_service_name": null
    },
    "id": "c58654d8-2fd5-43b6-854f-0fd65a353f7e",
    "service": {
        "id": "71b28160-15fc-4215-a68f-d0b607e9cb9f"
    },
    "enabled": true,
    "protocols": ["grpc", "grpcs", "http", "https"],
    "name": "zipkin",
    "consumer": null,
    "route": null,
    "tags": null
}

http_endpoint:zipkin的服務地址

sample_ratio:採集的頻率 設置爲1時全部採集,設置爲0時,全部不採集, 設置爲0-1之間的小數,則會採
用算法按照設定的參數隨機採集

測試插件使用效果:
訪問配置的地址之後,前面設置的採集頻率是1,表示全部採集,訪問之後登陸zipkin的地址http://172.17.23.14:9411/zipkin
在這裏插入圖片描述

HMAC Authentication

HMAC簽名身份驗證,Kong網關簽名驗證通過後,纔可以向後請求,實際應用場景聽多,具體流程如下:

  1. 創建消費者Consumer
  2. 給消費Consumer創建插件HMAC對應的username和secret,客戶端使用其簽名
  3. 在Service或者Route中啓用HMAC插件
  4. 客戶端在請求時,根據前面返回的username和secret簽名,Kong網關匹配後進行簽名驗證
  • 創建消費者

創建命令

curl -i http://localhost:8001/consumers/ -d "username=test_hmac&custom_id=test_hmac_id"

創建結果

{
    "custom_id": "test_hmac_id",
    "created_at": 1583071044,
    "id": "c9a4ad0e-174f-4a7f-81b0-ce9e3261c177",
    "tags": null,
    "username": "test_hmac"
}
  • 創建消費者相應HMAC插件信息
curl -X POST http://localhost:8001/consumers/test_hmac/hmac-auth \
--data "username=test_hmac" \
--data "secret=test_hmac_secret123"

username:在HMAC簽名驗證中使用的用戶名。
secret: 在HMAC簽名驗證中使用的祕鑰。

創建結果

{
    "created_at": 1583071212,
    "consumer": {
        "id": "c9a4ad0e-174f-4a7f-81b0-ce9e3261c177"
    },
    "id": "834a8f69-c63d-4183-8a50-b52ab6a6187e",
    "tags": null,
    "secret": "test_hmac_secret123",
    "username": "test_hmac"
}
  • 啓用HMAC插件

先把之前的Service上的IP Restriction插件去掉。否則我本地和服務器上的測試會提示無法處理

根據之前創建插件產生的ID,直接刪除即可。

curl -i -X DELETE http://localhost:8001/plugins/c0364fe4-2d43-451e-9330-b45964cf1483

把插件添加到指定的Route或者Service都可以,這次把這個插件加到Route上,也來測試一遍

添加命令

curl -i -X POST http://localhost:8001/routes/c156de0e-4b08-43a3-bc2d-3f0cf5d5d00a/plugins \
      -d "name=hmac-auth" \
      -d "config.enforce_headers=date" \
      -d "config.algorithms=hmac-sha1" \
      -d "config.algorithms=hmac-sha256" \
      -d "config.validate_request_body=true"
enforce_headers: Header中必須要有的參數,後續用於簽名驗證

algorithms:用戶想要支持的HMAC摘要算法列表。允許的值是hmac-sha1,hmac-sha256,
hmac-sha384,和hmac-sha512客戶端請求時,從中選擇一個即可

validate_request_body:啓用驗證請求體,設置爲true之後,客戶端請求頭中若是有Digest,
Kong則會使用HMAC算法驗證請求體body。

添加結果

{
    "created_at": 1583072768,
    "config": {
        "clock_skew": 300,
        "validate_request_body": true,
        "enforce_headers": ["date"],
        "algorithms": ["hmac-sha1", "hmac-sha256"],
        "anonymous": null,
        "hide_credentials": false
    },
    "id": "39188d02-5425-4f76-8795-5e028f793476",
    "service": null,
    "enabled": true,
    "protocols": ["grpc", "grpcs", "http", "https"],
    "name": "hmac-auth",
    "consumer": null,
    "route": {
        "id": "c156de0e-4b08-43a3-bc2d-3f0cf5d5d00a"
    },
    "tags": null
}
  • 客戶端請求測試
重要請求頭信息
1. Authorization 或者 Proxy-Authorization
    這個頭的組成是
    credentials 簽名算法  使用HMAC插件,固定值就是hmac
    params 具體的參數,參數用","分割。
        algorithm: 簽名算法 
        headers: 簽名計算包括的key 
        signature: 生成的簽名,生成規則 
2. Digest:啓用validate_request_body,需要生成
3. Date:必須是GMT時間格式

Python測試函數

def test_hmac_authentication():
    # 創建消費者對應HMAC插件時的用戶名和祕鑰
    hmac_username = "test_hmac"
    hmac_secret = "test_hmac_secret123"

    url = "https://example.com/v1/test_kong"

    # 請求頭
    # 注意,請求頭的時間格式必須是GMT時間格式
    request_headers = {
        "Authorization": "",
        "Digest": "",
        "Date": datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT")
    }

    # 請求體
    request_body = {
        "data": {"test": "hmac"}
    }

    # 生成Digest
    #  body="A small body"
    #  digest=SHA-256(body)
    #  base64_digest=base64(digest)
    #  Digest: SHA-256=<base64_digest>
    base64_digest = base64.b64encode(hashlib.sha256(json.dumps(request_body).encode('utf-8')).digest()).decode('utf-8')
    request_headers['Digest'] = "SHA-256={}".format(base64_digest)

    # 生成簽名
    # signing_string = "date: Thu, 22 Jun 2017 17:15:21 GMT\nGET /requests HTTP/1.1"
    # digest = HMAC - SHA256( < signing_string >, "secret")
    # base64_digest = base64( < digest >)
    # 簽名字符串構造
    # 如果參數不是request-line,則名字用小寫,然後跟":"和一個空格" ",
    # 如果參數是request-line,則追加http請求行【GET /requests HTTP/1.1】
    # 中間用換行符"\n"將所有的參數連接起來。

    # 按照上述規則拼接字符串
    sign_string = "date: {}\ndigest: {}".format(request_headers['Date'], request_headers['Digest'])

    # 開始簽名
    sign_digest = hmac.new(hmac_secret.encode("utf-8"), sign_string.encode('utf-8'), digestmod='sha256').digest()
    sign_base64_digest = base64.b64encode(sign_digest).decode('utf-8')

    # 構建Authorization參數 具體信息 https://docs.konghq.com/hub/kong-inc/hmac-auth/#signature-authentication-scheme
    authorization = 'hmac username="{}", algorithm="hmac-sha256", headers="date digest", signature="{}"'.format(
        hmac_username, sign_base64_digest)

    request_headers['Authorization'] = authorization
    resp = requests.post(url, headers=request_headers, json=request_body)
    print(request_headers)
    print(resp.status_code)
    print(resp.json())

測試結果

# 失敗
401
{'message': 'HMAC signature cannot be verified'}

# 成功
200
{'code': 200, 'message': 'test kong post success'}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章