亚马逊AWS官方博客

基于 AWS IoT 证书授权最终用户访问云上资源

前言

企业在 AWS 上构建自己的应用系统时,常常需要授权应用系统的最终用户直接访问 AWS 资源(例如:允许最终用户访问非公开权限的 Amazon S3 存储桶)。AWS 通过 Identity and Access Management (IAM)服务授权用户访问 AWS 资源。在面对最终用户时,企业可以选择 Amazon Cognito 结合 IAM从应用程序端控制 AWS 资源访问。这需要企业将 Amazon Cognito 与管理最终用户的身份提供商(IDP, Identity Provider)进行集成,以实现 AWS 资源访问控制[1]

本文将会介绍一种基于 x.509 证书的认证方式,并结合 AWS Identity and Access Management (IAM) 服务完成对相关资源的授权。从实现角度上看,该方式具有如下特点:

  1. 无需与第三方 IDP 即成,实现简单;
  2. 无需预付费用,适合对成本敏感的企业;
  3. 基于 509 的认证方式,安全可靠

 

关于 AWS IoT

在 AWS 上,用户可以通过 AWS Certificate Manager 或 AWS IoT 服务获取 x.509 证书。本文所介绍的方案涉及到基于证书的认证、授权,将使用 AWS IoT 服务对证书进行管理。

在介绍方案之前,先来简要介绍一下 AWS IoT[2] 服务。AWS IoT 通过“物品 (Thing)”、“证书 (Certificate)”、“策略 (Policy)”来管理物联设备。物品可以属于一个或多个物品组 (Thing Group),策略可以关联给物品、物品组、或物品组中包含的其它物品组。此外,物品还可以属于某一种物品类型 (Thing Type),属于物品类型的物品可以拥有最多 50 个属性 (Attribute) 用于描述物品,而不属于任何物品类型的物品最多只能拥有 3 个属性。下图所表示的是它们之间的对应关系:

图1:AWS IoT 基本概念

 

用户管理体系设计

借助 AWS IoT 证书实现对最终用户的认证、授权管理,本质上就是将最终用户在逻辑上视作一个物联设备,借助 AWS IoT 中的“物品”及物品属性的概念管理用户信息,利用 AWS IoT 证书的认证、授权过程实现针对最终用户的认证、授权。接下来,我将进一步阐述这一对应关系。

概念对应关系

  • 用户信息 vs 物品属性:

通常,在应用系统中进行用户管理时往往需要一个数据库来记录用户信息,例如记录用户的 ID、姓名、电话、邮箱等。AWS IoT 中的物品可以通过属性的方式来记录物品的元数据,这些元数据可以用来表示最终用户的基本信息。

  • 用户组 vs 物品组:

用户组通常用来简化对一批同类用户进行授权的过程。在 AWS IoT 服务中,物品组在关联策略之后可以向组内所有物品进行统一授权,这与用户组的概念非常接近。本文中的方法也是用 AWS IoT 物品组代表用户组。

  • 公司/组织 vs 物品类型:

每一个物品仅能属于一个物品类型,正如一个用户往往只属于一个公司或组织。在本文中将使用 AWS IoT 物品类型代表现实中的公司。这对于那些多租户的 SaaS 应用非常必要。

整体对应关系如下表示:

图2:概念对应

权限设计

借助关联给物品的证书,AWS IoT 服务与 AWS IAM、AWS STS 等服务集成,可以授权物品直接访问 AWS 资源和服务。这一过程如下图所示:

图3:借助 AWS IoT 服务直接访问 AWS 资源

上图所示过程可以在官方文档上找到详细说明[3],本文不再赘述。概括而言,这一过程可以细分为两次验证、两次授权:

  1. AWS IoT 服务验证证书的有效性,完成认证过程;
  2. AWS IoT 对用户进行授权,允许通过 AWS STS 服务继承特定的 IAM 角色,获得临时用户凭证(AKSK);
  3. 用户使用临时用户凭证对 API 调用请求进行签名,AWS 验证签名的有效性;
  4. 验证请求有效后,根据对应的 IAM 角色授予相关 AWS 资源的访问权限。

