亚马逊AWS官方博客

IOT 终端设备如何安全地往 S3 传输文件

前序

物联网设备通常使用 X.509 证书通过 Mqtt协议连接到 AWS IOT平台,通过Pub/Sub的方式进行交互。随着硬件设备的功能愈加强大,更多非控制的数据需要与云端进行交互。例如需要把更新包/插件从S3下载到硬件端,或者把视频数据从IP Camera传输到S3进行存储,这些场景下终端设备需要跟S3进行交互。与Mqtt使用X.509证书进行身份认证不同,S3(包括其他的AWS服务)使用Signature Version 4,也就是Access Key和Secret Access Key的方式进行身份认证(具体的签名过程已经在AWSCLI和SDK底层封装实现,只需要提供Access Key和Secret Access Key即可)。基于安全的考虑,AWS建议通过STS服务向用户群体颁发临时凭证(包含Access Key、Secret Access Key和Session Token),在短时间内向合法用户授予细粒度的权限,从而保证授权处在可控的范围。为了保证用户的合法性,我们往往需要通过登陆等逻辑模块进行身份验证,通过验证后再向用户颁发临时凭证,这个流程大概如下图所示:

上述方案看起来不错,提供足够的安全性。但在物联网场景中,终端设备往往是百万级别甚至是千万级别的,客户需要搭建集群支持登陆模块庞大的访问量。既然AWS把IOT打包成一个托管服务,那AWS能否把认证和分发临时凭证添加到IOT功能模块中呢?

答案是肯定的,这也是本文将要介绍的功能。在物联网场景中一个天然的优势是,大部分的硬件设备在出厂时内嵌了X.509证书,X.509证书在很大程度上能够作为合法凭证。AWS IOT最新推出的特性,能够复用这套身份检验方式,客户不再需要搭建登陆等组件进行合法性认证,就能够获取临时凭证。具体做法是设备端发起一个Https连接请求临时凭证,把X.509证书作为Https建立SSL通道的客户端证书来做身份验证,具体流程如下图所示。除了S3外,IOT终端设备也可以通过这种方法获取其他基于Signature Version 4的AWS服务的权限。本文将会对这项功能特性的配置流程进行介绍。

概述

如下图所示,配置这项功能需要以下四个步骤:

(1)    步骤一:配置终端设备获取临时凭证的连接。在终端设备上配置必要的证书和证书链,并通过CLI工具得到当前AWS账户获取临时凭证的终端节点。

(2)    步骤二:定义临时凭证的权限。在IAM Role配置信任关系,并赋予相应访问权限。

(3)    步骤三:实现AWS IOT组件与IAM Role的关联。

(4)    步骤四:进一步精细化权限控制。

设置步骤

一.设备端的准备工作

设备通过Https在AWS IOT的443端口上获取临时凭证。在建立Https时,您需要把以下证书加载到设备端:

(1)X.509证书以及私钥。这两个是Mqtt建立通讯时的客户端证书文件。如果之前通过IOT Mqtt建立通讯,这两个文件已经存在于设备端。

(2)RSA Amazon Root CA 1和 VeriSign Class 3 Public Primary G5 根 CA 证书。这两个文件是建立https时,AWS端的证书链文件。为了防止设备端环境不信任AWS的证书链造成SSL通道建立不成功,需要把这两个文件放置在设备端。

(3)查询AWS Account获取临时凭证的终端节点。需要注意的是,该终端节点不同于IOT终端节点,且暂时不支持通过AWS控制台进行获取。我们可以通过AWSCLI工具的describe-endpoint CLI命令进行获取,命令如下:

aws iot describe-endpoint –endpoint-type iot:CredentialProvider —region ap-southeast-1

需要注意的是,必须指定参数 endpoint-type iot:CredentialProvider ,否则无法获取临时凭证的endpoint。

返回的endpoint格式如下所示:

<your-iot-credentialprovider-id>.credentials.iot.<region>.amazonaws.com

最终访问的URL为:

https://<your-iot-credentialprovider-id>.credentials.iot.<region>.amazonaws.com:443/role-aliases/<your-role-alias>/credentials

