Service Mesh - Istio安全篇

官方文档:


守卫网格:配置TLS安全网关

Istio 1.5 的安全更新:

  • SDS (安全发现服务)趋于稳定、默认开启
  • 对等认证和请求认证配置分离
  • 自动 mTLS 从 alpha 变为 beta,默认开启
  • Node agent 和 Pilot agent 合并, 简化 Pod 安全策略的配置
  • 支持 first-party-jwt (ServiceAccountToken) 作为 third-party-jwt 的备用
  • …...

安全发现服务(SDS):

  • 身份和证书管理
  • 实现安全配置自动化
  • 中心化 SDS Server
  • 优点:
    • 无需挂载 secret 卷
    • 动态更新证书,无需重启
    • 可监视多个证书密钥对


      image.png

接下来我们配置一个安全网关,为外部提供 HTTPS 的访问方式。首先,确认 curl 命令是否通过LibreSSL去编译的:

$ curl --version |grep LibreSSL

为服务创建根证书和私钥:

$ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt

为 httpbin.example.com 域名创建证书和私钥:

$ openssl req -out httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"

$ openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in httpbin.example.com.csr -out httpbin.example.com.crt

完成以上操作后,当前目录会创建如下证书和密钥文件:

[root@m1 ~]# ls |grep example
example.com.crt
example.com.key
httpbin.example.com.crt
httpbin.example.com.csr
httpbin.example.com.key
[root@m1 ~]# 

部署 httpbin 服务:

apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
spec:
  ports:
  - name: http
    port: 8000
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      containers:
      - image: docker.io/citizenstig/httpbin
        imagePullPolicy: IfNotPresent
        name: httpbin
        ports:
        - containerPort: 8000

然后为入口网关创建k8s的secret,将 httpbin.example.com 域名的密钥和证书挂载到secret中:

$ kubectl create -n istio-system secret tls httpbin-credential --key=httpbin.example.com.key --cert=httpbin.example.com.crt

创建入口网关,并指定外部以 https 方式访问:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: mygateway
spec:
  selector:
    istio: ingressgateway 
  servers:
  - port:  # 使用https访问方式
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE   # 简单模式,单向TLS
      credentialName: httpbin-credential   # k8s secret的名称
    hosts:
    - httpbin.example.com

创建虚拟服务,配置 TLS 网关和路由规则:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService 
metadata:
  name: httpbin
spec:
  hosts:
  - "httpbin.example.com"
  gateways:
  - mygateway
  http:
  - match:
    - uri:
        prefix: /status
    - uri:
        prefix: /delay
    route:
    - destination:
        port:
          number: 8000
        host: httpbin

curl 测试,请求验证是否生效:

$ curl -HHost:httpbin.example.com \
--resolve httpbin.example.com:443:${INGRESS_HOST} \
--cacert example.com.crt "https://httpbin.example.com:443/status/418"

如果 istio-ingressgateway 组件是以 nodePort 方式开放端口的,那么这里的 443 端口需要替换成对应的 nodePort 端口。示例:

