| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- package providers
- import (
- "encoding/json"
- "errors"
- "fmt"
- "os"
- "strconv"
- "strings"
- "time"
- httputil "github.com/aliyun/credentials-go/credentials/internal/http"
- )
- type ECSRAMRoleCredentialsProvider struct {
- roleName string
- disableIMDSv1 bool
- // for sts
- session *sessionCredentials
- expirationTimestamp int64
- // for http options
- httpOptions *HttpOptions
- }
- type ECSRAMRoleCredentialsProviderBuilder struct {
- provider *ECSRAMRoleCredentialsProvider
- }
- func NewECSRAMRoleCredentialsProviderBuilder() *ECSRAMRoleCredentialsProviderBuilder {
- return &ECSRAMRoleCredentialsProviderBuilder{
- provider: &ECSRAMRoleCredentialsProvider{},
- }
- }
- func (builder *ECSRAMRoleCredentialsProviderBuilder) WithRoleName(roleName string) *ECSRAMRoleCredentialsProviderBuilder {
- builder.provider.roleName = roleName
- return builder
- }
- func (builder *ECSRAMRoleCredentialsProviderBuilder) WithDisableIMDSv1(disableIMDSv1 bool) *ECSRAMRoleCredentialsProviderBuilder {
- builder.provider.disableIMDSv1 = disableIMDSv1
- return builder
- }
- func (builder *ECSRAMRoleCredentialsProviderBuilder) WithHttpOptions(httpOptions *HttpOptions) *ECSRAMRoleCredentialsProviderBuilder {
- builder.provider.httpOptions = httpOptions
- return builder
- }
- const defaultMetadataTokenDuration = 21600 // 6 hours
- func (builder *ECSRAMRoleCredentialsProviderBuilder) Build() (provider *ECSRAMRoleCredentialsProvider, err error) {
- if strings.ToLower(os.Getenv("ALIBABA_CLOUD_ECS_METADATA_DISABLED")) == "true" {
- err = errors.New("IMDS credentials is disabled")
- return
- }
- // 设置 roleName 默认值
- if builder.provider.roleName == "" {
- builder.provider.roleName = os.Getenv("ALIBABA_CLOUD_ECS_METADATA")
- }
- if !builder.provider.disableIMDSv1 {
- builder.provider.disableIMDSv1 = strings.ToLower(os.Getenv("ALIBABA_CLOUD_IMDSV1_DISABLED")) == "true"
- }
- provider = builder.provider
- return
- }
- type ecsRAMRoleResponse struct {
- Code *string `json:"Code"`
- AccessKeyId *string `json:"AccessKeyId"`
- AccessKeySecret *string `json:"AccessKeySecret"`
- SecurityToken *string `json:"SecurityToken"`
- LastUpdated *string `json:"LastUpdated"`
- Expiration *string `json:"Expiration"`
- }
- func (provider *ECSRAMRoleCredentialsProvider) needUpdateCredential() bool {
- if provider.expirationTimestamp == 0 {
- return true
- }
- return provider.expirationTimestamp-time.Now().Unix() <= 180
- }
- func (provider *ECSRAMRoleCredentialsProvider) getRoleName() (roleName string, err error) {
- req := &httputil.Request{
- Method: "GET",
- Protocol: "http",
- Host: "100.100.100.200",
- Path: "/latest/meta-data/ram/security-credentials/",
- Headers: map[string]string{},
- }
- connectTimeout := 1 * time.Second
- readTimeout := 1 * time.Second
- if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 {
- connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond
- }
- if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 {
- readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond
- }
- if provider.httpOptions != nil && provider.httpOptions.Proxy != "" {
- req.Proxy = provider.httpOptions.Proxy
- }
- req.ConnectTimeout = connectTimeout
- req.ReadTimeout = readTimeout
- metadataToken, err := provider.getMetadataToken()
- if err != nil {
- return "", err
- }
- if metadataToken != "" {
- req.Headers["x-aliyun-ecs-metadata-token"] = metadataToken
- }
- res, err := httpDo(req)
- if err != nil {
- err = fmt.Errorf("get role name failed: %s", err.Error())
- return
- }
- if res.StatusCode != 200 {
- err = fmt.Errorf("get role name failed: %s %d", req.BuildRequestURL(), res.StatusCode)
- return
- }
- roleName = strings.TrimSpace(string(res.Body))
- return
- }
- func (provider *ECSRAMRoleCredentialsProvider) getCredentials() (session *sessionCredentials, err error) {
- roleName := provider.roleName
- if roleName == "" {
- roleName, err = provider.getRoleName()
- if err != nil {
- return
- }
- }
- req := &httputil.Request{
- Method: "GET",
- Protocol: "http",
- Host: "100.100.100.200",
- Path: "/latest/meta-data/ram/security-credentials/" + roleName,
- Headers: map[string]string{},
- }
- connectTimeout := 1 * time.Second
- readTimeout := 1 * time.Second
- if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 {
- connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond
- }
- if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 {
- readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond
- }
- if provider.httpOptions != nil && provider.httpOptions.Proxy != "" {
- req.Proxy = provider.httpOptions.Proxy
- }
- req.ConnectTimeout = connectTimeout
- req.ReadTimeout = readTimeout
- metadataToken, err := provider.getMetadataToken()
- if err != nil {
- return nil, err
- }
- if metadataToken != "" {
- req.Headers["x-aliyun-ecs-metadata-token"] = metadataToken
- }
- res, err := httpDo(req)
- if err != nil {
- err = fmt.Errorf("refresh Ecs sts token err: %s", err.Error())
- return
- }
- if res.StatusCode != 200 {
- err = fmt.Errorf("refresh Ecs sts token err, httpStatus: %d, message = %s", res.StatusCode, string(res.Body))
- return
- }
- var data ecsRAMRoleResponse
- err = json.Unmarshal(res.Body, &data)
- if err != nil {
- err = fmt.Errorf("refresh Ecs sts token err, json.Unmarshal fail: %s", err.Error())
- return
- }
- if data.AccessKeyId == nil || data.AccessKeySecret == nil || data.SecurityToken == nil {
- err = fmt.Errorf("refresh Ecs sts token err, fail to get credentials")
- return
- }
- if *data.Code != "Success" {
- err = fmt.Errorf("refresh Ecs sts token err, Code is not Success")
- return
- }
- session = &sessionCredentials{
- AccessKeyId: *data.AccessKeyId,
- AccessKeySecret: *data.AccessKeySecret,
- SecurityToken: *data.SecurityToken,
- Expiration: *data.Expiration,
- }
- return
- }
- func (provider *ECSRAMRoleCredentialsProvider) GetCredentials() (cc *Credentials, err error) {
- if provider.session == nil || provider.needUpdateCredential() {
- session, err1 := provider.getCredentials()
- if err1 != nil {
- return nil, err1
- }
- provider.session = session
- expirationTime, err2 := time.Parse("2006-01-02T15:04:05Z", session.Expiration)
- if err2 != nil {
- return nil, err2
- }
- provider.expirationTimestamp = expirationTime.Unix()
- }
- cc = &Credentials{
- AccessKeyId: provider.session.AccessKeyId,
- AccessKeySecret: provider.session.AccessKeySecret,
- SecurityToken: provider.session.SecurityToken,
- ProviderName: provider.GetProviderName(),
- }
- return
- }
- func (provider *ECSRAMRoleCredentialsProvider) GetProviderName() string {
- return "ecs_ram_role"
- }
- func (provider *ECSRAMRoleCredentialsProvider) getMetadataToken() (metadataToken string, err error) {
- // PUT http://100.100.100.200/latest/api/token
- req := &httputil.Request{
- Method: "PUT",
- Protocol: "http",
- Host: "100.100.100.200",
- Path: "/latest/api/token",
- Headers: map[string]string{
- "X-aliyun-ecs-metadata-token-ttl-seconds": strconv.Itoa(defaultMetadataTokenDuration),
- },
- }
- connectTimeout := 1 * time.Second
- readTimeout := 1 * time.Second
- if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 {
- connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond
- }
- if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 {
- readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond
- }
- if provider.httpOptions != nil && provider.httpOptions.Proxy != "" {
- req.Proxy = provider.httpOptions.Proxy
- }
- req.ConnectTimeout = connectTimeout
- req.ReadTimeout = readTimeout
- res, _err := httpDo(req)
- if _err != nil {
- if provider.disableIMDSv1 {
- err = fmt.Errorf("get metadata token failed: %s", _err.Error())
- }
- return
- }
- if res.StatusCode != 200 {
- if provider.disableIMDSv1 {
- err = fmt.Errorf("refresh Ecs sts token err, httpStatus: %d, message = %s", res.StatusCode, string(res.Body))
- }
- return
- }
- metadataToken = string(res.Body)
- return
- }
|