亚马逊AWS官方博客

使用 ALB 或 API Gateway 实现 App Mesh 的 JWT 身份验证

前言

JSON Web Token (JWT) 是目前最流行的跨域认证解决方案。用户在使用 App Mesh 或直接使用 EKS 部署应用时会面临需要身份验证的情况,使用JWT能够有效解决这个问题。

AWS App Mesh 是一种服务网格,可提供应用程序级网络,让您的服务可以轻松跨多种类型的计算基础设施相互通信。App Mesh 可以与 AWS上运行的 AWS Fargate、Amazon EC2、Amazon ECS、Amazon EKS 和 Kubernetes 以及 AWS Outposts 配合使用,以更好地大规模运行您的应用程序。

App  Mesh 由以下组件构成:

  • Service Mesh
  • Virtual gateways and Gateway routes
  • Virtual services
  • Virtual nodes
  • Virtual routers and routes
  • Proxy (Envoy)

其中 Virtual gateways 是虚拟网关允许网格外部的资源与网格内部的资源进行通信。(关于其他组件的详细介绍可以参考文档

目前 Virtual gateways 本身不支持身份验证等功能。随着Istio 等方案迁移至 App Mesh 的用户越来越多,服务网格的身份验证成为一个问题。

本文会结合 AWS Application Load Balancer (ALB) 或 API Gateway,使用 JWT 授权方控制对 App Mesh 应用的访问。但本方案不局限于 App Mesh,没有集成 App Mesh 的 EKS 集群可以使用同样的方案进行配置。架构如下:

关于 ALB 和 API Gateway

ALB作为负载均衡器将传入请求分发到目标和实例。负载均衡器使用监听器来执行特定操作。我们可以利用HTTPS的监听器配置JWT鉴权。

API Gateway 是一种API管理工具,位于客户端和后端服务之间,用于拦截所有传入请求并使用不同的功能发送它们。 API Gateway 无法直接连接到容器,因此,我们使用 NLB 和 VPC links 的方式连接到容器。

关于API网关和ALB如何选择?

与其讨论两种集成的优缺点,不如谈谈他们各自的使用场景。

API Gateway 是一个无服务器组件,与 ALB 相比,它还具有许多额外的功能。如果开始一个 Web 应用程序或移动应用程序需要身份验证的新项目,那么 API Gateway 是大多数人的更好选择。API Gateway 可以把多个Mesh或多个EKS集群,以及EKS之外的服务,例如Lambda等,进行统一的管理。

如果我们管理的是一个仅在服务端提供服务的应用,我们需要将其全局或部分置于身份验证机制之后,那么 ALB 就是更好的选择。

本文会对这两种访问分别进行介绍。

本文包含的主要内容有:

  • 准备工作
  • 创建一个EKS集群
  • 安装 App Mesh 控制器 和 AWS Loadbalancer Controller
  • 创建示例应用
  • 使用ALB公开示例应用并配置JWT鉴权
  • 使用API Gateway公开示例应用并配置JWT鉴权
  • 验证

准备工作

安装AWS CLI

适用于Linux(x86)的安装方式:

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

其他平台安装参考:https://docs.aws.amazon.com/zh_cn/cli/latest/userguide/install-cliv2.html

安装eksctl工具

curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl /usr/local/bin
eksctl version

详情参考:https://docs.aws.amazon.com/zh_cn/eks/latest/userguide/eksctl.html

安装kubectl工具

适用于Linux(x86)的安装方式:

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client

其他平台安装参考:https://kubernetes.io/docs/tasks/tools/

安装Helm工具

curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh

详情参考:https://helm.sh/zh/docs/intro/install/

创建一个EKS集群

请根据实际情况替换cluster-nameRegion-code 的值

export CLUSTER_NAME=cluster-name
export AWS_REGION=Region-code

创建EKS集群

cat <<EOF > eks_cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: $CLUSTER_NAME
  region: $AWS_REGION

fargateProfiles:
  - name: fp-default
    selectors:
      - namespace: my-apps
      - namespace: kube-system
      - namespace: default
      - namespace: appmesh-system
EOF
eksctl create cluster -f eks_cluster.yaml

安装 App Mesh 控制器 和 AWS Loadbalancer Controller

安装 App Mesh 控制器

# 添加eks charts repo 到本地 helm
helm repo add eks https://aws.github.io/eks-charts
# 安装 appmesh controller CRDS
kubectl apply -k "https://github.com/aws/eks-charts/stable/appmesh-controller/crds?ref=master"
# 创建 namespace appmesh-system
kubectl create ns appmesh-system

为集群创建 OpenID Connect (OIDC) 身份提供程序

eksctl utils associate-iam-oidc-provider \
    --region=$AWS_REGION \
    --cluster $CLUSTER_NAME \
    --approve

创建对应的 IAM Role

eksctl create iamserviceaccount \
    --cluster $CLUSTER_NAME \
    --namespace appmesh-system \
    --name appmesh-controller \
    --attach-policy-arn  arn:aws:iam::aws:policy/AWSCloudMapFullAccess,arn:aws:iam::aws:policy/AWSAppMeshFullAccess \
    --override-existing-serviceaccounts \
    --approve

安装 appmesh controller

helm upgrade -i appmesh-controller eks/appmesh-controller \
    --namespace appmesh-system \
    --set region=$AWS_REGION \
    --set serviceAccount.create=false \
    --set serviceAccount.name=appmesh-controller

验证

kubectl get deployment appmesh-controller \
    -n appmesh-system -owide

详情可以参考:https://docs.aws.amazon.com/zh_cn/app-mesh/latest/userguide/getting-started-kubernetes.html#install-controller

安装 AWS Loadbalancer Controller

获取AWS账户ID和VPC ID

export AWS_ACCOUNT_ID=$(aws sts get-caller-identity | jq -r '.Account')
export VPC_ID=$(aws ec2 describe-vpcs \
    --region=$AWS_REGION \
    --filters Name=tag:alpha.eksctl.io/cluster-name,Values=$CLUSTER_NAME | \
    jq -r '.Vpcs[].VpcId')

创建 AWS Load Balancer Controller 所需的 IAM 策略

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

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

创建 AWS Load Balancer Controller 所需的IAM角色

eksctl create iamserviceaccount \
--cluster=$CLUSTER_NAME \
--namespace=kube-system \
--name=aws-load-balancer-controller \
--attach-policy-arn=arn:aws:iam::$AWS_ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy \
--override-existing-serviceaccounts \
--approve

安装 AWS Load Balancer Controller CRDS

kubectl apply -k \
"github.com/aws/eks-charts/stable/aws-load-balancer-controller//crds?ref=master"

安装 AWS Load Balancer Controller

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 \
--set vpcId=$VPC_ID \
--set region=$AWS_REGION

详情可以参考:https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.2/deploy/installation/

创建示例应用

创建示例 Namespace

cat <<EOF > namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: my-apps
  labels:
    mesh: my-mesh
    appmesh.k8s.aws/sidecarInjectorWebhook: enabled
    gateway: ingress-gw
EOF
kubectl apply -f namespace.yaml

创建示例 Mesh

cat <<EOF > mesh.yaml
apiVersion: appmesh.k8s.aws/v1beta2
kind: Mesh
metadata:
  name: my-mesh
spec:
  namespaceSelector:
    matchLabels:
      mesh: my-mesh
EOF
kubectl apply -f mesh.yaml

创建示例 VirtualNode

cat <<EOF > VirtualNode.yaml
apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualNode
metadata:
  name: nginx
  namespace: my-apps
spec:
  podSelector:
    matchLabels:
      app: nginx
  listeners:
    - portMapping:
        port: 80
        protocol: http
  serviceDiscovery:
    dns:
      hostname: nginx-deployment.my-apps.svc.cluster.local
EOF
kubectl apply -f VirtualNode.yaml

创建示例 VirtualRouter

cat <<EOF > VirtualRouter.yaml
apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualRouter
metadata:
  namespace: my-apps
  name: nginx-virtual-router
spec:
  listeners:
    - portMapping:
        port: 80
        protocol: http
  routes:
    - name: nginx-route
      httpRoute:
        match:
          prefix: /
        action:
          weightedTargets:
            - virtualNodeRef:
                name: nginx
              weight: 1
EOF
kubectl apply -f VirtualRouter.yaml

创建示例 VirtualService

cat <<EOF > VirtualService.yaml
apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualService
metadata:
  name: nginx
  namespace: my-apps
spec:
  awsName: nginx-service.my-apps.svc.cluster.local
  provider:
    virtualRouter:
      virtualRouterRef:
        name: nginx-virtual-router
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: my-apps
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 8088
      name: http
  selector:
    app: nginx
EOF
kubectl apply -f VirtualService.yaml

创建 Envoy  IAM 策略和Service Account

cat <<EOF > proxy-auth.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "appmesh:StreamAggregatedResources",
            "Resource": [
                "*"
            ]
        }
    ]
}
EOF
aws iam create-policy \
    --policy-name mesh-app-proxy --policy-document file://proxy-auth.json

