Kubernetes (K8S)中APISIX高级使用

虚拟化运维Kubernetes

Kubernetes (K8S)中APISIX高级使用

王先森2023-10-092023-10-09

重定向-redirect

官方文档

现在当我们访问 http://whoami.boysec.cn/tls 或者 http://whoami.boysec.cn/tls/ 的时候都可以得到正常的结果,一般来说我们可能希望能够统一访问路径,比如访问 /tls 子路径的时候可以自动跳转到 /tls/ 以 Splash 结尾的路径上去。同样要实现该需求我们只需要使用一个名为 redirect 的插件即可,该插件是 URI 重定向插件,可配置的属性如下所示:

名称

类型

必选项

默认值

有效值

描述

http_to_https

boolean

false

[true,false]

当设置为 true 并且请求是 HTTP 时,它将被重定向具有相同 URI 和 301 状态码的 HTTPS,原 URI 的查询字符串也将包含在 Location 头中。

uri

string

要重定向到的 URI,可以包含 NGINX 变量。例如:/test/index.htm,$uri/index.html,${uri}/index.html,https://example.com/foo/bar。如果你引入了一个不存在的变量,它不会报错,而是将其视为一个空变量。

regex_uri

array[string]

将来自客户端的 URL 与正则表达式匹配并重定向。当匹配成功后使用模板替换发送重定向到客户端,如果未匹配成功会将客户端请求的 URI 转发至上游。和 regex_uri 不可以同时存在。例如:[“^/iresty/(.)/(.)/(.*)”,”/$1-$2-$3”] 第一个元素代表匹配来自客户端请求的 URI 正则表达式,第二个元素代表匹配成功后发送重定向到客户端的 URI 模板。

ret_code

integer

302

[200, …]

HTTP 响应码

encode_uri

boolean

false

[true,false]

当设置为 true 时,对返回的 Location Header 按照 RFC3986 的编码格式进行编码。

append_query_string

boolean

false

[true,false]

当设置为 true 时,将原始请求中的查询字符串添加到 Location Header。如果已配置 uri 或 regex_uri 已经包含查询字符串,则请求中的查询字符串将附加一个&。如果你已经处理过查询字符串(例如,使用 NGINX 变量 $request_uri),请不要再使用该参数以避免重复。

  • http_to_httpsuriregex_uri 只能配置其中一个属性。
  • http_to_https、和 append_query_string 只能配置其中一个属性。
  • 当开启http_to_https时,重定向 URL 中的端口将按如下顺序选取一个值(按优先级从高到低排列)
    • 从配置文件(conf/config.yaml)中读取 plugin_attr.redirect.https_port
    • 如果 apisix.ssl 处于开启状态,读取 apisix.ssl.listen 并从中随机选一个 port
    • 使用 443 作为默认 https port

要实现我们的需求直接使用 regex_uri 这个属性即可,只需要去匹配 /tls 的请求,然后进行跳转即可,更新 ApisixRoute 对象:

代码语言:javascript
复制
cat > ing.yml <<EOF
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: whoami-apisix
spec:
  http:
    - name: whoami-root
      match:
        hosts:
          - whoami.boysec.cn
        paths:
          - '/tls*'
      backends:
        - serviceName: whoami
          servicePort: 80
      plugins:
        - name: redirect
          enable: true
          config:
            regex_uri: ['^(/tls)$', '\$1/']
EOF