验证有效性的工作由 AWS 服务自动完成,用户只需对上述 2、4 两个环节中涉及到的权限进行设置:

  • AWS IoT 权限:授权所使用的证书可以使用 sts:AssumeRole。这是必须授予的权限,是后续操作正常完成的前提。
  • AWS IAM Role 权限:授予用户访问具体 AWS 服务的权限。该权限根据不同应用、不同用户进行对应的设置。

在实际应用过程中往往通过“用户组”和/或 传递变量的方式来简化授权过程。如前文中所提到的,我们可以使用 AWS IoT Thing Group 作为“用户组”实现批量授权。在变量方面,AWS IAM 可以接受的 AWS IoT 变量类型如下:

  • credentials-iot:ThingName
  • credentials-iot:ThingTypeName
  • credentials-iot:AwsCertificateId

我们可以利用这些变量实现只使用一个 AWS IAM Role 就可以针对不同用户授予不同权限的目的(例如:每一个用户仅可以访问 Amazon S3 存储桶中自己的目录)。

下面我将介绍上述设计的原型实现,以帮助大家更好的理解这一设计思路。

 

原型实现

此部分中如未做特殊说明,均使用 Linux 环境,并安装 awscli 工具。关于 awscli 的安装,请参考官方文档[4]。对于所使用到的 aws api 说明,请参考在线帮助[5]。该原型使用宁夏区域(cn-northwest-1)资源。

需要说明的是,该方法同样适用于海外的其它区域,您无需修改代码,只需要注意替换相关资源的 ARN 为海外区域的 ARN 即可。

该原型将模拟一名最终用户,以 IoT 证书作为自己的用户凭证,完成验证后可以访问 Amazon S3 存储桶中以自己名字命名的目录(读写权限),但无法对其它目录拥有写权限。

 

创建用户载体

根据本文“概念对应关系”章节中的内容,首先需要在 AWS IoT 服务中创建出“公司”、“最终用户”、“用户授权组”的对应的载体,即:ThingType、Thing 和 ThingGroup,以及用户用来作为自身凭据的 x.509 证书。

  1. 创建 ThingType(公司)

aws iot create-thing-type \

--thing-type-name "AWS" \

--thing-type-properties "thingTypeDescription=testing, searchableAttributes=login,emailAddress,assumeRole"

 

  1. 创建 Thing(最终用户)

aws iot create-thing --cli-input-json file://user-liuwp001-s3.json

 

其中,文件 user-liuwp001-s3.json 用于描述 Thing 的相关信息,示例如下:

{
  “thingName”: “liuwp001-s3”,
  “thingTypeName”: “AWS”,
  "attributes": {
    "JobRole": "PSA",
    "Company": "AWS",
    "FamilyName": "Liu",
"GivenName": "WP",
"emailAddress": "xxxxxxxx",
    "assumeRole": "S3User"
  }
}

其中的 thingName 就是用户在应用系统中的用户名,也是后续 S3 存储桶中用户目录的目录名。而属性信息其实就是用户自身的注册信息,它们将会以 Thing 属性的形式被保留在 AWS IoT 服务中。

  1. 创建 ThingGroup(授权组)

aws iot create-thing-group --cli-input-json file://s3-access.json

 

其中,文件 s3-access.json 用于描述该 ThingGroup 的相关信息,示例如下:

{
    "thingGroupName": "s3-access",
    "thingGroupProperties": {
        "thingGroupDescription": "This group is for s3 access based on different user.",
        "attributePayload": {
            "attributes": {
                "securityLevel": "High"
            },
            "merge": true
        }
    }
}

 

  1. 为 Thing 创建证书

执行以下命令时,THING_NAME 、证书保存的目录都可以根据自己的需要进行设定。

THING_NAME=user-liuwp001-s3

aws iot create-keys-and-certificate --set-as-active \
    --public-key-outfile ~/iot-cert/$THING_NAME/$THING_NAME.public.key \
    --private-key-outfile ~/iot-cert/$THING_NAME/$THING_NAME.private.key \
    --certificate-pem-outfile ~/iot-cert/$THING_NAME/$THING_NAME.certificate.pem

 

上述命令成功执行后会返回所生成证书的 ARN,执行以下命令将证书关联到之前创建的 Thing 上:

aws iot attach-thing-principal --thing-name $THING_NAME --principal <YOUR_CERT_ARN>