eksctl create iamserviceaccount \
    --cluster $CLUSTER_NAME \
    --namespace my-apps \
    --name nginx-serviceaccount \
    --attach-policy-arn  arn:aws:iam::$AWS_ACCOUNT_ID:policy/mesh-app-proxy \
    --override-existing-serviceaccounts \
    --approve

创建示例应用

cat <<EOF > nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      serviceAccountName: nginx-serviceaccount
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
EOF

kubectl apply -f nginx.yaml -n my-apps
kubectl expose deployment/nginx-deployment -n my-apps

创建示例 VirtualGateway

cat <<EOF > virtual_gateway.yaml
---
apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualGateway
metadata:
  name: ingress-gw
  namespace: my-apps
spec:
  namespaceSelector:
    matchLabels:
      gateway: ingress-gw
  podSelector:
    matchLabels:
      app: ingress-gw
  listeners:
    - portMapping:
        port: 8088
        protocol: http
  logging:
    accessLog:
      file:
        path: /dev/stdout
---
apiVersion: appmesh.k8s.aws/v1beta2
kind: GatewayRoute
metadata:
  name: gateway-route
  namespace: my-apps
spec:
  httpRoute:
    match:
      prefix: "/"
    action:
      target:
        virtualService:
          virtualServiceRef:
            name: nginx