二.AWS IAM Service的准备工作

设备端获取的临时凭证的权限在IAM Role中定义,我们需要创建一个新的IAM Role,并把设备端需要获取的权限(例如从S3下载文件)赋予该角色。这部分的工作有两点需要注意:

(1)创建完IAM Role后,需要把该IAM Role的信任关系编辑修改为以下内容:

{

  "Version": "2012-10-17",

  "Statement": [

    {

      "Effect": "Allow",

      "Principal": {

        "Service": "credentials.iot.amazonaws.com"

      },

      "Action": "sts:AssumeRole"

    }

  ]

}

可以注意到,上述信任关系与直接把IOT服务作为受信任实体有所不同。当前无法在控制台直接选择 credentials.iot 作为受信任实体,所以需要在创建IAM Role后把信任关系修改为上述内容。该信任关系表明:信任实体credentials.iot可以切换到该IAM Role,获取该IAM Role所拥有的权限。

(2)把设备端需要获取的权限策略Policy附加给IAM Role。例如:如果您希望设备端能够对vincent-iot这个S3 Bucket下的文件进行操作,IAM Policy的内容如下所示:

{

    "Version": "2012-10-17",

    "Statement": [

        {

            "Effect": "Allow",

            "Action": [

                "s3:ListAllMyBuckets",

                "s3:HeadBucket",

                "s3:ListObjects"

            ],

            "Resource": "*"

        },

        {

            "Effect": "Allow",

            "Action": "s3:*",

            "Resource": "arn:aws:s3:::vincent-iot/*"

        }

    ]

}

您可能注意到,上述IAM Policy的权限对于每个设备提供的权限是一样的,权限粒度不够精细。如果希望对每个设备端的权限进行更细粒度的控制,例如每个设备只能访问其在S3某一个目录下的文件,可以在上述Policy中通过添加IOT策略变量来实现。具体我们会在第四部分加以详述。

三.AWS IOT Service的准备工作

Https获取临时凭证的过程中,AWS通过X.509证书来做身份识别。每个X.509证书对应AWS IOT平台的一个Credential。要完成Credential跟IAM Role的关联,需要做以下两步关键操作:

(1)创建角色别名。角色别名是AWS IOT新增加的一个组件,用于把IAM Role映射到AWS IOT组件。我们可以在AWS IOT控制台创建一个角色别名,在角色别名中指定第二部分中创建的IAM Role作为目标,如下图所示:

(my_iot_role_alias为角色别名;my-iot-role为第二部分IAM Role的名称)

我们留意到,请求临时凭证的URL的相对路径中,包含了角色别名<your-role-alias>。因此可以看到,IAM Role的ARN并没有硬编码到URL中。当需要修改IAM Role时,我们只需要在角色别名中修改其所指向的IAM Role ARN即可,无需修改设备端的URL。角色别名起到解耦的作用。

另外需要注意的是,临时凭证的有效期是在创建角色别名时设定的,默认为900秒。AWS控制台暂时不提供有效期的选项,但我们可以在AWSCLI创建角色别名时通过参数credentialDurationInSeconds进行指定,如下所示:

aws iot create-role-alias –role-alias my_iot_role_alias –role-arn arn:aws:iam::123456789012:role/my-iot-role –credential-duration-seconds 3600 –region ap-southeast-1

(2)在每个设备Credential attach的IOT Policy中增加iot:AssumeRoleWithCertificate权限,使设备拥有代入角色别名的权限。IOT Policy的内容如下所示:

{

  "Version": "2012-10-17",

  "Statement": [

    {

      "Effect": "Allow",

      "Action": "iot:AssumeRoleWithCertificate",

      "Resource": "arn:aws:iot:ap-southeast-1:123456789012:rolealias/my_iot_role_alias"

    }

  ]

}

(您需要把上述Resource中的ARN替换成您角色别名的ARN)

经过上述的设置,终端设备就能通过Https获取临时凭证。我们用以下wget命令发起访问,会获得临时凭证的返回:

