使用访问控制策略访问服务网格内的服务

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用访问控制策略访问服务网格内的服务相关的知识,希望对你有一定的参考价值。

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

一种方式是通过统一的 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: curl
  labels:
    app: curl
    service: curl
spec:
  ports:
    - name: http
      port: 80
  selector:
    app: curl
EOF

接着创建访问源为 Service curl,目标服务为 httpbin 的访问控制策略:

kubectl apply -f - <<EOF
kind: AccessControl
apiVersion: policy.openservicemesh.io/v1alpha1
metadata:
  name: httpbin
  namespace: httpbin
spec:
  backends:
  - name: httpbin
    port:
      number: 14001 # targetPort of httpbin service
      protocol: http
  sources:
  - kind: Service
    namespace: curl
    name: 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: httpbin
  namespace: httpbin
spec:
  backends:
  - name: httpbin
    port:
      number: 14001 # targetPort of httpbin service
      protocol: http
  sources:
  - kind: IPRange
    name: $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-cert
  namespace: httpbin
spec:
  subjectAltNames:
  - curl.curl.cluster.local
  secret:
    name: curl-mtls-secret
    namespace: curl
EOF

重新部署 curl,将系统分配的 Secret 挂载到 pod 中:

kubectl apply -n curl -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: curl
spec:
  replicas: 1
  selector:
    matchLabels:
      app: curl
  template:
    metadata:
      labels:
        app: curl
    spec:
      serviceAccountName: curl
      nodeSelector:
        kubernetes.io/os: linux
      containers:
      - image: curlimages/curl
        imagePullPolicy: IfNotPresent
        name: curl
        command: ["sleep", "365d"]
        volumeMounts:
        - name: curl-mtls-secret
          mountPath: "/certs"
          readOnly: true
      volumes:
        - name: curl-mtls-secret
          secret:
            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: httpbin
  namespace: httpbin
spec:
  backends:
  - name: httpbin
    port:
      number: 14001 # targetPort of httpbin service
      protocol: http
    tls:
      skipClientCertValidation: false
  sources:
  - kind: Service
    namespace: curl
    name: curl
  - kind: AuthenticatedPrincipal
    name: 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: httpbin
  namespace: httpbin
spec:
  backends:
  - name: httpbin
    port:
      number: 14001 # targetPort of httpbin service
      protocol: http
    tls:
      skipClientCertValidation: false
  sources:
  - kind: IPRange
    name: $curl_pod_ip/32
  - kind: AuthenticatedPrincipal
    name: 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] AccessControlhttps://github.com/flomesh-io/osm-edge/blob/649e2febd1819a54782b254867c343febf808027/pkg/apis/policy/v1alpha1/accesscontrol.go#L13
[3] k8e: https://getk8e.com

以上是关于使用访问控制策略访问服务网格内的服务的主要内容,如果未能解决你的问题,请参考以下文章

WSO2 IS:服务提供商的访问控制策略

远程访问以及控制(2/2)+实验:TCP Wrappers的控制策略

httpd的安全策略

idou老师教你学Istio 16:如何用 Istio 实现微服务间的访问控制

idou老师教你学Istio 16:如何用 Istio 实现微服务间的访问控制

关于服务器的 域控制器安全策略