---
apiVersion: v1
kind: Service
metadata:
  name: ingress-gw
  namespace: my-apps
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 8088
      name: http
  selector:
    app: ingress-gw
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ingress-gw
  namespace: my-apps
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ingress-gw
  template:
    metadata:
      labels:
        app: ingress-gw
    spec:
      serviceAccountName: nginx-serviceaccount
      securityContext:
        fsGroup: 65534
      containers:
        - name: envoy
          image: 840364872350.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.17.2.0
          ports:
            - containerPort: 8088
EOF

kubectl apply -f virtual_gateway.yaml

a. 使用ALB公开示例应用并配置JWT身份验证

在 ALB 中配置 JWT 身份验证需要通过 HTTPS 访问 ALB,这就意味着你需要准备一个自己的域名,下面会从在 ACM 中申请免费的公有证书开始。如果你目前还没有一个自己的域名可以使用,可以考虑通过Route 53购买,或使用接下来的 API Gateway 方案。

创建 ACM 公有证书

打开 AWS ACM 的控制台

  • 在证书管理中选择请求证书
  • 选择请求公有证书
  • 填写域名,可以填写完全限定域名,例如你的域名为 com,可以填写 jwt.example.com。或者填写泛域名*example.com
  • 验证方式中,选择 DNS 验证
  • 确认并请求,并在你的域名解析服务商(这可能是Route 53 或其他平台)中填写对应解析,完成验证
  • 记录证书的 ARN,替换下面命令中cert-arn 为对应值
    export CERT_ARN=cert-arn

创建 Cognito 用户池

