临时授权访问

最近更新时间:2020-09-18 11:22:29

基本原理

无论是主账号还是IAM子用户都是可以长期正常使用的,如果对应的AK/SK发生泄露之后如果无法及时解除权限的话会很危险。如果众多的客户端需要直接与KS3交互,可以让客户端获取一个临时的最小权限的凭证,并且该权限有一定的有效期即临时身份凭证,从而最大程度上保护用户数据安全。

考虑到如下的案例:

客户开发的App会分发给终端用户,终端用户的数据需要直接上传到KS3,如果将主账号或者IAM子用户的AK/SK嵌入到App都是非常危险的行为,那如何才能安全的授权给众多的App用户上传数据呢,以及如何保证多个用户之间存储的隔离。

类似这种需要临时访问的场景可以使用临时身份凭证来应对,其主要操作步骤为:在当前的主账号下创建一个角色,指定角色授信的主账户,并且给角色通过用户策略(user poicy)授予一个最小权限。然后在授信主账号下创建一个子用户,并授权给子用户角色(让这个子用户扮演这个角色),子用户就可以通过STS服务获取临时AK/SK及token,去访问KS3了,为了提升安全性该token还有一定的过期时间。
原理图如下:

临时授权访问

具体的步骤如下:

创建策略

1,安全的STS服务,需要设置两个自定义策略:1.子用户策略(保证子用户有扮演角色的权限)2.角色策略(保证角色对目标资源有对应权限)在控制台上操作请点击进入访问控制->策略,进入策略管理界面,选择自定义策略->新建策略
临时授权访问
2,在自定义策略中,输入策略名,此处假设为"test-policy",然后选择【可视化配置】->【添加策略语句】
临时授权访问
3,在策略语句中选择产品服务为“临时身份验证”,在操作中选择“AssumeRole”,该权限表示可以扮演角色,子用户策略创建完成
临时授权访问

4,再次创建自定义策略,假设名称为“role-test-policy”,设置策略类型为策略语法,可以通过模板来简化创建策略过程,并点击【下一步】
临时授权访问
5,编辑相应的权限,如下载文件、查看bucket列表等,角色策略创建完成
临时授权访问
如下例子中对桶app_bucket及其内文件有get和ListBucket权限,分别对Action和Resource进行编辑,

{
    "Version": "2019-11-01",
    "Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "ks3:GetObject",
            "ks3:ListBucket"
         ],
        "Resource":[
            "krn:ksc:ks3:::app_bucket",
            "krn:ksc:ks3:::app_bucket/*"
         ]
     }
   ]
}

创建子用户

主账户是不能扮演角色的,需要受信主账户通过用户权限(user policy)把扮演角色的权限授权给IAM子用户。 1,在控制台上操作请点击进入【访问控制】->【子用户】,进入子用户管理界面,输入子用户登录账户等信息,此处假设为“test-subuser”选中编程访问复选框为子用户创建AK和SK。

临时授权访问

2,选中刚创建的子用户,点击添加权限按钮来为子用户授权。

临时授权访问
3,在自定义策略中选择,在“创建策略”步骤中创建的"test-policy"策略,这样子用户就具有了扮演角色的权限,但是现在还没有角色,因此接下来要创建角色
临时授权访问

创建角色

1,在控制台上操作请点击进入【访问控制】->【角色管理】,进入角色管理界面。
2,点击“新建角色”按钮,选择授信云账号,可以选择当前的账号,也可以选择其它云账号。我们创建一个名为“test-role”的角色,设置载体信息为当前账号(也可以选择其他账号,载体信息即授信账号,比如你在账号1下创建角色授信给账号2,则账号2的子用户就可以扮演账号1下的角色)。新创建的角色是没有任何权限的,选择“下一步”来设置角色权限。

临时授权访问

3,创建完角色之后,需要记录下角色的KRN,角色是没有任何权限的,在角色详情中为该角色增加前面创建的角色策略role-test-policy,这样角色“test-role”就有的相应的权限
临时授权访问
临时授权访问

子用户扮演角色

主账户是不能扮演角色的,需要受信主账户通过用户权限(user policy)把扮演角色的权限授权给IAM子用户。

1,再次进入【访问控制】->【子用户】页面,选择刚创建的子用户“test-subuser”进入详情页面

2,在选择权限中,选择为子用户创建的策略"test-policy",这样子用户就具有扮演角色的权限,而且所扮演的角色对目标资源有相应的权限。

临时授权访问
临时授权访问 3,同样此时,该子用户仅具有了扮演角色的权限,但还没有关联具体的角色,因此还需要添加权限对应的资源,即角色ID,在策略中点击选择所创建的"test-policy",点击修改信任策略在编辑页面中的Resource填入角色的KRN。
临时授权访问 临时授权访问

{
    "Version": "2015-11-01",
    "Statement": [
    {
    "Sid": "Stmt15253327178180",
    "Effect": "Allow",
    "Action": ["sts:AssumeRole"],
    "Resource": [" krn:ksc:iam::Account_ID:role/test-role"]
    }
    ]
}

使用STS授权访问

1,使用子用户"test-subuser"的AK/SK 去调用STS服务获取临时权限。详见文档获取角色的临时身份

http://sts.cn-beijing-6.api.ksyun.com/?Action=AssumeRole
&Version=2019-11-01
&RoleSessionName=Bob
&RoleKrn=krn:ksc:iam::Account_ID:role/test-role
&AUTHPARAMS

