本指南将演示如何在使用 Kubernetes Nginx 入口控制器 时将 HTTP 和 HTTPS 入口配置到 OSM 托管服务网格的部分服务。

先决条件

  • Kubernetes 集群运行版本 v1.22.9 或者更高。
  • 使用 kubectl 与 API server 交互。
  • 已安装的 OSM 版本不低于 v0.10.0。
  • 已安装 osm 命令行工具,用于管理服务网格。

演示

首先,明确有关 OSM 和 Nginx 入口控制器的安装细节:

  1. osm_namespace=osm-system # Replace osm-system with the namespace where OSM is installed
  2. osm_mesh_name=osm # replace osm with the mesh name (use `osm mesh list` command)
  3. nginx_ingress_namespace=<nginx-namespace> # replace <nginx-namespace> with the namespace where Nginx is installed
  4. nginx_ingress_service=<nginx-ingress-controller-service> # replace <nginx-ingress-controller-service> with the name of the nginx ingress controller service
  5. nginx_ingress_host="$(kubectl -n "$nginx_ingress_namespace" get service "$nginx_ingress_service" -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"
  6. nginx_ingress_port="$(kubectl -n "$nginx_ingress_namespace" get service "$nginx_ingress_service" -o jsonpath='{.spec.ports[?(@.name=="http")].port}')"

为了将后端的入口流量限制到授权客户端,我们将设置 IngressBackend 配置,以便只有来自 Nginx 入口控制器 service 的流量,才能访问到对应的服务后端。为了能够发现该 service 的端点,我们需要 OSM 控制器来监控相应的命名空间。 然而,为了 Nginx 正常运行,其必须不能注入 Envoy sidecar。

  1. osm namespace add "$nginx_ingress_namespace" --mesh-name "$osm_mesh_name" --disable-sidecar-injection

接下来,我们将部署 httpbin 的示例 service。

  1. # Create a namespace
  2. kubectl create ns httpbin
  3. # Add the namespace to the mesh
  4. osm namespace add httpbin
  5. # Deploy the application
  6. kubectl apply -f https://raw.githubusercontent.com/openservicemesh/osm-docs/release-v1.2/manifests/samples/httpbin/httpbin.yaml -n httpbin

确认 httpbin service 和 pod 启动并运行。

  1. $ kubectl get pods -n httpbin
  2. NAME READY STATUS RESTARTS AGE
  3. httpbin-74677b7df7-zzlm2 2/2 Running 0 11h
  4. $ kubectl get svc -n httpbin
  5. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  6. httpbin ClusterIP 10.0.22.196 <none> 14001/TCP 11h

HTTP 入口

下一步,我们将创建对应的 Ingress 和 IngressBackend 配置,来允许外部的客户端访问位于 httpbin 命名空间下 ,运行在 14001 端口上的 httpbin service 。由于我们没有使用 TLS,Nginx 入口 service 到 httpbin 后端 pod 的连接是没有进行加密。

  1. kubectl apply -f - <<EOF
  2. apiVersion: networking.k8s.io/v1
  3. kind: Ingress
  4. metadata:
  5. name: httpbin
  6. namespace: httpbin
  7. spec:
  8. ingressClassName: nginx
  9. rules:
  10. - http:
  11. paths:
  12. - path: /
  13. pathType: Prefix
  14. backend:
  15. service:
  16. name: httpbin
  17. port:
  18. number: 14001
  19. ---
  20. kind: IngressBackend
  21. apiVersion: policy.openservicemesh.io/v1alpha1
  22. metadata:
  23. name: httpbin
  24. namespace: httpbin
  25. spec:
  26. backends:
  27. - name: httpbin
  28. port:
  29. number: 14001 # targetPort of httpbin service
  30. protocol: http
  31. sources:
  32. - kind: Service
  33. namespace: "$nginx_ingress_namespace"
  34. name: "$nginx_ingress_service"
  35. EOF

现在,我们预期外部的客户端可以通过 HTTP 的方式访问 httpbin service :

  1. $ curl -sI http://"$nginx_ingress_host":"$nginx_ingress_port"/get
  2. HTTP/1.1 200 OK
  3. Date: Wed, 18 Aug 2021 18:12:35 GMT
  4. Content-Type: application/json
  5. Content-Length: 366
  6. Connection: keep-alive
  7. access-control-allow-origin: *
  8. access-control-allow-credentials: true
  9. x-envoy-upstream-service-time: 2

HTTPS 出口 (mTLS 和 TLS)

为了将连接代理到 HTTPS 后端,我们将在 Ingress 和 IngressBackend 配置中指定使用 https 作为后端协议,同时由 OSM 颁发证书,Nginx 将使用该证书作为客户端证书,来将 HTTPS 连接代理到 TLS 后端。客户端证书和 CA 证书将保存在 Kubernetes secret 中,Nginx 将使用该证书来认证服务网格后端。

为了给 Nginx 入口 service 颁发一个客户端证书,需要更新 osm-mesh-config MeshConfig 资源。

  1. kubectl edit meshconfig osm-mesh-config -n "$osm_namespace"

spec.certificate 下添加字段 ingressGateway :

  1. certificate:
  2. ingressGateway:
  3. secret:
  4. name: osm-nginx-client-cert
  5. namespace: <osm-namespace> # replace <osm-namespace> with the namespace where OSM is installed
  6. subjectAltNames:
  7. - ingress-nginx.ingress-nginx.cluster.local
  8. validityDuration: 24h

注意:主体备用名称(Subject Alternative Name,SAN)使用类似 <service-account>.<namespace>.cluster.local 格式,sevice account 和 namespace 为 Nginx service 对应的信息。

