From ea79e2c1d359c59d0cadbb2cadd38e4760f726e2 Mon Sep 17 00:00:00 2001 From: yndu13 Date: Fri, 10 May 2024 12:19:01 +0800 Subject: [PATCH] feat: support IMDSv2 for ECS metadata --- credentials/credential.go | 45 ++++++++- credentials/credential_test.go | 20 +++- credentials/ecs_ram_role.go | 60 +++++++++++- credentials/ecs_ram_role_test.go | 127 ++++++++++++++++++++++++++ credentials/instance_provider.go | 7 +- credentials/instance_provider_test.go | 27 ++++++ credentials/provider.go | 15 +-- go.sum | 70 ++++++++++++++ 8 files changed, 352 insertions(+), 19 deletions(-) create mode 100644 go.sum diff --git a/credentials/credential.go b/credentials/credential.go index a08e8cc..32aa2a9 100644 --- a/credentials/credential.go +++ b/credentials/credential.go @@ -47,6 +47,8 @@ type Config struct { RoleSessionName *string `json:"role_session_name"` PublicKeyId *string `json:"public_key_id"` RoleName *string `json:"role_name"` + EnableIMDSv2 *bool `json:"enable_imds_v2"` + MetadataTokenDuration *int `json:"metadata_token_duration"` SessionExpiration *int `json:"session_expiration"` PrivateKeyFile *string `json:"private_key_file"` BearerToken *string `json:"bearer_token"` @@ -106,6 +108,16 @@ func (s *Config) SetRoleName(v string) *Config { return s } +func (s *Config) SetEnableIMDSv2(v bool) *Config { + s.EnableIMDSv2 = &v + return s +} + +func (s *Config) SetMetadataTokenDuration(v int) *Config { + s.MetadataTokenDuration = &v + return s +} + func (s *Config) SetSessionExpiration(v int) *Config { s.SessionExpiration = &v return s @@ -205,19 +217,33 @@ func NewCredential(config *Config) (credential Credential, err error) { ConnectTimeout: tea.IntValue(config.ConnectTimeout), STSEndpoint: tea.StringValue(config.STSEndpoint), } - credential = newOIDCRoleArnCredential(tea.StringValue(config.AccessKeyId), tea.StringValue(config.AccessKeySecret), tea.StringValue(config.RoleArn), tea.StringValue(config.OIDCProviderArn), tea.StringValue(config.OIDCTokenFilePath), tea.StringValue(config.RoleSessionName), tea.StringValue(config.Policy), tea.IntValue(config.RoleSessionExpiration), runtime) + credential = newOIDCRoleArnCredential( + tea.StringValue(config.AccessKeyId), + tea.StringValue(config.AccessKeySecret), + tea.StringValue(config.RoleArn), + tea.StringValue(config.OIDCProviderArn), + tea.StringValue(config.OIDCTokenFilePath), + tea.StringValue(config.RoleSessionName), + tea.StringValue(config.Policy), + tea.IntValue(config.RoleSessionExpiration), + runtime) case "access_key": err = checkAccessKey(config) if err != nil { return } - credential = newAccessKeyCredential(tea.StringValue(config.AccessKeyId), tea.StringValue(config.AccessKeySecret)) + credential = newAccessKeyCredential( + tea.StringValue(config.AccessKeyId), + tea.StringValue(config.AccessKeySecret)) case "sts": err = checkSTS(config) if err != nil { return } - credential = newStsTokenCredential(tea.StringValue(config.AccessKeyId), tea.StringValue(config.AccessKeySecret), tea.StringValue(config.SecurityToken)) + credential = newStsTokenCredential( + tea.StringValue(config.AccessKeyId), + tea.StringValue(config.AccessKeySecret), + tea.StringValue(config.SecurityToken)) case "ecs_ram_role": checkEcsRAMRole(config) runtime := &utils.Runtime{ @@ -226,7 +252,12 @@ func NewCredential(config *Config) (credential Credential, err error) { ReadTimeout: tea.IntValue(config.Timeout), ConnectTimeout: tea.IntValue(config.ConnectTimeout), } - credential = newEcsRAMRoleCredential(tea.StringValue(config.RoleName), tea.Float64Value(config.InAdvanceScale), runtime) + credential = newEcsRAMRoleCredentialWithEnableIMDSv2( + tea.StringValue(config.RoleName), + tea.BoolValue(config.EnableIMDSv2), + tea.IntValue(config.MetadataTokenDuration), + tea.Float64Value(config.InAdvanceScale), + runtime) case "ram_role_arn": err = checkRAMRoleArn(config) if err != nil { @@ -274,7 +305,11 @@ func NewCredential(config *Config) (credential Credential, err error) { ConnectTimeout: tea.IntValue(config.ConnectTimeout), STSEndpoint: tea.StringValue(config.STSEndpoint), } - credential = newRsaKeyPairCredential(privateKey, tea.StringValue(config.PublicKeyId), tea.IntValue(config.SessionExpiration), runtime) + credential = newRsaKeyPairCredential( + privateKey, + tea.StringValue(config.PublicKeyId), + tea.IntValue(config.SessionExpiration), + runtime) case "bearer": if tea.StringValue(config.BearerToken) == "" { err = errors.New("BearerToken cannot be empty") diff --git a/credentials/credential_test.go b/credentials/credential_test.go index 45ae455..263149e 100644 --- a/credentials/credential_test.go +++ b/credentials/credential_test.go @@ -15,8 +15,8 @@ this is privatekey` func TestConfig(t *testing.T) { config := new(Config) - assert.Equal(t, "{\n \"type\": null,\n \"access_key_id\": null,\n \"access_key_secret\": null,\n \"oidc_provider_arn\": null,\n \"oidc_token\": null,\n \"role_arn\": null,\n \"role_session_name\": null,\n \"public_key_id\": null,\n \"role_name\": null,\n \"session_expiration\": null,\n \"private_key_file\": null,\n \"bearer_token\": null,\n \"security_token\": null,\n \"role_session_expiratioon\": null,\n \"policy\": null,\n \"host\": null,\n \"timeout\": null,\n \"connect_timeout\": null,\n \"proxy\": null,\n \"inAdvanceScale\": null,\n \"url\": null,\n \"sts_endpoint\": null,\n \"external_id\": null\n}", config.String()) - assert.Equal(t, "{\n \"type\": null,\n \"access_key_id\": null,\n \"access_key_secret\": null,\n \"oidc_provider_arn\": null,\n \"oidc_token\": null,\n \"role_arn\": null,\n \"role_session_name\": null,\n \"public_key_id\": null,\n \"role_name\": null,\n \"session_expiration\": null,\n \"private_key_file\": null,\n \"bearer_token\": null,\n \"security_token\": null,\n \"role_session_expiratioon\": null,\n \"policy\": null,\n \"host\": null,\n \"timeout\": null,\n \"connect_timeout\": null,\n \"proxy\": null,\n \"inAdvanceScale\": null,\n \"url\": null,\n \"sts_endpoint\": null,\n \"external_id\": null\n}", config.GoString()) + assert.Equal(t, "{\n \"type\": null,\n \"access_key_id\": null,\n \"access_key_secret\": null,\n \"oidc_provider_arn\": null,\n \"oidc_token\": null,\n \"role_arn\": null,\n \"role_session_name\": null,\n \"public_key_id\": null,\n \"role_name\": null,\n \"enable_imds_v2\": null,\n \"metadata_token_duration\": null,\n \"session_expiration\": null,\n \"private_key_file\": null,\n \"bearer_token\": null,\n \"security_token\": null,\n \"role_session_expiratioon\": null,\n \"policy\": null,\n \"host\": null,\n \"timeout\": null,\n \"connect_timeout\": null,\n \"proxy\": null,\n \"inAdvanceScale\": null,\n \"url\": null,\n \"sts_endpoint\": null,\n \"external_id\": null\n}", config.String()) + assert.Equal(t, "{\n \"type\": null,\n \"access_key_id\": null,\n \"access_key_secret\": null,\n \"oidc_provider_arn\": null,\n \"oidc_token\": null,\n \"role_arn\": null,\n \"role_session_name\": null,\n \"public_key_id\": null,\n \"role_name\": null,\n \"enable_imds_v2\": null,\n \"metadata_token_duration\": null,\n \"session_expiration\": null,\n \"private_key_file\": null,\n \"bearer_token\": null,\n \"security_token\": null,\n \"role_session_expiratioon\": null,\n \"policy\": null,\n \"host\": null,\n \"timeout\": null,\n \"connect_timeout\": null,\n \"proxy\": null,\n \"inAdvanceScale\": null,\n \"url\": null,\n \"sts_endpoint\": null,\n \"external_id\": null\n}", config.GoString()) config.SetSTSEndpoint("sts.cn-hangzhou.aliyuncs.com") assert.Equal(t, "sts.cn-hangzhou.aliyuncs.com", *config.STSEndpoint) @@ -96,6 +96,22 @@ func TestNewCredentialWithECSRAMRole(t *testing.T) { cred, err = NewCredential(config) assert.Nil(t, err) assert.NotNil(t, cred) + + config.SetEnableIMDSv2(false) + cred, err = NewCredential(config) + assert.Nil(t, err) + assert.NotNil(t, cred) + + config.SetEnableIMDSv2(true) + cred, err = NewCredential(config) + assert.Nil(t, err) + assert.NotNil(t, cred) + + config.SetEnableIMDSv2(true) + config.SetMetadataTokenDuration(180) + cred, err = NewCredential(config) + assert.Nil(t, err) + assert.NotNil(t, cred) } func TestNewCredentialWithRSAKeyPair(t *testing.T) { diff --git a/credentials/ecs_ram_role.go b/credentials/ecs_ram_role.go index d86360f..f88a279 100644 --- a/credentials/ecs_ram_role.go +++ b/credentials/ecs_ram_role.go @@ -3,6 +3,7 @@ package credentials import ( "encoding/json" "fmt" + "strconv" "time" "github.com/alibabacloud-go/tea/tea" @@ -11,13 +12,20 @@ import ( ) var securityCredURL = "http://100.100.100.200/latest/meta-data/ram/security-credentials/" +var securityCredTokenURL = "http://100.100.100.200/latest/api/token" + +const defaultMetadataTokenDuration = int(21600) // EcsRAMRoleCredential is a kind of credential type EcsRAMRoleCredential struct { *credentialUpdater - RoleName string - sessionCredential *sessionCredential - runtime *utils.Runtime + RoleName string + EnableIMDSv2 bool + MetadataTokenDuration int + sessionCredential *sessionCredential + runtime *utils.Runtime + metadataToken string + staleTime int64 } type ecsRAMRoleResponse struct { @@ -40,6 +48,20 @@ func newEcsRAMRoleCredential(roleName string, inAdvanceScale float64, runtime *u } } +func newEcsRAMRoleCredentialWithEnableIMDSv2(roleName string, enableIMDSv2 bool, metadataTokenDuration int, inAdvanceScale float64, runtime *utils.Runtime) *EcsRAMRoleCredential { + credentialUpdater := new(credentialUpdater) + if inAdvanceScale < 1 && inAdvanceScale > 0 { + credentialUpdater.inAdvanceScale = inAdvanceScale + } + return &EcsRAMRoleCredential{ + RoleName: roleName, + EnableIMDSv2: enableIMDSv2, + MetadataTokenDuration: metadataTokenDuration, + credentialUpdater: credentialUpdater, + runtime: runtime, + } +} + func (e *EcsRAMRoleCredential) GetCredential() (*CredentialModel, error) { if e.sessionCredential == nil || e.needUpdateCredential() { err := e.updateCredential() @@ -123,6 +145,26 @@ func getRoleName() (string, error) { return string(content), nil } +func (e *EcsRAMRoleCredential) getMetadataToken() (err error) { + if e.needToRefresh() { + if e.MetadataTokenDuration <= 0 { + e.MetadataTokenDuration = defaultMetadataTokenDuration + } + tmpTime := time.Now().Unix() + int64(e.MetadataTokenDuration*1000) + request := request.NewCommonRequest() + request.URL = securityCredTokenURL + request.Method = "PUT" + request.Headers["X-aliyun-ecs-metadata-token-ttl-seconds"] = strconv.Itoa(e.MetadataTokenDuration) + content, err := doAction(request, e.runtime) + if err != nil { + return err + } + e.staleTime = tmpTime + e.metadataToken = string(content) + } + return +} + func (e *EcsRAMRoleCredential) updateCredential() (err error) { if e.runtime == nil { e.runtime = new(utils.Runtime) @@ -134,6 +176,13 @@ func (e *EcsRAMRoleCredential) updateCredential() (err error) { return fmt.Errorf("refresh Ecs sts token err: %s", err.Error()) } } + if e.EnableIMDSv2 { + err = e.getMetadataToken() + if err != nil { + return fmt.Errorf("Failed to get token from ECS Metadata Service: %s", err.Error()) + } + request.Headers["X-aliyun-ecs-metadata-token"] = e.metadataToken + } request.URL = securityCredURL + e.RoleName request.Method = "GET" content, err := doAction(request, e.runtime) @@ -163,3 +212,8 @@ func (e *EcsRAMRoleCredential) updateCredential() (err error) { return } + +func (e *EcsRAMRoleCredential) needToRefresh() (needToRefresh bool) { + needToRefresh = time.Now().Unix() >= e.staleTime + return +} diff --git a/credentials/ecs_ram_role_test.go b/credentials/ecs_ram_role_test.go index 5bdef96..676b443 100644 --- a/credentials/ecs_ram_role_test.go +++ b/credentials/ecs_ram_role_test.go @@ -136,3 +136,130 @@ func Test_EcsRAmRoleCredential(t *testing.T) { assert.Equal(t, "refresh Ecs sts token err: error parse", err.Error()) assert.Equal(t, "", *accesskeyId) } + +func Test_EcsRAmRoleCredentialEnableIMDSv2(t *testing.T) { + auth := newEcsRAMRoleCredentialWithEnableIMDSv2("go sdk", false, 0, 0.5, nil) + origTestHookDo := hookDo + defer func() { hookDo = origTestHookDo }() + + hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { + return func(req *http.Request) (*http.Response, error) { + return mockResponse(300, ``, errors.New("sdk test")) + } + } + accesskeyId, err := auth.GetAccessKeyId() + assert.NotNil(t, err) + assert.Equal(t, "refresh Ecs sts token err: sdk test", err.Error()) + assert.Equal(t, "", *accesskeyId) + + auth = newEcsRAMRoleCredentialWithEnableIMDSv2("go sdk", true, 0, 0.5, nil) + accesskeyId, err = auth.GetAccessKeyId() + assert.NotNil(t, err) + assert.Equal(t, "Failed to get token from ECS Metadata Service: sdk test", err.Error()) + assert.Equal(t, "", *accesskeyId) + + auth = newEcsRAMRoleCredentialWithEnableIMDSv2("go sdk", true, 180, 0.5, nil) + accesskeyId, err = auth.GetAccessKeyId() + assert.NotNil(t, err) + assert.Equal(t, "Failed to get token from ECS Metadata Service: sdk test", err.Error()) + assert.Equal(t, "", *accesskeyId) + + hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { + return func(req *http.Request) (*http.Response, error) { + return mockResponse(300, ``, nil) + } + } + accesskeyId, err = auth.GetAccessKeyId() + assert.NotNil(t, err) + assert.Equal(t, "Failed to get token from ECS Metadata Service: httpStatus: 300, message = ", err.Error()) + assert.Equal(t, "", *accesskeyId) + + hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { + return func(req *http.Request) (*http.Response, error) { + return mockResponse(400, `role`, nil) + } + } + auth.RoleName = "" + accesskeyId, err = auth.GetAccessKeyId() + assert.NotNil(t, err) + assert.Equal(t, "refresh Ecs sts token err: httpStatus: 400, message = role", err.Error()) + + hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { + return func(req *http.Request) (*http.Response, error) { + return mockResponse(200, `role`, nil) + } + } + accesskeyId, err = auth.GetAccessKeyId() + assert.NotNil(t, err) + assert.Equal(t, "refresh Ecs sts token err: Json Unmarshal fail: invalid character 'r' looking for beginning of value", err.Error()) + hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { + return func(req *http.Request) (*http.Response, error) { + return mockResponse(200, `"AccessKeyId":"accessKeyId","AccessKeySecret":"accessKeySecret","SecurityToken":"securitytoken","Expiration":"expiration"`, nil) + } + } + auth.RoleName = "role" + accesskeyId, err = auth.GetAccessKeyId() + assert.NotNil(t, err) + assert.Equal(t, "refresh Ecs sts token err: Json Unmarshal fail: invalid character ':' after top-level value", err.Error()) + assert.Equal(t, "", *accesskeyId) + + hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { + return func(req *http.Request) (*http.Response, error) { + return mockResponse(200, `{"AccessKeySecret":"accessKeySecret","SecurityToken":"securitytoken","Expiration":"expiration","Code":"fail"}`, nil) + } + } + accesskeyId, err = auth.GetAccessKeyId() + assert.NotNil(t, err) + assert.Equal(t, "refresh Ecs sts token err: Code is not Success", err.Error()) + assert.Equal(t, "", *accesskeyId) + + hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { + return func(req *http.Request) (*http.Response, error) { + return mockResponse(200, `{"AccessKeySecret":"accessKeySecret","SecurityToken":"securitytoken","Expiration":"expiration","Code":"Success"}`, nil) + } + } + accesskeyId, err = auth.GetAccessKeyId() + assert.NotNil(t, err) + assert.Equal(t, "refresh Ecs sts token err: AccessKeyId: , AccessKeySecret: accessKeySecret, SecurityToken: securitytoken, Expiration: expiration", err.Error()) + assert.Equal(t, "", *accesskeyId) + + hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { + return func(req *http.Request) (*http.Response, error) { + return mockResponse(200, `{"AccessKeyId":"accessKeyId","AccessKeySecret":"accessKeySecret","SecurityToken":"securitytoken","Expiration":"2018-01-02T15:04:05Z","Code":"Success"}`, nil) + } + } + accesskeyId, err = auth.GetAccessKeyId() + assert.Nil(t, err) + assert.Equal(t, "accessKeyId", *accesskeyId) + + accesskeySecret, err := auth.GetAccessKeySecret() + assert.Nil(t, err) + assert.Equal(t, "accessKeySecret", *accesskeySecret) + + ststoken, err := auth.GetSecurityToken() + assert.Nil(t, err) + assert.Equal(t, "securitytoken", *ststoken) + + err = errors.New("credentials") + err = hookParse(err) + assert.Equal(t, "credentials", err.Error()) + + cred, err := auth.GetCredential() + assert.Nil(t, err) + assert.Equal(t, "accessKeyId", *cred.AccessKeyId) + assert.Equal(t, "accessKeySecret", *cred.AccessKeySecret) + assert.Equal(t, "securitytoken", *cred.SecurityToken) + assert.Nil(t, cred.BearerToken) + assert.Equal(t, "ecs_ram_role", *cred.Type) + + originHookParse := hookParse + hookParse = func(err error) error { + return errors.New("error parse") + } + defer func() { + hookParse = originHookParse + }() + accesskeyId, err = auth.GetAccessKeyId() + assert.Equal(t, "refresh Ecs sts token err: error parse", err.Error()) + assert.Equal(t, "", *accesskeyId) +} diff --git a/credentials/instance_provider.go b/credentials/instance_provider.go index 7e2ea07..c82091d 100644 --- a/credentials/instance_provider.go +++ b/credentials/instance_provider.go @@ -2,6 +2,7 @@ package credentials import ( "os" + "strings" "github.com/alibabacloud-go/tea/tea" ) @@ -19,10 +20,12 @@ func (p *instanceCredentialsProvider) resolve() (*Config, error) { if !ok { return nil, nil } + enableIMDSv2, _ := os.LookupEnv(ENVEcsMetadataIMDSv2Enable) config := &Config{ - Type: tea.String("ecs_ram_role"), - RoleName: tea.String(roleName), + Type: tea.String("ecs_ram_role"), + RoleName: tea.String(roleName), + EnableIMDSv2: tea.Bool(strings.ToLower(enableIMDSv2) == "true"), } return config, nil } diff --git a/credentials/instance_provider_test.go b/credentials/instance_provider_test.go index b43be0f..14ff787 100644 --- a/credentials/instance_provider_test.go +++ b/credentials/instance_provider_test.go @@ -25,6 +25,33 @@ func TestInstanceCredentialsProvider(t *testing.T) { assert.Equal(t, "role_name", tea.StringValue(c.RoleName)) assert.Equal(t, "ecs_ram_role", tea.StringValue(c.Type)) + os.Setenv(ENVEcsMetadataIMDSv2Enable, "1") + c, err = p.resolve() + assert.Nil(t, err) + assert.Equal(t, "role_name", tea.StringValue(c.RoleName)) + assert.Equal(t, "ecs_ram_role", tea.StringValue(c.Type)) + + os.Setenv(ENVEcsMetadataIMDSv2Enable, "1") + c, err = p.resolve() + assert.Nil(t, err) + assert.Equal(t, "role_name", tea.StringValue(c.RoleName)) + assert.Equal(t, "ecs_ram_role", tea.StringValue(c.Type)) + assert.False(t, tea.BoolValue(c.EnableIMDSv2)) + + os.Setenv(ENVEcsMetadataIMDSv2Enable, "false") + c, err = p.resolve() + assert.Nil(t, err) + assert.Equal(t, "role_name", tea.StringValue(c.RoleName)) + assert.Equal(t, "ecs_ram_role", tea.StringValue(c.Type)) + assert.False(t, tea.BoolValue(c.EnableIMDSv2)) + + os.Setenv(ENVEcsMetadataIMDSv2Enable, "true") + c, err = p.resolve() + assert.Nil(t, err) + assert.Equal(t, "role_name", tea.StringValue(c.RoleName)) + assert.Equal(t, "ecs_ram_role", tea.StringValue(c.Type)) + assert.True(t, tea.BoolValue(c.EnableIMDSv2)) + os.Unsetenv(ENVEcsMetadata) c, err = p.resolve() assert.Nil(t, c) diff --git a/credentials/provider.go b/credentials/provider.go index 9ad6216..506e110 100644 --- a/credentials/provider.go +++ b/credentials/provider.go @@ -2,13 +2,14 @@ package credentials // Environmental virables that may be used by the provider const ( - ENVCredentialFile = "ALIBABA_CLOUD_CREDENTIALS_FILE" - ENVEcsMetadata = "ALIBABA_CLOUD_ECS_METADATA" - PATHCredentialFile = "~/.alibabacloud/credentials" - ENVRoleArn = "ALIBABA_CLOUD_ROLE_ARN" - ENVOIDCProviderArn = "ALIBABA_CLOUD_OIDC_PROVIDER_ARN" - ENVOIDCTokenFile = "ALIBABA_CLOUD_OIDC_TOKEN_FILE" - ENVRoleSessionName = "ALIBABA_CLOUD_ROLE_SESSION_NAME" + ENVCredentialFile = "ALIBABA_CLOUD_CREDENTIALS_FILE" + ENVEcsMetadata = "ALIBABA_CLOUD_ECS_METADATA" + ENVEcsMetadataIMDSv2Enable = "ALIBABA_CLOUD_ECS_IMDSV2_ENABLE" + PATHCredentialFile = "~/.alibabacloud/credentials" + ENVRoleArn = "ALIBABA_CLOUD_ROLE_ARN" + ENVOIDCProviderArn = "ALIBABA_CLOUD_OIDC_PROVIDER_ARN" + ENVOIDCTokenFile = "ALIBABA_CLOUD_OIDC_TOKEN_FILE" + ENVRoleSessionName = "ALIBABA_CLOUD_ROLE_SESSION_NAME" ) // Provider will be implemented When you want to customize the provider. diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c4d4f5b --- /dev/null +++ b/go.sum @@ -0,0 +1,70 @@ +github.com/alibabacloud-go/debug v1.0.0 h1:3eIEQWfay1fB24PQIEzXAswlVJtdQok8f3EVN5VrBnA= +github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= +github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU= +github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=