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 卷
    • 動態更新證書,無需重啓
    • 可監視多個證書密鑰對
      Service Mesh - Istio安全篇

接下來我們配置一個安全網關,爲外部提供 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 ~]# 

配置選項
Service Mesh - Istio安全篇


雙重保障:爲應用設置不同級別的雙向TLS

Istio 認證策略

  • 認證策略的分類
    • 對等認證(PeerAuthentication)
    • 請求認證(RequestAuthentication)
  • 認證策略範圍
    • 網格
    • 命名空間
    • 特定服務
  • 優先級:最窄原則

mTLS 簡介:

  • TLS:客戶端根據服務端證書驗證其身份
  • mTLS:客戶端、服務端彼此都驗證對方身份
    Service Mesh - Istio安全篇
  • 對等認證主要用於服務之間的通訊,一般不去用於服務與外界的通訊,因爲比較慢,雙方都需要互相驗證及握手

接下來我們嘗試爲應用設置不同級別的雙向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

配置選項
Service Mesh - Istio安全篇


授權策略:如何實現JWT身份認證與授權?

與認證相對應的就是授權,簡單來說授權就是授予你做什麼事情的權利,例如某個數據只有得到授權的用戶才能訪問。在 Istio 中我們可以使用 JWT 來實現身份認證與授權

什麼是 JWT:

  • JWT的全稱爲JSON Web Token,就是JSON格式的Web令牌
  • 以 JSON 格式傳遞信息
  • 應用場景
    • 授權
    • 信息交換
  • 組成部分
    • Header、payload、signature
      Service Mesh - Istio安全篇

通過如下命令創建用於測試的命名空間,以及兩個分別作爲客戶端(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: "[email protected]"   # 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: ["[email protected]/[email protected]"]  # 來源於此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 ~]# 

請求認證配置選項:
Service Mesh - Istio安全篇

授權策略配置選項:
Service Mesh - Istio安全篇

關於安全方面更多的內容可以參考官方文檔的使用示例:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章