接下来,我们需要创建一个 Ingress 和 IngressBackend 配置以使用 TLS 代理到后端服务,同时通过 mTLS 启用到后端的代理。为此,我们必须创建一个 IngressBackend 资源,指定指向 httpbin service 的 HTTPS 入口流量只能接受来自受信任客户端的流量。OSM 使用 主体备用名称(Subject Alternative Name,SAN)ingress-nginx.ingress-nginx.cluster.local 为 Nginx 入口 service 提供了一个客户端证书,因此 IngressBackend 配置需要引用相同的 SAN 用于 Nginx 入口 service 和 httpbin 后端之间的 mTLS 身份验证。

应用配置:

  1. kubectl apply -f - <<EOF
  2. apiVersion: networking.k8s.io/v1
  3. kind: Ingress
  4. metadata:
  5. name: httpbin
  6. namespace: httpbin
  7. annotations:
  8. nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
  9. # proxy_ssl_name for a service is of the form <service-account>.<namespace>.cluster.local
  10. nginx.ingress.kubernetes.io/configuration-snippet: |
  11. proxy_ssl_name "httpbin.httpbin.cluster.local";
  12. nginx.ingress.kubernetes.io/proxy-ssl-secret: "osm-system/osm-nginx-client-cert"
  13. nginx.ingress.kubernetes.io/proxy-ssl-verify: "on"
  14. spec:
  15. ingressClassName: nginx
  16. rules:
  17. - http:
  18. paths:
  19. - path: /
  20. pathType: Prefix
  21. backend:
  22. service:
  23. name: httpbin
  24. port:
  25. number: 14001
  26. ---
  27. apiVersion: policy.openservicemesh.io/v1alpha1
  28. kind: IngressBackend
  29. metadata:
  30. name: httpbin
  31. namespace: httpbin
  32. spec:
  33. backends:
  34. - name: httpbin
  35. port:
  36. number: 14001 # targetPort of httpbin service
  37. protocol: https
  38. tls:
  39. skipClientCertValidation: false
  40. sources:
  41. - kind: Service
  42. name: "$nginx_ingress_service"
  43. namespace: "$nginx_ingress_namespace"
  44. - kind: AuthenticatedPrincipal
  45. name: ingress-nginx.ingress-nginx.cluster.local
  46. EOF

现在,我们预期外部客户端能够通过入口网关和服务后端之间的 mTLS 上的 HTTPS 代理访问 httpbin service 。

  1. $ curl -sI http://"$nginx_ingress_host":"$nginx_ingress_port"/get
  2. HTTP/1.1 200 OK
  3. Date: Wed, 18 Aug 2021 18:12:35 GMT
  4. Content-Type: application/json
  5. Content-Length: 366
  6. Connection: keep-alive
  7. access-control-allow-origin: *
  8. access-control-allow-credentials: true
  9. x-envoy-upstream-service-time: 2

为了验证未经授权的客户端是无权访问后端,我们可以更新 IngressBackend 配置中 sources 指定的内容。让我们将主题更新为 Nginx 客户端证书中编码的 SAN 以外的其他内容。

  1. kubectl apply -f - <<EOF
  2. apiVersion: policy.openservicemesh.io/v1alpha1
  3. kind: IngressBackend
  4. metadata:
  5. name: httpbin
  6. namespace: httpbin
  7. spec:
  8. backends:
  9. - name: httpbin
  10. port:
  11. number: 14001 # targetPort of httpbin service
  12. protocol: https
  13. tls:
  14. skipClientCertValidation: false
  15. sources:
  16. - kind: Service
  17. name: "$nginx_ingress_service"
  18. namespace: "$nginx_ingress_namespace"
  19. - kind: AuthenticatedPrincipal
  20. name: untrusted-client.cluster.local # untrusted
  21. EOF

确认请求被拒绝并收到 HTTP 403 Forbidden 响应:

  1. $ curl -sI http://"$nginx_ingress_host":"$nginx_ingress_port"/get
  2. HTTP/1.1 403 Forbidden
  3. Date: Wed, 18 Aug 2021 18:36:09 GMT
  4. Content-Type: text/plain
  5. Content-Length: 19
  6. Connection: keep-alive

接下来,我们通过更新 IngressBackend 配置 skipClientCertValidation: true 来演示对在服务后端禁用客户端证书验证的支持,同时仍使用不受信任的客户端:

  1. kubectl apply -f - <<EOF
  2. apiVersion: policy.openservicemesh.io/v1alpha1
  3. kind: IngressBackend
  4. metadata:
  5. name: httpbin
  6. namespace: httpbin
  7. spec:
  8. backends:
  9. - name: httpbin
  10. port:
  11. number: 14001 # targetPort of httpbin service
  12. protocol: https
  13. tls:
  14. skipClientCertValidation: true
  15. sources:
  16. - kind: Service
  17. name: "$nginx_ingress_service"
  18. namespace: "$nginx_ingress_namespace"
  19. - kind: AuthenticatedPrincipal
  20. name: untrusted-client.cluster.local # untrusted
  21. EOF

由于未经信任的已验证用户被允许连接后端服务,确认请求再次访问成功

  1. $ curl -sI http://"$nginx_ingress_host":"$nginx_ingress_port"/get
  2. HTTP/1.1 200 OK
  3. Date: Wed, 18 Aug 2021 18:36:49 GMT
  4. Content-Type: application/json
  5. Content-Length: 364
  6. Connection: keep-alive
  7. access-control-allow-origin: *
  8. access-control-allow-credentials: true
  9. x-envoy-upstream-service-time: 2