返回的内容如下:

<AssumeRoleResponse>
  <AssumeRoleResult>
      <Credentials>
        <SecretAccessKey>wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY</SecretAccessKey>
        <Expiration>2017-07-15T23:28:33.359Z</Expiration>
        <AccessKeyId>AKIAIOSFODNN7EXAMPLE</AccessKeyId>
        <SecurityToken>V1xxxxxxxxxxxx</SecurityToken>
      </Credentials>
      <AssumedRoleUser>
         <Krn>krn:ksc:sts::123456789012:assumed-role/demo/Bob</Krn>
         <AssumedRoleId>ARO123EXAMPLE123:Bob</AssumedRoleId>
      </AssumedRoleUser>
   </AssumeRoleResult>
   <ResponseMetadata>
     <RequestId>c6104cbe-af31-11e0-8154-cbc7ccf896c7</RequestId>
   </ResponseMetadata>
</AssumeRoleResponse>

2,从返回的XML结果中找到临时权限:AccessKeyId、SecretAccessKey及SecurityToken,然后通过KS3 APIKS3 SDK去访问相应的资源。

GO示例:

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "github.com/KscSDK/ksc-sdk-go/ksc"
    "github.com/KscSDK/ksc-sdk-go/ksc/utils"
    "github.com/KscSDK/ksc-sdk-go/service/sts"
    "github.com/ks3sdklib/aws-sdk-go/aws"
    "github.com/ks3sdklib/aws-sdk-go/aws/credentials"
    "github.com/ks3sdklib/aws-sdk-go/service/s3"
    "os"
)

func main() {
    ak := "<AccessKeyID>"
    sk := "<AccessKeySecret>"
    stsRegion := "cn-beijing-6"
    assumeRole := assumeRoleRequest(ak, sk, stsRegion) //调用ksc-go-sdk的sts服务
    var data Response
    _ = json.Unmarshal(assumeRole, &data)
    stsAk := data.AssumeRoleResult.Credentials.AccessKeyId      // sts ak
    stsSk := data.AssumeRoleResult.Credentials.SecretAccessKey  // sts sk
    stsToken := data.AssumeRoleResult.Credentials.SecurityToken // sts token

    //create ks3 client with sts ak,sk,token
    credentials := credentials.NewStaticCredentials(stsAk, stsSk, stsToken)
    client := s3.New(&aws.Config{
        Region:           "BEIJING",
        Credentials:      credentials,
        Endpoint:         "ks3-cn-beijing.ksyun.com", //ks3地址
        DisableSSL:       true,                       //是否禁用https
        LogLevel:         1,                          //是否开启日志,0为关闭日志,1为开启日志 n
        S3ForcePathStyle: false,                      //是否强制使用path style方式访问
        LogHTTPBody:      true,                       //是否把HTTP请求body打入日志
        Logger:           os.Stdout,                  //打日志的位置
    })

    // example for uploading file
    params := &s3.PutObjectInput{
        Bucket:      aws.String("BucketName"),              // bucket名称
        Key:         aws.String("ObjectKey"),               // object key
        ACL:         aws.String("public-read"),             //权限,支持private(私有),public-read(公开读)
        Body:        bytes.NewReader([]byte("PAYLOAD")),    //要上传的内容
        ContentType: aws.String("application/ocet-stream"), //设置content-type
        Metadata: map[string]*string{
            //"Key": aws.String("MetadataValue"), // 设置用户元数据
            // More values...
        },
    }
    resp, err := client.PutObject(params)
    if err != nil {
        panic(err)
    }
    fmt.Println(resp)
    //获取新的文件名
    fmt.Println(*resp.NewFileName)

}

func assumeRoleRequest(ak string, sk string, region string) []byte { //调用ksc-go-sdk的sts服务
    svc := sts.SdkNew(ksc.NewClient(ak, sk /*,true*/), &ksc.Config{Region: &region}, &utils.UrlInfo{ //debug模式的话,打开true开关
        UseSSL:      true,
        UseInternal: false,
    })
    var resp *map[string]interface{}
    var err error

    //set your assumeRole here
    assumeRoleInput := make(map[string]interface{})
    assumeRoleInput["RoleKrn"] = "<YourRoleKrn>"
    assumeRoleInput["RoleSessionName"] = "Bob"
    assumeRoleInput["DurationSeconds"] = "3600"
    //assumeRole["Policy"] = ""

    resp, err = svc.AssumeRole(&assumeRoleInput)
    if err != nil {
        fmt.Println("error:", err.Error())
        return nil
    }
    var str []byte
    if resp != nil {
        str, _ = json.Marshal(&resp)
        //fmt.Printf("%+v\n", string(str))
    }
    return str
}

type (
    Response struct {
        AssumeRoleResult struct {
            Credentials struct {
                SecretAccessKey string `json:"SecretAccessKey"`
                Expiration      string `json:"Expiration"`
                AccessKeyId     string `json:"AccessKeyId"`
                SecurityToken   string `json:"SecurityToken"`
            } `json:"Credentials"`
            AssumedRoleUser struct {
                Krn           string `json:"Krn"`
                AssumedRoleId string `json:"AssumedRoleId"`
            } `json:"AssumedRoleUser"`
        } `json:AssumeRoleResult`
        RequestId string `json:"RequestId"`
    }
)

金山云,开启您的云计算之旅

免费注册