打开 AWS 控制台,选择 Cognito 服务,选择用户池,点击创建用户池

  1. 填写用户池名称(例如alb_jwt)
  2. 点击查看默认配置
  3. 创建池
  4. 在左侧导航栏里选择域名,填写域名前缀,检查可用并保存更改,并记录域名值,替换下面命令中domain为对应值
        export DOMAIN=domain
  5. 在左侧导航栏里选择应用程序客户端,添加应用程序客户端,输入应用程序客户端名称,并勾选身份验证流程配置中,启用基于用户名密码的身份验证(ALLOW_USER_PASSWORD_AUTH)。最后点击创建应用程序客户端,并记下应用程序客户端 ID。
        export CLIENT_ID=client-id
        export USER_POOL_ID=$(aws cognito-idp list-user-pools --region $AWS_REGION --max-results 10 | jq -r '.UserPools[] | select(.Name == "alb_jwt") .Id')
  6.  在左侧导航栏里选择应用程序客户端设置。启用身份提供商,并勾选Cognito User Pool。回调 URL中填写 https:// 和你的域名地址,并添加后缀 /oauth2/idpresponse,例如 https://jwt.example.com/oauth2/idpresponse。允许的 OAuth 流程中勾选 Authorization code grant 和 Implicit grant。允许的 OAuth 范围中勾选email,openid,profile。最后点击保存更改。

创建并配置ALB

创建示例 VirtualGateway 的 Internal ALB Ingress

cat <<EOF > ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: my-apps
  name: nginx
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/auth-type: cognito
    alb.ingress.kubernetes.io/auth-idp-cognito: '{"userPoolARN":"arn:aws:cognito-idp:$AWS_REGION:$AWS_ACCOUNT_ID:userpool/$USER_POOL_ID","userPoolClientID":"$CLIENT_ID","userPoolDomain":"$DOMAIN"}'
    alb.ingress.kubernetes.io/auth-on-unauthenticated-request: authenticate
    alb.ingress.kubernetes.io/auth-session-cookie: cognito
    alb.ingress.kubernetes.io/auth-scope: openid
    alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]'
spec:
  rules:
    - http:
        paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: ingress-gw
              port:
                number: 80
EOF

kubectl apply -f ingress.yaml

配置域名解析

最后将你的域名地址解析到ALB的域名上:

如果你的域名托管在AWS Route 53上,可以通过A记录,使用别名方式,选择区域和对应的ALB的方式配置域名解析。

如果你的域名没有托管在AWS Route 53,可以通过以下命令查看 ALB DNS,自行进行域名解析配置。

aws elbv2 describe-load-balancers --region $AWS_REGION  | \
    jq -r '.LoadBalancers[] | select(.LoadBalancerName | startswith("k8s-myapps-nginx"))'.DNSName

验证通过 ALB 进行 JWT 身份验证

可以在浏览器中通过HTTPS访问你的域名,会跳转到Cognito Host UI进行登录或注册,登录或注册成功后,会跳转回部署的实例应用。

b. 使用 API Gateway公开示例应用并配置 JWT 身份验证

更新 App Mesh Virtual Gateway SVC 配置,使其通过Internal NLB在VPC内暴露

cat <<EOF > virtual_gateway_svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: ingress-gw
  namespace: my-apps
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-scheme: internal
    service.beta.kubernetes.io/aws-load-balancer-type: nlb-ip
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 8088
      name: http
  selector:
    app: ingress-gw
EOF

kubectl apply -f virtual_gateway_svc.yaml

使用下列命令确保 NLB 已经就绪,一般需要等待1-2分钟

kubectl get svc -n my-apps

创建AWS API Gateway

获取子网 IDs和安全组 ID

export SUBNET_IDS=$(aws ec2 describe-subnets \
    --region $AWS_REGION \
    --filters "Name=vpc-id,Values=$VPC_ID" | \
    jq -r '.Subnets[].SubnetId' | \
    sed  ":a;N;s/\\n/ /g;ta")

export SG_ID=$(aws ec2 describe-security-groups \
    --region $AWS_REGION \
    --filters Name=tag:ingress.k8s.aws/stack,Values=my-apps/nginx | \
    jq -r '.SecurityGroups[].GroupId')

获取ELB URI和LISTENER URI

export ELB_URI=$(aws elbv2 describe-load-balancers --region $AWS_REGION  | \
    jq -r '.LoadBalancers[] | select(.LoadBalancerName | startswith("k8s-myapps-ingress")).LoadBalancerArn')

