背景
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網關簽名驗證通過後,纔可以向後請求,實際應用場景聽多,具體流程如下:
- 創建消費者Consumer
- 給消費Consumer創建插件HMAC對應的username和secret,客戶端使用其簽名
- 在Service或者Route中啓用HMAC插件
- 客戶端在請求時,根據前面返回的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'}