生成的证书文件需要交付到最终用户手中。

 

授权设置

根据前文“权限设计”中的介绍,本方案中有两处权限需要设置:AWS IAM Role 和 AWS IoT。

  1. AWS IAM 中进行权限设置:

关于 AWS IAM Role 的详细概念说明可以参考官方文档[6],本文不再赘述。下面着重介绍如何创建出符合本文设定场景的 IAM Role。

 

此处创建的 IAM Role 需要被 AWS IoT 的 credentials(见本文图3)服务所调用。因此,这个 Role 的信任实体应当设置为 credentials.iot.amazonaws.com。执行以下命令创建 IAM Role,请注意将 <YOUR_ROLE_NAME> 根据需要进行替换。

aws iam create-role --role-name <YOUR_ROLE_NAME> --assume-role-policy-document file://trustpolicy.json

 

其中 trustpolicy.json 的内容示例如下:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "credentials.iot.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

 

Role 创建出来后需要和 IAM 策略相关联,以便授予对应权限。本文中最终用户通过 IoT 证书所获得的权限是访问专门的 S3 存储桶(只读),仅对以自己名字命名的目录拥有读写权限。通过以下方式创建用于描述 IAM 策略的 json 文件,在 IAM 策略中接受 credentials-iot:ThingName 变量。

 

使用过程中注意将 <YOUR_BUCKET> 和 <BUCKET_PREFIX> 根据需要进行替换。

cat >s3-access-policy.json <<eof
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws-cn:s3:::<YOUR_BUCKET>",
            "Condition": {
                "StringLike": {
                    "s3:prefix": [
                        "<BUCKET_PREFIX>/${credentials-iot:ThingName}/*",
                        "<BUCKET_PREFIX>/${credentials-iot:ThingName}/"
                    ]
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "s3:GetObject",
            "Resource": "arn:aws-cn:s3::: <YOUR_BUCKET>/*"
        },
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": [
                "arn:aws-cn:s3::: <YOUR_BUCKET>/<BUCKET_PREFIX>/${credentials-iot:ThingName}",
                "arn:aws-cn:s3::: <YOUR_BUCKET>/<BUCKET_PREFIX>/${credentials-iot:ThingName}/*"
            ]
        }
    ]
}
eof

 

基于该内容创建 IAM 策略,并关联到之前创建的 IAM Role 上:

# 创建 IAM Policy,并记录结果中的 ARN

aws iam create-policy --policy-name sobey-cctv-reporter --policy-document file://s3-access-policy.json

 

aws iam attach-role-policy --role-name <ROLE_NAME> --policy-arn <POLICY_ARN>

 

  1. AWS IoT 中进行权限设置:

AWS IoT 不能直接使用 IAM Role,需要先在 AWS IoT 服务中为这个 IAM Role 创建别名:

aws iot create-role-alias --role-alias <ROLE_ALIAS> --role-arn <IAM_ROLE_ARN> --credential-duration-seconds 3600

 

<ROLE_ALIAS>可根据需要自行定义,<IAM_ROLE_ARN> 为之前所创建的 IAM Role 的 ARN。

 

接下来,需要为 IoT 的证书关联一个 IoT 策略,以允许该证书通过刚刚创建的别名使用 IAM Role。创建策略命令如下:

aws iot create-policy --policy-name <Policy_NAME> --policy-document file:// <Policy_NAME>.json

 

其中 <Policy_NAME>.json 内容如下:

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "iot:AssumeRoleWithCertificate",
    "Resource": "<ALIAS_ARN>",
    "Condition": {
      "StringEquals": {
        "iot:Connection.Thing.Attributes[assumeRole]": "S3User",
        "iot:Connection.Thing.ThingTypeName": "AWS"
      },
      "Bool": {
        "iot:Connection.Thing.IsAttached": "true"
      }
    }
  }
}

 

策略中增加了 Condition 字段,以减少发生用户元数据错填、加错 ThingGroup 时错误的获取了相关权限的情况。

 

将创建好的 IoT 策略关联给之前创建的 ThingGroup,这样加入到 ThingGroup 中的所有 Thing 都会获得调用 IAM Role 的权限(被调用的 Role 通过 IoT 的角色别名进行了限制):