export LISTENER_URI=$(aws elbv2 describe-listeners \
    --region $AWS_REGION --load-balancer-arn $ELB_URI | \
    jq -r '.Listeners[] | select(.Port == 80) .ListenerArn')

创建API Gateway VPC Links

aws apigatewayv2 create-vpc-link \
    --region $AWS_REGION \
    --name nginx_vpclink \
    --subnet-ids $SUBNET_IDS \
    --security-group-ids $SG_ID
export VPC_LINK_ID=$(aws apigatewayv2 get-vpc-links \
    --region $AWS_REGION | jq -r '.Items[] | select(.Name == "nginx_vpclink") .VpcLinkId')

创建 HTTP API Gateway

aws apigatewayv2 create-api \
    --region $AWS_REGION \
    --name nginx_http_api \
    --protocol-type HTTP
export API_ID=$(aws apigatewayv2 get-apis \
    --region $AWS_REGION | jq -r '.Items[]  | select(.Name == "nginx_http_api") .ApiId')

创建 HTTP API Gateway 路由

aws apigatewayv2 create-route \
    --region $AWS_REGION \
    --api-id $API_ID \
    --route-key 'ANY /'
export ROUTE_ID=$(aws apigatewayv2 get-routes \
    --api-id $API_ID --region $AWS_REGION | \
    jq -r '.Items[]  | select(.RouteKey == "ANY /") .RouteId')

创建 HTTP API Gateway 集成

aws apigatewayv2 create-integration \
    --region $AWS_REGION \
    --api-id $API_ID \
    --integration-method ANY \
    --connection-id $VPC_LINK_ID \
    --connection-type VPC_LINK \
    --integration-type HTTP_PROXY \
    --payload-format-version 1.0 \
    --integration-uri $LISTENER_URI
export INTEGRATION_ID=$(aws apigatewayv2 \
    get-integrations --api-id $API_ID \
    --region $AWS_REGION | jq -r '.Items[].IntegrationId')

配置 HTTP API Gateway 路由

aws apigatewayv2 update-route \
    --region $AWS_REGION \
    --api-id $API_ID  \
    --route-id $ROUTE_ID  \
    --target  integrations/$INTEGRATION_ID

创建HTTP API Gateway 自动部署阶段

aws apigatewayv2 create-stage \
    --region $AWS_REGION \
    --api-id $API_ID \
    --auto-deploy \
    --stage-name '$default'

等待1-2分钟后,创建的App Mesh应用已经可以通过API Gateway访问,可以在 AWS API Gateway 控制台中,通过选择对应API和阶段,看到调用 URL,并用其访问我们部署的应用。

使用 JWT 身份验证进行鉴权

由于现在所有人都可以访问API Gateway。出于安全性考虑,需要对API Gateway的访问进行鉴权

创建Cognito用户池

打开 AWS 控制台,选择 Cognito 服务,选择用户池,点击创建用户池

1. 填写用户池名称 (例如apigw_jwt)

2. 点击查看默认配置

3. 创建池

4. 在左侧导航栏里选择域名,填写域名前缀,检查可用并保存更改,并记录域名值,替换下面命令中domain为对应值

export DOMAIN_API=domain

5. 左侧导航栏里选择应用程序客户端,添加应用程序客户端,输入应用程序客户端名称,并勾选身份验证流程配置中,启用基于用户名密码的身份验证 (ALLOW_USER_PASSWORD_AUTH)。最后点击创建应用程序客户端,并记下应用程序客户端 ID。

    export CLIENT_ID_API=client-id
    export USER_POOL_ID_API=$(aws cognito-idp list-user-pools --region $AWS_REGION --max-results 10 | jq -r '.UserPools[] | select(.Name == "apigw_jwt") .Id')

6. 左侧导航栏里选择应用程序客户端设置。启用身份提供商,并勾选 Cognito。回调 URL中填写 API Gateway 中 API 地址。允许的 OAuth 流程中勾选Authorization code grant 和 Implicit grant。允许的 OAuth 范围中勾选email,openid,profile。最后点击保存更改。

为API Gateway增加鉴权

创建 API Gateway鉴权

