AWS IoT 支持使用四种身份委托人进行身份验证:
通常,AWS IoT 设备使用 X.509 证书,移动应用程序使用 Amazon Cognito 身份。Web 和桌面应用程序使用 IAM 或联合身份。CLI 命令使用 IAM。
通过之前的学习,发现现有的教程和说明文档都只针对如何利用这四种方式进行身份认证,使得MQTT客户端与AWS IoT建立通信连接。而对于MQTT客户端的clientId,亚马逊并没有强制绑定,用户可以任意构造clientId凭借合法的身份凭证与AWS IoT建立连接,存在安全隐患。因此,本博文主要介绍如何通过设置亚马逊策略policy的方式限制MQTT客户端的clientId。
此方案采用Amazon Cognito身份的方式进行身份认证,通过设置相应的用户池、身份池和角色策略达到限制clientId的效果。具体步骤如下:
1、创建用户池
登录AWS管理控制台,并导航到Amazon Cognito。在Amazon Cognito控制台中,选择“ 管理用户池”,然后选择“ 创建用户池”。
对于“池名称”,输入demo_pool。记下用户池ID。
2、创建应用客户端
从导航窗格中,选择“应用程序客户端”,然后选择添加应用程序客户端。对于应用程序客户端名称,请输入demo_app。记下应用程序客户端ID。
查看用户池设置,然后选择“ 创建应用程序客户端”。
3、创建标识池
接下来,使用Amazon Cognito用户池作为身份提供者创建联合身份池。使用前面步骤中创建的用户池ID和应用程序客户端ID。
在Amazon Cognito控制台中,选择“ 联合身份”。创建一个标识池并将其命名为demo_idpool。对于身份验证提供商,请选择Cognito。
输入用户池ID。(在用户池→常规设置→用户池ID)
输入应用程序客户端ID。(在用户池→应用程序客户端→演示应用程序客户端ID)
选择“ 创建池”。
注意:将创建两个IAM角色:一个为经过身份验证的角色,另一个未经身份验证的角色。在这篇文章中,将仅使用经过身份验证的角色。
经过Amazon Cognito身份验证的用户需要两个策略才能访问AWS IoT。第一个策略附加到经过身份验证的角色上,以验证和授权Cognito用户与AWS IoT进行通信。第二个策略附加到经过身份验证的Cognito用户主体,以获得细粒度权限。
1、授予Amazon Cognito角色访问AWS IoT的权限
在AWS管理控制台,导航到Amazon IAM,选择“角色”,然后选择之前创建的经过身份验证的角色。点击附加策略,然后选择创建策略,在JSON窗口中输入以下内容,授予角色访问AWS IoT的权限。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"iot:Receive",
"iot:Subscribe",
"iot:Connect",
"iot:GetThingShadow",
"iot:Publish"
],
"Resource": "*"
}
]
}
2、设置AWS IoT策略进行细粒度的访问控制
在AWS管理控制台,导航到Amazon IoT Core,选择“ 安全”,然后选择“ 策略”。在“ 创建策略”页面上,输入策略名称“demo_policy”。具体内容如下
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:Connect"
],
"Resource": [
"arn:aws:iot:*:*:client/${cognito-identity.amazonaws.com:sub}"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Receive",
"iot:Subscribe",
"iot:GetThingShadow",
"iot:Publish"
],
"Resource": [
"*"
]
}
]
}
这是此方案的关键,在策略文件中限制MQTT客户端clientId。亚马逊的官方文档中说明,可以利用IAM策略变量控制多个用户或资源的访问。该变量使用 $
前缀标记,后跟一对大括号 ({ }
)。在 ${ }
字符内,可以包含想要在策略中使用的请求中的值名称。实验验证${aws:username} 和${aws:userid}都无法成功使用户MQTT客户端与AWS IoT建立连接。
之后通过查找资料,可以通过${cognito-identity.amazonaws.com:sub}匹配用户Id,从而实现合法用户仅能使用自身的身份Id作为clientId,才能与IoT建立通信连接,保证clientId不会被任意篡改。
本文通过修改github中aws-sdk-android-samples的Android实例创建 Android APP。
其中关键代码如下:
new Thread() {
@Override
public void run() {
identityId = credentialsProvider.getIdentityId();//获得身份Id
clientId = identityId;//将身份Id赋予MQTT客户端的clientId
}
}.start();
通过getIdentityId()的方法获得该用户的身份I的,并赋予给MQTT客户端clientId。
注册用户,可以在身份池→身份浏览器中看到用户身份Id。值得注意的是,用户池中用户详情的sub不是身份Id,不要搞错。
将IoT策略附加到之前注册的用户上,由于Android没有相应的API,采用awscli命令行的方式进行操作,安装awscli,输入
attach-policy --policy-name --target
其中target为用户的身份Id。这样便将IoT策略附加至特定的用户。可以在IoT策略的版本中看到效果
用户注册登录成功获得Amazon Cognito身份凭证,并创建MQTT客户端。之后获取唯一的身份Id,并将clientId设置为相应的身份Id,便可与AWS IoT建立MQTT连接和通信。由于IoT策略文件中的"arn:aws:iot:*:*:client/${cognito-identity.amazonaws.com:sub}"限制了合法用户的MQTT客户端clientId的唯一性,从而保证了IoT连接的安全性。
参考:
https://www.slideshare.net/AmazonWebServices/iot-apps-with-aws-iot-and-websockets
https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/reference_policies_variables.html
https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/reference_policies_iam-condition-keys.html