初探Kong网关(api权限控制,防刷)

Posted by laosuan on December 31, 2020

前言

当我们应用的接口不需要密钥既可访问时,我们的接口容易被人抓包,数据容易被人爬取. 当我们应用的接口没有限制访问频率,手机短信验证码等成本较高的服务容易被人恶意攻击.Kong是一款开源的云原生架构下的分布式 API 网关,其性能和可扩展性在同类组件中,表现都很优异.Kong 官方提供了很多直接可用的插件,本文详细记录了kong的部署搭建过程以及使用官方插件实现api权限控制,接口访问频率限制的功能.

以下主要介绍kong的基本概念,部署配置,权限控制和限流的插件使用

1 基本概念

Features in getting started guide

Admin API(动态修改配置):Kong Gateway带有一个内部restfulapi,用于管理目的。API命令可以在集群中的任何节点上运行,并且配置将一致地应用于所有节点。

Consumers(用户,群组,版本): Consumer的核心原则是您可以为其添加插件,从而自定义他的请求行为. 所以,或许您会有一个手机APP应用,并为他的每个版本都定义一个consumer, 又或者您有一个应用或几个应用,并为这些应用定义统一个consumer,这些都无所谓.

Routes(相当于nginx配置中的location,url匹配): 路由是进入kong的入口,为匹配的请求定义规则,在Kong中定义细粒度的入口点,从而引导您的访问到不同接口服务.

Service (相当于nginx配置中的server):服务实体是您自己的每个上游服务的抽象。 Service 可以与下面的Route进行关联,一个Service可以有很多Route,匹配到的Route就会转发到Service中.

Plugins(相当于nginx配置中的脚本):插件提供了一个模块化的系统来修改和控制网关的功能。例如,为了保护您的API,您可能需要一个访问密钥,您可以使用keyauth插件设置该密钥。插件提供了广泛的功能,包括访问控制、缓存、速率限制、修改请求体、日志记录等等。

2 部署

初始化数据库:

sudo git clone https://github.com/mrts/docker-postgresql-multiple-databases
cd docker-postgresql-multiple-databases
sudo docker build -t="postgresql:multi" .
sudo docker run --name=pgtemp -e POSTGRES_PASSWORD=kong -e POSTGRES_MULTIPLE_DATABASES=kong,konga -v "$PWD/../data:/var/lib/postgresql/data" postgresql:multi
#可选择检查是否在初始化时成功创建两个数据库
#docker exec -it pgtemp sh 
#psql kong kong
#postgres=# \l
sudo docker rm -f pgtemp

cd ../

sudo touch docker-compose.yml

配置docker-compose.yml:

version: '3.7'

volumes:
  kong_data: {}

networks:
  kong-net:
    external: false

services:
  kong-migrations:
    image: "${KONG_DOCKER_TAG:-kong:latest}"
    command: kong migrations bootstrap
    depends_on:
      - db
    environment:
      KONG_DATABASE: postgres
      KONG_PG_DATABASE: kong
      KONG_PG_HOST: db
      KONG_PG_USER: postgres
      KONG_PG_PASSWORD: 密码

    networks:
      - kong-net
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure

  kong-migrations-up:
    image: "${KONG_DOCKER_TAG:-kong:latest}"
    command: kong migrations up && kong migrations finish
    depends_on:
      - db
    environment:
      KONG_DATABASE: postgres
      KONG_PG_DATABASE: kong
      KONG_PG_HOST: db
      KONG_PG_USER: postgres
      KONG_PG_PASSWORD: 密码
    networks:
      - kong-net
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure

  kong:
    image: "${KONG_DOCKER_TAG:-kong:latest}"
    user: "${KONG_USER:-kong}"
    depends_on:
      - db
    environment:
      KONG_ADMIN_LISTEN: '0.0.0.0:8001'
      KONG_CASSANDRA_CONTACT_POINTS: db
      KONG_DATABASE: postgres
      KONG_PG_DATABASE: kong
      KONG_PG_HOST: db
      KONG_PG_USER: postgres
      KONG_PG_PASSWORD: 密码
	    KONG_LOG_LEVEL: debug
	    KONG_REAL_IP_HEADER: X-Forwarded-For
      KONG_TRUSTED_IPS: 172.20.0.1

    networks:
      - kong-net
    ports:
      - "8000:8000/tcp"
      - "0.0.0.1:8001:8001/tcp"
      - "8443:8443/tcp"
      - "0.0.0.1:8444:8444/tcp"
    healthcheck:
      test: ["CMD", "kong", "health"]
      interval: 10s
      timeout: 10s
      retries: 10
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure
    volumes:
      - ./logs:/usr/local/kong/logs

  konga:
    image: pantsel/konga
    environment:
      DB_ADAPTER: postgres
      DB_HOST: db
      DB_PORT: 5432
      DB_DATABASE: konga
      DB_USER: progress
      DB_PASSWORD: 密码
      NODE_ENV: ${KONGA_ENV}
      KONGA_HOOK_TIMEOUT: 10000
    restart: on-failure
    ports:
      - 8080:1337
    depends_on:
      - db
    networks:
      - kong-net
    
  db:
    image: postgres:9.6
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=密码
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 30s
      timeout: 30s
      retries: 3
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure
    stdin_open: true
    tty: true
    networks:
      - kong-net
    ports:
      - 5432:5432
    volumes:
      - ./postgresql/data:/var/lib/postgresql/data