wget –private-key=/home/ubuntu/iotgetaksk/2ffd3c6424-private.pem.key –certificate=/home/ubuntu/iotgetaksk/2ffd3c6424-certificate.pem.crt –ca-directory=/home/ubuntu/iotgetaksk/AmazonRootCA1.cer –debug https://abcdefghijklm.credentials.iot.ap-southeast-1.amazonaws.com:443/role-aliases/my_iot_role_alias/credentials

四.权限的细粒度考虑

在第二部分中,我们提到该IAM Policy给每个设备提供相同权限,存在一定的安全隐患。我们能够在IAM Policy中嵌入IOT策略变量为每个设备提供更细粒度的权限,例如我们可以把第二部分的IAM Policy改为如下:

{

    "Version": "2012-10-17",

    "Statement": [

        {

            "Sid": "VisualEditor0",

            "Effect": "Allow",

            "Action": [

                "s3:ListAllMyBuckets",

                "s3:HeadBucket",

                "s3:ListObjects"

            ],

            "Resource": "*"

        },

        {

            "Sid": "VisualEditor1",

            "Effect": "Allow",

            "Action": "s3:*",

            "Resource": "arn:aws:s3:::vincent-iot/${ iot:certificateId}/*"

        }

    ]

}

在上述IAM Policy中,我们以${ iot:certificateId}策略变量作为S3 Resource的一部分。当请求临时凭证时,AWS会使用实际值替换该变量,从而做到每个临时凭证精细化权限控制。

这个特性当前支持三种策略变量,分别:

·         ${iot:thingName}        事物名称

·         ${iot:thingType}          事物类型

·         ${iot:certificateId}       X.509证书ID

其中${iot:certificateId}会自动跟当前的X.509证书相匹配,但${iot:thingName}和${iot:thingType}与前者稍有不同:

(1)如果在IAM Policy中使用了${iot: thingName}或者${iot:thingType},我们必须在Https的request Header中添加x-amzn-iot-thingname请求标头来传递事物名称thing name,否则无法在IAM Policy中使用事物变量${iot:thingName}和${iot:thingType}。采用这种操作是因为当前一个X.509证书能够附加多个thing,因此我们必须在Https请求标头中指定具体的thing name,AWS才能获取具体对应的${iot:thingName}和${iot:thingType}。这也说明了,当前通过X.509证书获取临时凭证这种做法,一个临时凭证只能精细化控制对某一个thing的权限分发。

(2)如果在IAM Policy中指定了${iot:thingName}或者${iot:thingType},但在Https Request中没有添加x-amzn-iot-thingname标头,AWS会正常给您返回临时凭证,但我们会发现该临时凭证没有任何权限。

(3)当在Https Request中添加x-amzn-iot-thingname标头后,AWS会检查当前的X.509证书是否已经附加相应的thing。如果发现附加关系不正确,AWS会拒绝返回临时凭证,返回403,提示“Invalid thing name passed”。

当您一切准备就绪后,您可以尝试通过以下wget命令发起访问:

wget –header=”x-amzn-iot-thingname: bulbthing” –private-key=/home/ubuntu/iotgetaksk/2ffd3c6424-private.pem.key –certificate=/home/ubuntu/iotgetaksk/2ffd3c6424-certificate.pem.crt –ca-directory=/home/ubuntu/iotgetaksk/AmazonRootCA1.cer –debug https://abcdefghijklm.credentials.iot.ap-southeast-1.amazonaws.com:443/role-aliases/my_iot_role_alias/credentials

再次提醒,只有在IAM Policy中使用了${iot:thingName}或者${iot:thingType}策略变量才需要在Https请求中添加x-amzn-iot-thingname请求标头,对于${iot:certificateId}是不需要的。

结束语

Mqtt与其他AWS服务的身份认证方式存在差异。为了减少与两套体系交互时重复的身份认证工作,AWS推出本文介绍的功能,复用X.509证书,减少客户的开发量和成本。目前,该功能在AWS海外具有IOT服务的区域可用,但在由光环新网运营的北京区域暂时不可用。

邱越俊

亚马逊AWS解决方案架构师,熟悉AWS IOT平台的开发和使用,擅长数据分析、IOT架构和管理, 具有丰富的解决客户实际问题的经验。