當在實施服務網格時,不可避免的存在網格外服務訪問網格內服務的情況,也就是服務網格的平滑落地。這種中間狀態可能會持續較長的時間,也是我們在落地的時候需要解決的問題之一。又或者,有的應用處于某些考慮并不適合使用服務網格,而又需要訪問網格內的服務。

一種方式是通過統一的 Ingress 入口來訪問服務網格內的服務,將網格外的服務當做集群在的服務來對待。這種方式的方式優點是架構簡單、實施方便。缺點也比較明顯,統一的訪問入口無法做到細粒度的訪問控制,網格外的服務均能訪問網格內的服務。
這篇文章將介紹另外一種方式,在打通內外服務通信的同時又可以支持對訪問源進行細粒度的訪問控制,它就是在?osm-edge v1.2.0[1]?中引入的訪問控制特性。
訪問控制?AccessControl
[2]?的訪問源有兩種:Service
?和 IP 范圍。數據傳輸方面,支持明文傳輸和雙向加密傳輸(mTLS)。
接下來,我們就來看下如何使用訪問控制。
環境準備
Kubernetes 集群
使用極簡的 Kubernetes 發行版?k8e[3]:
curl?-sfL?https://getk8e.com/install.sh?|?K8E_TOKEN=k8e-mesh?INSTALL_K8E_EXEC="server?--cluster-init?--write-kubeconfig-mode?644?--write-kubeconfig?~/.kube/config"?sh?-
osm-edge CLI
system=$(uname?-s?|?tr?[:upper:]?[:lower:])
arch=$(dpkg?--print-architecture)
release=v1.2.0
curl?-L?https://github.com/flomesh-io/osm-edge/releases/download/${release}/osm-edge-${release}-${system}-${arch}.tar.gz?|?tar?-vxzf?-
./${system}-${arch}/osm?version
cp?./${system}-${arch}/osm?/usr/local/bin/
安裝 osm-edge
執行下面的命令,安裝 osm-edge 的相關組件。
export?osm_namespace=osm-system?
export?osm_mesh_name=osm?osm?install?\--mesh-name?"$osm_mesh_name"?\--osm-namespace?"$osm_namespace"?\--set=osm.image.pullPolicy=Always
檢查并確認所有的 pod 都正常啟動并運行。
部署示例應用
#模擬目標服務
kubectl?create?namespace?httpbin
osm?namespace?add?httpbin
kubectl?apply?-n?httpbin?-f?https://raw.githubusercontent.com/flomesh-io/osm-edge-docs/main/manifests/samples/httpbin/httpbin.yaml#模擬外部服務
kubectl?create?namespace?curl
kubectl?apply?-n?curl?-f?https://raw.githubusercontent.com/flomesh-io/osm-edge-docs/main/manifests/samples/curl/curl.yaml#等待依賴的?POD?正常啟動
kubectl?wait?--for=condition=ready?pod?-n?httpbin?-l?app=httpbin?--timeout=180s
kubectl?wait?--for=condition=ready?pod?-n?curl?-l?app=curl?--timeout=180s
演示
這時,我們嘗試從服務?curl
?發送請求到目標服務?httpbin
,執行下面的命令:
kubectl?exec?"$(kubectl?get?pod?-n?curl?-l?app=curl?-o?jsonpath='{.items..metadata.name}')"?-n?curl?--?curl?-sI?http://httpbin.httpbin:14001/get
command?terminated?with?exit?code?56
訪問失敗,這是因為默認情況下網格外的服務是無法訪問網格內服務的,我們需要應用訪問控制策略。
在應用策略之前,需要開啟訪問控制特性,默認情況下該特性是禁用的。
kubectl?patch?meshconfig?osm-mesh-config?-n?"$osm_namespace"?-p?'{"spec":{"featureFlags":{"enableAccessControlPolicy":true}}}'??--type=merge
明文傳輸
數據的傳輸可以有明文傳輸和雙向 TLS 加密。明文傳輸相對簡單,我們先演示明文傳輸的場景。
基于服務的訪問控制
首先為服務?curl
?創建?Service
:
kubectl?apply?-n?curl?-f?-?<<EOF
apiVersion:?v1
kind:?Service
metadata:name:?curllabels:app:?curlservice:?curl
spec:ports:-?name:?httpport:?80selector:app:?curl
EOF
接著創建訪問源為?Service
?curl
,目標服務為?httpbin
?的訪問控制策略:
kubectl?apply?-f?-?<<EOF
kind:?AccessControl
apiVersion:?policy.openservicemesh.io/v1alpha1
metadata:name:?httpbinnamespace:?httpbin
spec:backends:-?name:?httpbinport:number:?14001?#?targetPort?of?httpbin?serviceprotocol:?httpsources:-?kind:?Servicenamespace:?curlname:?curl
EOF
再次執行命令發送驗證請求,可以看到這次收到?HTTP 200
?響應。
kubectl?exec?"$(kubectl?get?pod?-n?curl?-l?app=curl?-o?jsonpath='{.items..metadata.name}')"?-n?curl?--?curl?-sI?http://httpbin.httpbin:14001/get
HTTP/1.1?200?OK
server:?gunicorn/19.9.0
date:?Mon,?07?Nov?2022?08:47:55?GMT
content-type:?application/json
content-length:?267
access-control-allow-origin:?*
access-control-allow-credentials:?true
osm-stats-namespace:?httpbin
osm-stats-kind:?Deployment
osm-stats-name:?httpbin
osm-stats-pod:?httpbin-69dc7d545c-qphrh
connection:?keep-alive
在繼續后面的演示之前,執行命令?kubectl delete accesscontrol httpbin -n httpbin
?刪除剛才創建的策略。
基于 IP 范圍的訪問控制,明文傳輸
先獲取服務?curl
?的 pod IP 地址:
curl_pod_ip="$(kubectl?get?pod?-n?curl?-l?app=curl?-o?jsonpath='{.items[0].status.podIP}')"
使用 IP 范圍的訪問控制很簡單,只需要將訪問源類型設置為?IPRange
,并配置剛才獲取到的 IP 地址。
kubectl?apply?-f?-?<<EOF
kind:?AccessControl
apiVersion:?policy.openservicemesh.io/v1alpha1
metadata:name:?httpbinnamespace:?httpbin
spec:backends:-?name:?httpbinport:number:?14001?#?targetPort?of?httpbin?serviceprotocol:?httpsources:-?kind:?IPRangename:?${curl_pod_ip}/32
EOF
再次執行命令測試控制策略是否生效:
kubectl?exec?"$(kubectl?get?pod?-n?curl?-l?app=curl?-o?jsonpath='{.items..metadata.name}')"?-n?curl?--?curl?-sI?http://httpbin.httpbin:14001/get
HTTP/1.1?200?OK
server:?gunicorn/19.9.0
date:?Mon,?07?Nov?2022?09:20:57?GMT
content-type:?application/json
content-length:?267
access-control-allow-origin:?*
access-control-allow-credentials:?true
osm-stats-namespace:?httpbin
osm-stats-kind:?Deployment
osm-stats-name:?httpbin
osm-stats-pod:?httpbin-69dc7d545c-qphrh
connection:?keep-alive
記得執行?kubectl delete accesscontrol httpbin -n httpbin
?清理策略。
前面我們用的都是明文傳輸,接下來我們看下加密傳輸。
加密傳輸
默認訪問策略證書特性是關閉的,執行下面的命令開啟:
kubectl?patch?meshconfig?osm-mesh-config?-n?"$osm_namespace"?-p?'{"spec":{"featureFlags":{"enableAccessCertPolicy":true}}}'??--type=merge
為了訪問源創建?AccessCert
?來分配用于數據加密的證書,控制器會將證書信息保存在命名空間?curl
?下的?Secret
?curl-mtls-secret
?中,這里還要為訪問源分配 SAN?curl.curl.cluster.local
。
kubectl?apply?-f?-?<<EOF
kind:?AccessCert
apiVersion:?policy.openservicemesh.io/v1alpha1
metadata:name:?curl-mtls-certnamespace:?httpbin
spec:subjectAltNames:-?curl.curl.cluster.localsecret:name:?curl-mtls-secretnamespace:?curl
EOF
重新部署?curl
,將系統分配的?Secret
?掛載到 pod 中:
kubectl?apply?-n?curl?-f?-?<<EOF
apiVersion:?apps/v1
kind:?Deployment
metadata:name:?curl
spec:replicas:?1selector:matchLabels:app:?curltemplate:metadata:labels:app:?curlspec:serviceAccountName:?curlnodeSelector:kubernetes.io/os:?linuxcontainers:-?image:?curlimages/curlimagePullPolicy:?IfNotPresentname:?curlcommand:?["sleep",?"365d"]volumeMounts:-?name:?curl-mtls-secretmountPath:?"/certs"readOnly:?truevolumes:-?name:?curl-mtls-secretsecret:secretName:?curl-mtls-secret
EOF
基于服務的訪問控制
接來下就是創建使用加密傳輸的訪問控制策略,配置目標服務的時候通過指定?tls.skipClientCertValidation = false
?來啟用客戶端證書的檢查。而訪問源這里,除了指定?Service
?類型的訪問源外,還要指定通過?AuthenticatedPrincipal
?指定 SAN?curl.curl.cluster.local
。
kubectl?apply?-f?-?<<EOF
kind:?AccessControl
apiVersion:?policy.openservicemesh.io/v1alpha1
metadata:name:?httpbinnamespace:?httpbin
spec:backends:-?name:?httpbinport:number:?14001?#?targetPort?of?httpbin?serviceprotocol:?httptls:skipClientCertValidation:?falsesources:-?kind:?Servicenamespace:?curlname:?curl-?kind:?AuthenticatedPrincipalname:?curl.curl.cluster.local
EOF
測試下訪問策略是否有效,發送請求時我們要為訪問源的?curl
?指令指定要使用的 CA 證書、密鑰、證書,正常會受到?HTTP 200
?響應。
kubectl?exec?"$(kubectl?get?pod?-n?curl?-l?app=curl?-o?jsonpath='{.items..metadata.name}')"?-n?curl?--?curl?-ksI?https://httpbin.httpbin:14001/get?--cacert?/certs/ca.crt?--key?/certs/tls.key?--cert?/certs/tls.crt
HTTP/2?200
server:?gunicorn/19.9.0
date:?Mon,?07?Nov?2022?10:44:05?GMT
content-type:?application/json
content-length:?267
access-control-allow-origin:?*
access-control-allow-credentials:?true
osm-stats-namespace:?httpbin
osm-stats-kind:?Deployment
osm-stats-name:?httpbin
osm-stats-pod:?httpbin-69dc7d545c-qphrh
基于 IP 范圍的訪問控制
基于服務的訪問控制后,基于 IP 范圍的控制就很簡單了。只需要將訪問源的類型指定為?IPRange
?以及指定訪問源的 IP 地址。由于重新部署了應用?curl
,需要重新獲取其 IP 地址(可能你也發現了基于 IP 范圍的訪問控制的弊端了吧)。
curl_pod_ip="$(kubectl?get?pod?-n?curl?-l?app=curl?-o?jsonpath='{.items[0].status.podIP}')"
kubectl?apply?-f?-?<<EOF
kind:?AccessControl
apiVersion:?policy.openservicemesh.io/v1alpha1
metadata:name:?httpbinnamespace:?httpbin
spec:backends:-?name:?httpbinport:number:?14001?#?targetPort?of?httpbin?serviceprotocol:?httptls:skipClientCertValidation:?falsesources:-?kind:?IPRangename:?${curl_pod_ip}/32-?kind:?AuthenticatedPrincipalname:?curl.curl.cluster.local
EOF
再次發送請求進行測試:
kubectl?exec?"$(kubectl?get?pod?-n?curl?-l?app=curl?-o?jsonpath='{.items..metadata.name}')"?-n?curl?--?curl?-ksI?https://httpbin.httpbin:14001/get?--cacert?/certs/ca.crt?--key?/certs/tls.key?--cert?/certs/tls.crt
HTTP/2?200
server:?gunicorn/19.9.0
date:?Mon,?07?Nov?2022?10:58:55?GMT
content-type:?application/json
content-length:?267
access-control-allow-origin:?*
access-control-allow-credentials:?true
osm-stats-namespace:?httpbin
osm-stats-kind:?Deployment
osm-stats-name:?httpbin
osm-stats-pod:?httpbin-69dc7d545c-qphrh
Bingo!訪問成功,說明我們的策略生效了。
總結
實際的環境中,系統中可能會存在某些應用不適合使用服務網格,比如對性能的要求、無法兼容服務網格等原因;或者在服務網格落地的過程中,經常會先在增量應用中進行驗證,然后才是存量的服務的遷移。這些情況下,都會存在網格內外應用的互相通信的情況。
本文介紹的訪問控制便正是適合解決此類的問題,并可根據需求選擇合適的訪問源類型,以及明文還是加密傳輸數據。相比使用統一的 Ingress 進行訪問,控制的粒度更細。
引用鏈接
[1]
?osm-edge v1.2.0:?https://github.com/flomesh-io/osm-edge/releases/tag/v1.2.0[2]
?AccessControl
:?https://github.com/flomesh-io/osm-edge/blob/649e2febd1819a54782b254867c343febf808027/pkg/apis/policy/v1alpha1/accesscontrol.go#L13[3]
?k8e:?https://getk8e.com