启动:

sudo curl -L "https://github.com/docker/compose/releases/download/1.28.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo docker-copmose up

3 konga管理UI界面介绍

demo地址:lab.liuxuan.tech

登陆:kong kongkong

3.1 配置kong-admin url

konga管理控制台界面需要指定kong-admin url后才能正常工作,下面是配置的过程:

1 在konga管理后台的菜单列表点击CONNECTIONS

QQ20210127-104154@2x

2 点击+NEW CONNECTIOIN按钮

QQ20210127-104602@2x

3 填写kong-admin url信息

name可任意填写:kong

kong Admin URL填写部署应用时配置的kong-admin url:http://kong:8002

点击 UPDATE CONNECTION 按钮

QQ20210127-110922@2x

再点击ACTIVE按钮

QQ20210127-110949@2x

3.1.1 拓展:admin url安全性配置

Kong-admin,此API是为内部使用而设计的,并提供对kong的完全控制,因此在设置kong环境时应小心,以避免此API的不当公开暴露。以下有三种方式:

1 admin api指定特定ip访问

admin_listen = 127.0.0.1:8001

avoid binding Kong to all of your interfaces避免设置为向所有ip开放, by using values such as 0.0.0.0:8001.

2 使用iptable指定特定ip访问

$ grep admin_listen /etc/kong/kong.conf
admin_listen 10.10.10.3:8001

# explicitly allow TCP packets on port 8001 from the Kong node itself
# this is not necessary if Admin API requests are not sent from the node
$ iptables -A INPUT -s 10.10.10.3 -m tcp -p tcp --dport 8001 -j ACCEPT

# explicitly allow TCP packets on port 8001 from the following addresses
$ iptables -A INPUT -s 10.10.10.4 -m tcp -p tcp --dport 8001 -j ACCEPT
$ iptables -A INPUT -s 10.10.10.5 -m tcp -p tcp --dport 8001 -j ACCEPT

# drop all TCP packets on port 8001 not in the above IP list
$ iptables -A INPUT -m tcp -p tcp --dport 8001 -j DROP

3 将admin-url作为服务管理权限控制

curl -X POST http://localhost:8001/services \  --data name**=**my-service \  --data url**=**http://example.com/some_api

3.2 配置services

新建services,相当于nginx中的server,主要用来区分url

QQ20210127-111333@2x

QQ20210127-111504@2x

3.3 配置routers

在service下配置routers,相当于nginx中的server,即配置接口或接口url前缀转发规则

QQ20210127-111648@2x

QQ20210127-111727@2x

至此,基本的配置已经完毕,请求curl https://www.liuxuan.tech/baike/api/healthcheck/readiness 即可转发至

http://testask.gf.com.cn/baike/api/healthcheck/readiness