aws apigatewayv2 create-authorizer \
    --name jwt \
    --region $AWS_REGION \
    --api-id $API_ID  \
    --authorizer-type JWT \
    --identity-source '$request.header.Authorization' \
    --jwt-configuration Audience=$CLIENT_ID_API,Issuer=https://cognito-idp.$AWS_REGION.amazonaws.com/$USER_POOL_ID_API
export AUTHORIZATION_ID=$(aws apigatewayv2 \
    get-authorizers --region $AWS_REGION --api-id $API_ID | \
    jq -r '.Items[] | select(.Name == "jwt") .AuthorizerId')

更新API Gateway路由

aws apigatewayv2 update-route \
    --region $AWS_REGION \
    --api-id $API_ID  \
    --route-id $ROUTE_ID  \
    --authorization-type JWT \
    --authorizer-id $AUTHORIZATION_ID

验证

此时已经可以发现,刚才可以访问的 API Gateway 的调用URL 已经不能直接访问,返回内容变成{“message”:”Unauthorized”}。

我们可以借助API 请求工具 Postman 模拟 JWT 请求。

  1. Request URL 填写 API Gateway 的调用URL,并选择 Get 方法
  2. 选择 Authorization,Type选择 OAuth 2.0
  3. Add authorization data to 选择 Request Headers
  4. 右侧在 Configure New Token 下,填写 Token 名称
  5. Grant Type 选择 Implicit
  6. Callback URL 填写 API Gateway 的调用URL,例如https://pbsvtevsad.execute-api.eu-central-1.amazonaws.com/
  7. Auth URL 填写 Cognito User Pool 域名,并添加后缀 /login,例如https://example.auth.eu-central-1.amazoncognito.com/login
  8. 填写Client ID
  9. 点击Get New Access Token

此时,如果一切正常,会弹出一个网页,登陆或注册后,Postman会自动收集 JWT 的 Access Token

  1. 在Header Prefix处,填写Bearer
  2. 发送请求,可以看到收到了正确的返回

清理环境

删除 API Gateway 部分

aws apigatewayv2 delete-api --api-id $API_ID --region $AWS_REGION
aws apigatewayv2 delete-vpc-link --vpc-link-id $VPC_LINK_ID --region $AWS_REGION

aws cognito-idp delete-user-pool-domain --user-pool-id $USER_POOL_ID --domain $DOMAIN_API --region $AWS_REGION
aws cognito-idp delete-user-pool --user-pool-id $USER_POOL_ID_API --region $AWS_REGION

删除 ALB 部分

aws cognito-idp delete-user-pool-domain --user-pool-id $USER_POOL_ID --domain $DOMAIN --region $AWS_REGION
aws cognito-idp delete-user-pool --user-pool-id $USER_POOL_ID --region $AWS_REGION

kubectl delete ingress nginx -n my-apps
aws acm delete-certificate --certificate-arn $CERT_ARN --region $AWS_REGION

并登陆控制台删除Route 53解析记录

删除EKS集群

kubectl delete -f virtual_gateway_svc.yaml
eksctl delete cluster $CLUSTER_NAME --region $AWS_REGION

结束语

本文使用两种方式,分别是

  • ALB + Cognito 的方式简单快捷地实现 App Mesh 的 JWT 身份验证
  • API Gateway + Cognito 以无服务的方式为后端服务鉴权,也方便进行深度的API 管理,例如黑、白名单鉴权,参数转换等

但这只是实现鉴权的多种方式之一。例如API Gateway也可以直接使用如果有更复杂的鉴权策略和场景,可以考虑使用Lambda等方式配合API Gateway自行实现鉴权逻辑。

本文虽然是从 App Mesh Virtual Gateway JWT 身份验证出发,讨论了JWT身份验证方案的实现,但 ALB 或 API Gateway JWT 身份验证方案本身不局限于使用 App Mesh 的 Kubernetes 集群。没有集成 App Mesh 的 Kubernetes可以使用同样的方式配置鉴权,欢迎大家自行探索。

本篇作者

王逸飞

亚马逊云科技解决方案架构师,负责基于亚马逊云科技 的云计算方案的咨询与架构设计,同时致力于亚马逊云科技云服务知识体系的传播与普及。在微服务,容器等领域拥有多年经验。