istio 的请求改写功能

istio 可以按照用户的定义改写请求,譬如增加请求头、改写 uri、请求重定向等。

Control Headers and Routing 演示了改写请求的方法同时也演示了 istio 的 adpater 的用法。

启动 adpater

keyval adpater 是一个 key:value 服务,输入 key 返回 value,如果 key 不存在返回 NOT_FOUND。

启动 keyval 容器:

$ kubectl run keyval --image=gcr.io/istio-testing/keyval:release-1.1 --namespace istio-system --port 9070 --expose

确定 keyval 创建成功:

$ kubectl -n istio-system get pod |grep keyval 
keyval-57f7c4b4cc-t5xw7                   1/1     Running     0          2m51s

把 keyval 封装成 istio 的 adapter:

$ kubectl apply -f samples/httpbin/policy/keyval-template.yaml
$ kubectl apply -f samples/httpbin/policy/keyval.yaml

keyval-template.yaml 的类型是 template,keyval.yaml 的类型是 adapter, 现在只能判断这就是在 handler 中使用的 adapter。

adapter 的运行、衔接机制现在还不清楚(2019-11-27 19:25:30)。adapter 显然是 istio 的重点特性,istio 的灵活性很大一部分来自于 apdater。

创建 handler

handler 引用了前面的创建的 adpater,并设置了 params(一个 K-V 查找表)

$ kubectl apply -f - <<EOF
apiVersion: config.istio.io/v1alpha2
kind: handler
metadata:
  name: keyval
  namespace: istio-system
spec:
  adapter: keyval
  connection:
    address: keyval:9070
  params:
    table:
      jason: admin
EOF

创建 instance

instance 引用了前面创建的 template:

$ kubectl apply -f - <<EOF
apiVersion: config.istio.io/v1alpha2
kind: instance
metadata:
  name: keyval
  namespace: istio-system
spec:
  template: keyval
  params:
    key: request.headers["user"] | ""
EOF

这个 instance 的含义使用请求头中的 user 头的值作为 key,从 keyval 获取价值。

前面的 adpater 中设置的 jason 对应的只为 admin,后面会用到这个 key。

创建 rule

rule 将前面创建的 handler 和 instance 绑定,设置了处理动作(注入 user-group header):

$ kubectl apply -f - <<EOF
apiVersion: config.istio.io/v1alpha2
kind: rule
metadata:
  name: keyval
  namespace: istio-system
spec:
  actions:
  - handler: keyval.istio-system
    instances: [ keyval ]
    name: x
  requestHeaderOperations:
  - name: user-group
    values: [ x.output.value ]
EOF

这个 handler 的含义是读取前面创建的 instance 的值(x),然后在请求头中添加一个 user-group header,字段值为 instaces 的值。

http 头改写效果

带上 user:jason 请求头,访问 http-record 服务(一个回显请求的 http 服务):

curl  -H "user:jason"   10.101.26.165:8000
{
    "RemoteAddr": "127.0.0.1:41098",
    "Method": "GET",
    "Host": "10.101.26.165:8000",
    "RequestURI": "/",
    "Header": {
        "Accept": [
            "*/*"
        ],
        "Content-Length": [
            "0"
        ],
        "User": [
            "jason"
        ],
        "User-Agent": [
            "curl/7.61.1"
        ],
        "User-Group": [
            "admin"
        ],
        "X-B3-Sampled": [
            "1"
        ],
        "X-B3-Spanid": [
            "51eb9b405f2c1207"
        ],
        "X-B3-Traceid": [
            "a63afc71120d234051eb9b405f2c1207"
        ],
        "X-Forwarded-Proto": [
            "http"
        ],
        "X-Request-Id": [
            "877e616c-7abc-93f4-913d-7dea33b3144e"
        ]
    },
    "Body": ""
}

可以看到服务端接收的请求头中多了一个 "User-Group”。

修改 uri

修改 uri 的操作和注入请求头类似:

$ kubectl apply -f - <<EOF
apiVersion: config.istio.io/v1alpha2
kind: rule
metadata:
  name: keyval
  namespace: istio-system
spec:
  match: source.labels["istio"] == "ingressgateway"
  actions:
  - handler: keyval.istio-system
    instances: [ keyval ]
  requestHeaderOperations:
  - name: :path
    values: [ '"/status/418"' ]
EOF

上面的定义中使用了 match,只影响到 ingressgateway 的请求,通过 ingressgateway 访问,uri 被改写:

$ curl -H "user: jason"  -H "Host: http-record.example" 192.168.99.100:31380
{
    "RemoteAddr": "127.0.0.1:50684",
    "Method": "GET",
    "Host": "http-record.example",
    "RequestURI": "/status/418",
...省略...

参考

  1. 李佶澳的博客