[root@m1 ~]# kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}'
192.168.243.140   # istio-ingressgateway 组件所在的虚拟机IP
[root@m1 ~]# kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}'
32155   # https的nodePort端口
[root@m1 ~]# curl -HHost:httpbin.example.com --resolve httpbin.example.com:32155:192.168.243.140 --cacert example.com.crt "https://httpbin.example.com:32155/status/418"

    -=[ teapot ]=-

       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`
[root@m1 ~]# 

配置选项

image.png


双重保障:为应用设置不同级别的双向TLS

Istio 认证策略

  • 认证策略的分类
    • 对等认证(PeerAuthentication)
    • 请求认证(RequestAuthentication)
  • 认证策略范围
    • 网格
    • 命名空间
    • 特定服务
  • 优先级:最窄原则

mTLS 简介:

  • TLS:客户端根据服务端证书验证其身份
  • mTLS:客户端、服务端彼此都验证对方身份


    image.png
  • 对等认证主要用于服务之间的通讯,一般不去用于服务与外界的通讯,因为比较慢,双方都需要互相验证及握手

接下来我们尝试为应用设置不同级别的双向TLS。首先,创建一个用于测试的命令空间:

[root@m1 ~]# kubectl create ns testaut
namespace/testaut created
[root@m1 ~]# 

在该命名空间下创建测试用的客户端(sleep):

[root@m1 ~]# kubectl apply -f /usr/local/istio-1.8.1/samples/sleep/sleep.yaml -n testaut
serviceaccount/sleep created
service/sleep created
deployment.apps/sleep created
[root@m1 ~]# 

我们使用上一小节的 httpbin 服务作为服务端,注意 httpbin 是在 default 命名空间下的。我们通过 sleep 访问一下 httpbin 的接口:

[root@m1 ~]# kubectl get po -n testaut 
NAME                     READY   STATUS    RESTARTS   AGE
sleep-854565cb79-tk586   1/1     Running   0          2m4s
[root@m1 ~]# kubectl exec -it sleep-854565cb79-tk586 -n testaut -c sleep -- curl http://httpbin.default:8000/ip
{
  "origin": "127.0.0.1"
}
[root@m1 ~]# 

目前它们的通讯方式是没有采用TLS的,接下来我们配置一个对等认证策略:

$ kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
  name: "default"
  namespace: "default"   # 给default添加命名空间策略
spec:
  mtls:  # 采用对等认证
    mode: PERMISSIVE    # 兼容模式
EOF

此时,依旧可以采用非TLS方式进行通讯,因为兼容模式可以同时通过非TLS和TLS方式进行通讯:

[root@m1 ~]# kubectl exec -it sleep-854565cb79-tk586 -n testaut -c sleep -- curl http://httpbin.default:8000/ip
{
  "origin": "127.0.0.1"
}
[root@m1 ~]# 

现在我们将策略改为严格模式,如下:

$ kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
  name: "default"
  namespace: "default"
spec:
  mtls:
    mode: STRICT  # 严格模式
EOF

改为严格模式后,使用非TLS的通讯方式就会被拒绝访问了:

[root@m1 ~]# kubectl exec -it sleep-854565cb79-tk586 -n testaut -c sleep -- curl http://httpbin.default:8000/ip
curl: (56) Recv failure: Connection reset by peer
command terminated with exit code 56
[root@m1 ~]# 

此时我们就要为网格内的服务开启自动 mTLS,开启的方式也比较简单,只需要注入 Sidecar 即可。因为 Istio 已经实现了一个自动的 mTLS ,会帮我们完成证书和密钥的管理。命令如下:

[root@m1 ~]# kubectl apply -f <(istioctl kube-inject -f /usr/local/istio-1.8.1/samples/sleep/sleep.yaml) -n testaut
serviceaccount/sleep unchanged
service/sleep unchanged
deployment.apps/sleep configured
[root@m1 ~]# 

而且访问方式也不需要改变,还是和之前一样:

[root@m1 ~]# kubectl get pods -n testaut 
NAME                    READY   STATUS    RESTARTS   AGE
sleep-866b7dc94-dqd9p   2/2     Running   0          4m21s
[root@m1 ~]# kubectl exec -it sleep-866b7dc94-dqd9p -n testaut -c sleep -- curl http://httpbin.default:8000/ip
{
  "origin": "127.0.0.1"
}
[root@m1 ~]# 

上面示例的认证范围针对的是命名空间,我们也可以添加全局策略,如下示例:

$ kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
  name: "default"
spec:
  mtls:
    mode: STRICT
EOF

配置选项

image.png


授权策略:如何实现JWT身份认证与授权?

与认证相对应的就是授权,简单来说授权就是授予你做什么事情的权利,例如某个数据只有得到授权的用户才能访问。在 Istio 中我们可以使用 JWT 来实现身份认证与授权

什么是 JWT:

  • JWT的全称为JSON Web Token,就是JSON格式的Web令牌
  • 以 JSON 格式传递信息
  • 应用场景
    • 授权
    • 信息交换
  • 组成部分
    • Header、payload、signature


      image.png

通过如下命令创建用于测试的命名空间,以及两个分别作为客户端(sleep)和服务端(httpbin)的应用:

[root@m1 ~]# kubectl create ns testjwt
namespace/testjwt created
[root@m1 ~]# kubectl apply -f <(istioctl kube-inject -f /usr/local/istio-1.8.1/samples/httpbin/httpbin.yaml) -n testjwt   # httpbin作为服务端
serviceaccount/httpbin created
service/httpbin created
deployment.apps/httpbin created
[root@m1 ~]# kubectl apply -f <(istioctl kube-inject -f /usr/local/istio-1.8.1/samples/sleep/sleep.yaml) -n testjwt      # sleep作为客户端
serviceaccount/sleep created
service/sleep created
deployment.apps/sleep created
[root@m1 ~]# kubectl get pods -n testjwt 
NAME                      READY   STATUS    RESTARTS   AGE
httpbin-5b6477fb8-5pn4v   2/2     Running   0          48s
sleep-866b7dc94-mrzdg     2/2     Running   0          42s
[root@m1 ~]# 

测试客户端与服务端之间的连通性:

[root@m1 ~]# kubectl exec "$(kubectl get pod -l app=sleep -n testjwt -o jsonpath={.items..metadata.name})" -c sleep -n testjwt -- curl http://httpbin.testjwt:8000/ip -s -o /dev/null -w "%{http_code}\n"
200
[root@m1 ~]# 

接下来配置基于 JWT 的认证策略,创建一个请求认证资源,如下所示:

kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "RequestAuthentication"  # 资源类型为请求认证
metadata:
  name: "jwt-example"
  namespace: testjwt   # 作用于哪个命名空间
spec:
  selector:
    matchLabels:
      app: httpbin  # 需要请求认证的服务
  jwtRules:
  - issuer: "testing@secure.istio.io"   # JWT的签发人
    jwks: "https://raw.githubusercontent.com/istio/istio/release-1.8/security/tools/jwt/samples/jwks.json"   # 用于验证JWT签名的提供者公钥集的URL
EOF

测试使用不合法的JWT访问,会返回401:

[root@m1 ~]# kubectl exec $(kubectl get pod -l app=sleep -n testjwt -o jsonpath={.items..metadata.name}) -c sleep -n testjwt -- curl "http://httpbin.testjwt:8000/headers" -H "Authorization: Bearer invalidToken" -s -o /dev/null -w "%{http_code}\n"
401
[root@m1 ~]# 

测试没有授权策略时,可以直接访问:

[root@m1 ~]# kubectl exec $(kubectl get pod -l app=sleep -n testjwt -o jsonpath={.items..metadata.name}) -c sleep -n testjwt -- curl "http://httpbin.testjwt:8000/headers" -s -o /dev/null -w "%{http_code}\n"
200
[root@m1 ~]# 

配置 JWT 的授权策略,实现基于 JWT 的授权访问:

kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy    # 授权策略
metadata:
  name: require-jwt
  namespace: testjwt   # 作用于哪个命名空间
spec:
  selector:
    matchLabels:
      app: httpbin   # 需要授权访问的服务
  action: ALLOW   # 符合授权条件时的动作,拒绝或允许
  rules:  # 定义授权规则
  - from:
    - source:
       requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"]  # 来源于此JWT签发人列表的请求满足条件
EOF

解析token,并设置为系统变量:

[root@m1 ~]# TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.8/security/tools/jwt/samples/demo.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode -

然后进行验证,测试带token的请求是否正常:

[root@m1 ~]# kubectl exec $(kubectl get pod -l app=sleep -n testjwt -o jsonpath={.items..metadata.name}) -c sleep -n testjwt -- curl "http://httpbin.testjwt:8000/headers" -s -o /dev/null -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n"
200
[root@m1 ~]# 

请求认证配置选项:


image.png

授权策略配置选项:


image.png

关于安全方面更多的内容可以参考官方文档的使用示例:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容