Pod上终止TLS - 端到端加密

一些用户可能有端到端加密的监管要求,在本节,我们将在EKS上设置端到端加密,流量源自客户端,并终止于Pod应用

AWS Load Balancer Controller支持创建出NLB,使用IP模式,这样可以直接将流量路由到Pod内部,以减少走worker节点网络的延迟:

image-20220222093327558

安装 AWS Load Balancer Controller

首先,创建可供 AWS Load Balancer Controller使用的 IAM 策略:

curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.1/docs/install/iam_policy.json

创建policy:

aws iam create-policy \
    --policy-name AWSLoadBalancerControllerIAMPolicy \
    --policy-document file://iam_policy.json

<ACCOUNT_ID>替换为实际账户 ID,将<cluster-name>替换为实际的集群名称,然后运行以下命令创建service account:


eksctl create iamserviceaccount \
  --cluster=<cluster-name> \
  --namespace=kube-system \
  --name=aws-load-balancer-controller \
  --role-name "AmazonEKSLoadBalancerControllerRole" \
  --attach-policy-arn=arn:aws:iam::<ACCOUNT_ID>:policy/AWSLoadBalancerControllerIAMPolicy \
  --approve

使用Helm安装Load Balancer Controller

helm repo add eks https://aws.github.io/eks-charts
helm repo update

helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=<cluster-name> \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller

使用以下命令验证Load Balancer Controller是否正在运行:

kubectl get deployment -n kube-system aws-load-balancer-controller

这可能需要几分钟才能完成。重复上一个命令以检查状态, 直到看到aws-load-balancer-controller已准备就绪,状态为Ready 2/2

部署应用

现在将应用到部署到EKS,它是一个简单的 NGINX Web 服务器,返回 Hello pod-hostname

复制以下 YAML,保存为nlb-tls-app.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nlb-tls-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nlb-tls-app
  template:
    metadata:
      labels:
        app: nlb-tls-app
    spec:
      containers:
        - name: nlb-tls-app
          image: nginxdemos/nginx-hello:plain-text
          ports:
            - containerPort: 8443
          volumeMounts:
            - name: secret
              mountPath: /etc/nginx/ssl
              readOnly: true
            - name: config-volume
              mountPath: /etc/nginx/conf.d
      volumes:
        - name: secret
          secret:
            secretName: nlb-tls-app-secret
        - name: config-volume
          configMap:
            name: secure-config
---
apiVersion: v1
kind: Service
metadata:
  name: nlb-tls-app
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb-ip"
    service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
spec:
  ports:
    - port: 443
      targetPort: 8443
      protocol: TCP
      name: https
  selector:
    app: nlb-tls-app
  type: LoadBalancer
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: secure-config
data:
  app.conf: |-
    server {
      listen 8443 ssl proxy_protocol;
      real_ip_header proxy_protocol;
      set_real_ip_from 192.168.0.0/16;
      server_name <your_server_name>;

      ssl_certificate /etc/nginx/ssl/tls.crt;
      ssl_certificate_key /etc/nginx/ssl/tls.key;

      default_type text/plain;

      location / {
        return 200 "hello from pod $hostname\n";
      }
    }

Service对象中,共有三个注解:

  • aws-load-balancer-type 指定创建出NLB,且使用IP模式
  • aws-load-balancer-proxy-protocol在NLB上启用proxy protocol v2 ,以将客户端源 IP 地址传递到 pod
  • aws-load-balancer-scheme指定NLB是面向互联网的

ConfigMap对象包含 NGINX 服务器的配置:

  • real_ip_header: 用于告知 nginx 从客户端请求中的哪个头字段来获取客户端真实的 IP
  • set_real_ip_from:set_real_ip_from是一个Nginx模块,可以帮助配置服务器获取客户端的真实IP地址。当有一些代理服务器时,客户端的IP地址会被代理服务器替代,导致IP地址无法正确识别。通过使用set_real_ip_from,可以指定代理服务器的IP地址获取客户端真实IP地址。这个应该设置为EKS集群VPC的CIDR
  • server_name:我们需要在server_name指令中添加NLB的 DNS ,但我们还没有这个- NLB将在我们apply yaml 时创建, 我们稍后会对此ConfigMap进行编辑。

应用该yaml:

kubectl apply -f nlb-tls-app.yaml

在EC2控制台会看到一个新的负载均衡器,其 DNS 名称包括nlbtlsap如下所示:

image-20231204195338887

注意:配置NLB可能需要几分钟的时间。

NLB设置完毕后,我们可以编辑ConfigMap以添加新的负载均衡器地址作为server_name值, 使用命令kubectl edit configmap secure-config编辑app.confConfigMap如下所示,将该server_name字段替换为NLB DNS 名称:

Edit appconf

运行以下命令来验证应用程序是否已启动并正在运行:

kubectl get pods

image-20231204195728556

Pod 尚未显示为“Running” - 这是因为我们需要为新的NLB请求证书

为后端应用程序生成 TLS 证书

使用 AWS Private CA 颁发私有证书。

创建一个名为nlb-lab-tls.yaml的文件并将以下内容保存在其中:


kind: Certificate
apiVersion: cert-manager.io/v1
metadata:
  name: nlb-lab-tls-cert
spec:
  commonName: e2e-eks-example
  dnsNames:
    - k8s-default-nlbtlsap-5583fd0a04-3d8832058330c9f6.elb.us-west-2.amazonaws.com  # 替换为实际的NLB DNS
  duration: 2160h0m0s
  issuerRef:
    group: awspca.cert-manager.io
    kind: AWSPCAClusterIssuer
    name: demo-test-root-ca
  renewBefore: 360h0m0s
  secretName: nlb-tls-app-secret
  usages:
    - server auth
    - client auth
  privateKey:
    algorithm: "RSA"
    size: 2048

创建资源并请求证书:

kubectl apply -f nlb-lab-tls.yaml

运行以下命令验证证书是否正确颁发:

kubectl get certificate

状态显示Ready:

image-20231204200038358

现在这个新证书已准备就绪,检查之前pod的状态:

kubectl get deployments -A

应该看到显示1/1 Ready(有可能需要几分钟才能完成):

image-20231204200236933

验证端到端加密

访问nlb:

https://k8s-default-nlbtlsap-5583fd0a04-3d8832058330c9f6.elb.us-west-2.amazonaws.com

返回hello from pod...,在浏览器地址栏中有一个“锁”,表明连接是安全的。单击锁的图标,查看证书:

image-20231204200802485

接下来,验证客户端源 IP 地址是否已保留:

kubectl logs nlb-tls-app-547b78746-skmns  # 替换为实际pod的名称

输出示例:

image-20231204200958447

Pod 日志中请求的原始 IP与本机的公共 IP 匹配,该连接源自使用浏览器, 并在单个 Pod 上终止

前面实验中,已经将Root CA在本机上信任,所以在浏览器中可以正常访问。