我们新启用了一个 redirect 插件,并配置 regex_uri: ["^(/tls)

同样如果我们想要重定向到 https,只需要在该插件下面设置 config.http_to_https=true 即可:

代码语言:javascript
复制
# ... 其他部分省略
        - name: redirect
          enable: true
          config:
            http_to_https: true

证书-tls

通过使用上面的 redirect 插件配置 http_to_https 可以将请求重定向到 https 上去,但是我们现在并没有对我们的 ops.qikqiak.com 配置 https 证书,这里我们就需要使用 ApisixTls 对象来进行证书管理。

我们先使用 openssl 创建一个自签名的证书,当然你有正规 CA 机构购买的证书的话直接将证书下载下来使用即可:

代码语言:javascript
复制
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=whoami.boysec.cn/O=boysec"

然后通过 Secret 对象来引用上面创建的证书文件:

代码语言:javascript
复制
# 要注意证书文件名称必须是 tls.crt 和 tls.key
kubectl create secret tls who-tls --cert=tls.crt --key=tls.key

然后就可以创建一个 ApisixTls 资源对象,引用上面的 Secret 即可:

代码语言:javascript
复制
apiVersion: apisix.apache.org/v2
kind: ApisixTls
metadata:
  name: who-tls
spec:
  hosts:
    - whoami.boysec.cn
  secret:
    name: who-tls
    namespace: default

同时 APISIX TLS 还可以配置 spec.client,用于进行 mTLS 双向认证的配置。上面的资源对象创建完成后,即可访问 https 服务了(chrome 浏览器默认会限制不安全的证书,只需要在页面上输入 thisisunsafe 即可访问了):

URL跳转-proxy-rewrite

官方文档

假设现在有这样一个需求,当访问 http://whoami.boysec.cn/v1 时,流量调度至 whoami。当访问 http://whoami.boysec.cn/v2 时,流量调度至 nginx。这种需求是非常常见的。

创建俩个 ApiSixRoute规则,根据不同的访问路径代理至相对应的 service

Appv1Whoami-IngressAppv1

vim appv1.yml

代码语言:javascript
复制
apiVersion: apps/v1
kind: Deployment
metadata:
  name: appv1
spec:
  selector:
    matchLabels:
      app: appv1
  template:
    metadata:
      labels:
        use: test
        app: appv1
    spec:
      containers:
      - image: nginx:alpine
        name: appv1
        command: ["/bin/sh", "-c", "echo '你好, 这是(王先森)APP-v1服务中心'>/usr/share/nginx/html/index.html;nginx -g 'daemon off;'"]
        ports:
          - containerPort: 80
            name: portv1
---
apiVersion: v1
kind: Service
metadata:
  name: appv1
spec:
  selector:
    app: appv1
  ports:
    - name: http
      port: 80
      targetPort: portv1

vim who-ingress

代码语言:javascript
复制
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: whoami-apisix
spec:
  http:
    - name: whoami-root
      match:
        hosts:
          - whoami.boysec.cn
        paths:
          - '/v1*'
      backends:
        - serviceName: whoami
          servicePort: 80
      plugins:
        - name: proxy-rewrite
          enable: true
          config:
            regex_uri: ['^/v1(/|$)(.*)', '/$2']

vim app-ingress

代码语言:javascript
复制
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: app-apisix
spec:
  http:
    - name: app-root
      match:
        hosts:
          - whoami.boysec.cn
        paths:
          - '/v2*'
      backends:
        - serviceName: appv1
          servicePort: 80
      plugins:
        - name: proxy-rewrite
          enable: true
          config:
            regex_uri: ['^/v2(/|$)(.*)', '/$2']

这里我们启用一个 proxy-rewrite 插件,并且将所有 /v1v2 路径的请求都重写到了 / 路径下:

黑/白名单-ip-restriction

官方文档

ip-restriction 插件可以通过将 IP 地址列入白名单或黑名单来限制对服务或路由的访问。

支持对单个 IP 地址、多个 IP 地址和类似 10.1.1.0/24 的 CIDR(无类别域间路由)范围的限制。

参数名

类型

必选项

默认值

有效值

描述

whitelist

array[string]

加入白名单的 IP 地址或 CIDR 范围。

blacklist

array[string]

加入黑名单的 IP 地址或 CIDR 范围。

message

string

“Your IP address is not allowed”

[1, 1024]

在未允许的 IP 访问的情况下返回的信息。

示例:

拒绝10.1.1.1这个IP访问v2版本

代码语言:javascript
复制
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: app-apisix
spec:
  http:
    - name: app-root
      match:
        hosts:
          - whoami.boysec.cn
        paths:
          - '/v2*'
      backends:
        - serviceName: appv1
          servicePort: 80
      plugins:
        - name: proxy-rewrite
          enable: true
          config:
            regex_uri: ['^/v2(/|$)(.*)', '/$2']
        - name: ip-restriction
          enable: true
          config:
            blacklist: ['10.1.1.1']

在黑名单中IP访问则会出现403。

image-20231012130850212

用户认证

身份认证在日常生活当中是非常常见的一项功能,大家平时基本都会接触到。比如用支付宝消费时的人脸识别确认、公司上班下班时的指纹/面部打卡以及网站上进行账号密码登录操作等,其实都是身份认证的场景体现。

auth

如上图,Jack 通过账号密码请求服务端应用,服务端应用中需要有一个专门用做身份认证的模块来处理这部分的逻辑。请求处理完毕子后,如果使用 JWT Token 认证方式,服务器会反馈一个 Token 去标识这个用户为 Jack。如果登录过程中账号密码输入错误,就会导致身份认证失败。

但是每个应用服务模块去开发一个单独的身份认证模块,用来支持身份认证的一套流程处理,当服务量多了之后,就会发现这些模块的开发工作量都是非常巨大且重复的。这个时候,我们可以通过把这部分的开发逻辑放置到 Apache APISIX 的网关层来实现统一,减少开发量。

apisix auth

如上图所示,用户或应用方直接去请求 Apache APISIX,然后 Apache APISIX 通过识别并认证通过后,会将鉴别的身份信息传递到上游应用服务,之后上游应用服务就可以从请求头中读到这部分信息,然后进行后续的逻辑处理。

Apache APISIX 作为一个 API 网关,目前已开启与各种插件功能的适配合作,插件库也比较丰富。目前已经可与大量身份认证相关的插件进行搭配处理,如下图所示。

API 网关认证插件

基础认证插件比如 Key-AuthBasic-Auth,他们是通过账号密码的方式进行认证。复杂一些的认证插件如 Hmac-AuthJWT-Auth,如 Hmac-Auth 通过对请求信息做一些加密,生成一个签名,当 API 调用方将这个签名携带到 Apache APISIX,Apache APISIX 会以相同的算法计算签名,只有当签名方和应用调用方认证相同时才予以通过。其他则是一些通用认证协议和联合第三方组件进行合作的认证协议,例如 OpenID-Connect 身份认证机制,以及 LDAP 认证等。

Apache APISIX 还可以针对每一个 Consumer (即调用方应用)去做不同级别的插件配置。如下图所示,我们创建了两个消费者 Consumer A、Consumer B,我们将 Consumer A 应用到应用 1,则后续应用 1 的访问将会开启 Consumer A 的这部分插件,例如 IP 黑白名单,限制并发数量等。将 Consumer B 应用到应用 2 ,由于开启了 http-log 插件,则应用 2 的访问日志将会通过 HTTP 的方式发送到日志系统进行收集。

配置灵活

总体说来 APISIX 的认证系统功能非常强大,我们非常有必要掌握。

basic-auth

官方文档

首先我们来了解下最简单的基本认证在 APISIX 中是如何使用的。basic-auth 是一个认证插件,它需要与 Consumer 一起配合才能工作。添加 Basic Auth 到一个 Service 或 Route,然后 Consumer 将其用户名和密码添加到请求头中以验证其请求。

首先我们需要在 APISIX Consumer 消费者中增加 basic auth 认证配置,为其指定用户名和密码,我们这里在 APISIX Ingress 中,可以通过 ApisixConsumer 资源对象进行配置,比如这里我们为前面的 whoami实例应用添加一个基本认证,如下所示:

代码语言:javascript
复制
cat > basic-auth.yaml <<EOF
apiVersion: apisix.apache.org/v2
kind: ApisixConsumer
metadata:
  name: whoamibauth
spec:
  authParameter:
    basicAuth:
      value:
        username: admin
        password: admin321
EOF

ApisixConsumer 资源对象中只需要配置 authParameter 认证参数即可,目前只支持 BasicAuthKeyAuth 两种认证类型,在 basicAuth 下面可以通过 value 可直接去配置相关的 username 和 password,也可以直接使用 Secret 资源对象进行配置,比起明文配置会更安全一些。

然后在 ApisixRoute 中添加 authentication,将其开启并指定认证类型即可,就可以实现使用 Consumer 去完成相关配置认证,如下所示:

代码语言:javascript
复制
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: whoami-apisix
spec:
  http:
    - name: whoami-root
      match:
        hosts:
          - whoami.boysec.cn
        paths:
          - '/tls*'
          - '/v1*'
      backends:
        - serviceName: whoami
          servicePort: 80
      plugins:
        - name: redirect
          enable: true
          config:
            regex_uri: ['^(/tls)$', '$1/']
        - name: proxy-rewrite
          enable: true
          config:
            regex_uri: ['^/v1(/|$)(.*)', '/$2']
      authentication: # 开启 basic auth 认证
        enable: true
        type: basicAuth

直接更新上面的资源即可开启 basic auth 认证了,在 Dashboard 上也可以看到创建了一个 Consumer:

然后我们可以进行如下的测试来进行验证: