当在实施服务网格时,不可避免的存在网格外服务访问网格内服务的情况,也就是服务网格的平滑落地。这种中间状态可能会持续较长的时间,也是我们在落地的时候需要解决的问题之一。又或者,有的应用处于某些考虑并不适合使用服务网格,而又需要访问网格内的服务。
一种方式是通过统一的 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