4 配置插件

4.1 鉴权

4.1.1开启Hmac Auth鉴权

使用场景:

1 提供给其他服务的外部接口增加鉴权

2 包含敏感信息或重要数据的接口增加鉴权

QQ20210127-112947@2x

QQ20210127-113032@2x

QQ20210127-113103@2x

  • 参数
参数 描述
name 插件名称、此处为hmac-auth
service_id 绑定的服务Id
route_id 绑定的路由Id
enabled 是否启用该插件,默认是true
config.hide_credentials 是否隐藏请求中的凭证信息(如Authorization头),默认是false
config.clock_skew 时间偏移量,默认是300s
config.anonymous 在验证失败后是否启用匿名消费者,值可以配置为消费者Id
config.validate_request_body 是否启用Body体校验,默认是false
config.enforce_headers 请求中必须包含的头部信息
config.algorithms HMAC 算法支持的列表,默认值是hmac-sha1,hmac-sha256,hmac-sha384,hmac-sha512

lient端实例代码:

https://dzone.com/articles/protect-rest-service-using-0

4.1.2 登陆态鉴权-kong 自研插件实践,广发通 oa token 解析

http://wiki.gf.com.cn/pages/viewpage.action?pageId=89839509

4.2 开启Rate Limiting限频

4.2.1 配置插件

QQ20210127-140424@2x

QQ20210127-140510@2x

4.2.2 如何防欺诈

封禁用户的标记有很多种,最基本的如果用户有登录态的话,就可以直接封账号。如果没有的话,可以通过设备指纹来标识设备,根据平台的不同,设备指纹可以是 PC 端的浏览器指纹,移动端的 IMEI、MAC 地址、UUID 等,或者这些条件综合起来计算出来的 machine key,使之无法造假,然后在关键请求时校验 machine key 的合法性。下面介绍可以快速实现的根据ip地址限频:

请求中几个关于IP的http头信息

  • X-FORWARDED-FOR 并不是标准协议头。当你使用了代理时,web服务器就不知道你的真实IP了,为了避免这个情况,代理服务器通常会增加一个叫做X_Forwarded_For的头信息,把连接它的客户端IP(即你的上网机器IP)加到这个头信息里,这样就能保证网站的web服务器能获取到真实IP。它的格式通常是X-Forwarded-For: client1, proxy1, proxy2。此外,请注意,它也很容易被客户端欺骗。只有当您控制设置标头的代理时,才应该使用此标头

  • RemoteAddr 代表客户端的IP,但它的值不是由客户端提供的,而是服务端根据客户端的IP指定的,访问某个网站时,假设中间没有任何代理,那么网站的web服务器(NginxApache)就会把remote_addr设为你的机器IP,如果你用了某个代理,那么你的浏览器会先访问这个代理,然后再由这个代理转发到网站,这样web服务器就会把remote_addr设为这台代理机器的IP。RemoteAddr基本上不能被伪造,因为是直接从 TCP 连接信息中获取的.

    关于伪造remoteAddr:https://yonghaowu.github.io/2018/11/23/get_reql_ip/

获取ip的方案:如果http请求经过代理,那么就从X-Forwarded-For中获取,如果没有经过代理,那就从REMOTE_ADDR中获取。由于我们部署服务所处的网络环境基本都被代理,故用X-Forwarded-For来获取客户端的ip.

在kong的配置文件or启动参数中加入:

#通过X-Forwarded-For请求头获取真实ip,默认是通过RemoteAddr
real_ip_header X-Forwarded-For;
#信任ip名单;配置后kong会把X-Forwarded-For中最后一个不是信任服务器的IP当成真实IP
real_ip_from [trusted proxy addresses or cidr blocks]

拓展:谈谈 Kong rate-limiting 插件中的缺陷-Redis 高频卡控中的 Race Conditions 问题 https://ms2008.github.io/2018/04/04/kong-rate-limiting/

refference:

[github issues] KONG如何配置X-Forwarded-For参数代替RemoteAddr

https://github.com/Kong/kong/issues/2020

https://discuss.konghq.com/t/how-to-forward-clients-request-ip/384