aws iot attach-policy --policy-name <POLICY_NAME> --target <THINGGROUP_ARN>

 

接下来只要将之前创建的 Thing 加入到这个 ThingGroup 中即可:

aws iot add-thing-to-thing-group --thing-name <YOUR_THING_NAME> --thing-group-name <GROUP_NAME>

 

验证

最终用户在获得 IoT 证书后,可以通过该证书访问 AWS IoT Credentials Provider,通过 Credentials Provider 访问 AWS STS 服务以换取临时 credentials。

 

首先需要获得自己的 AWS 账号内 IoT Credentials Provider 的 endpoint:

aws iot describe-endpoint --endpoint-type iot:CredentialProvider

返回结果的格式应为:xxxxxxxxx.credentials.iot.cn.<REGION>.amazonaws.com.rproxy.goskope.com.cn

 

通过标准的 curl 命令即可访问上述 endpoint,命令中需包含 Thing 证书、Thing 私钥、用于验证 AWS IoT Core 服务器身份的根 CA 证书。根 CA 证书的获取可参考官方文档[7]

 

请求临时 credentials 命令如下:

curl –cert <THING_CERT> --key <THING_PRIVATE_KEY> -H "x-amzn-iot-thingname: <THING_NAME>" –cacert <AWS_ROOT_CA> https://<CREDENTIALS_ENDPOINT>/role-aliases/<ALIAS_NAME>/credentials > temptoken

 

在 temptoken 文件中可以找到所需的 access key id, secret access key。如安装了 jq 工具,也可通过如下方式设置环境变量:

export AWS_ACCESS_KEY_ID=$(jq -r ".credentials.accessKeyId" temptoken)

export AWS_SECRET_ACCESS_KEY=$(jq -r ".credentials.secretAccessKey" temptoken)

export AWS_SESSION_TOKEN=$(jq -r ".credentials.sessionToken" temptoken)

 

设置完成后,即可通过 awscli 或 SDK 工具操作 s3 存储桶中的对应目录。

 

检索

实际应用过程中可能存在大量用户(Thing)的情况,为了快速查找相关用户,可以利用 AWS IoT 提供的检索功能。

 

激活 Fleet Indexing 功能:

aws iot update-indexing-configuration \

--thing-indexing-configuration thingIndexingMode=REGISTRY_AND_SHADOW

 

根据需要检索,例如查找 Company 为 AWS、assumeRole 为 S3User 的用户信息(即 Thing 信息):

aws iot search-index \

--query-string "attributes.Company:AWS AND attributes.assumeRole:S3User"

 

总结

本文介绍了如何借助 AWS IoT 服务实现用户的授权、认证管理,并以访问 S3 存储桶中的资源为例介绍了如何实现原型系统的搭建。该方案具有如下“三高一低”的特点:

  1. 低成本:方案中无需部署实体服务器,没有资源预部署的需求;
  2. 高可靠:验证、授权过程均由 AWS 托管服务完成,其可靠性与 AWS 平台的整体可靠性相同;
  3. 高安全:用户访问云上资源所需的凭证均为动态凭证,证书本身则没有访问 AWS 云上资源的权限;
  4. 高灵活:通过 ThingGroup+变量 的方式实现灵活的授权管理,而无需为每个最终用户创建单独的权限策略。

 

当然,实际应用环境中的需求远比本文介绍的原型更负责,您需要根据自己的实际需求进行调整。必要时可以联系与您接口的 AWS 人员做进一步的讨论。

 

参考文档

[1] Amazon Cognito Identity Pools (Federated Identities)

[2] What is AWS IoT

[3] Authorizing direct calls to AWS services

[4] Installing, updating, und uninstalling the AWS CLI

[5] AWS CLI Command Reference

[6] IAM roles

[7] CA certificates for server authentication

 

本篇作者

刘伟平

AWS APN 合作伙伴解决方案架构师,主要负责 AWS (中国)合作伙伴的技术支持工作,同时致力于 AWS 云服务在国内的应用及推广。加入 AWS 前,在 HP(HPE)服务超过7年,历任存储售前工程师、电信行业售前工程师、NFV 解决方案架构师,熟悉传统企业 IT 架构、私有云及混合云部署。