aliyun-net-sdk-core/Auth/Provider/ECSMetadataServiceCredentialsFetcher.cs (211 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ using System; using System.Collections.Generic; using System.Net; using System.Text; using Aliyun.Acs.Core.Exceptions; using Aliyun.Acs.Core.Http; using Aliyun.Acs.Core.Reader; using Aliyun.Acs.Core.Utils; namespace Aliyun.Acs.Core.Auth { public class ECSMetadataServiceCredentialsFetcher : AlibabaCloudCredentialsProvider { private const string URL_IN_ECS_METADATA = "/latest/meta-data/ram/security-credentials/"; private const string URL_IN_ECS_METADATA_TOKEN = "/latest/api/token"; private const int DEFAULT_TIMEOUT_IN_MILLISECONDS = 1000; private const string ECS_METADAT_FETCH_ERROR_MSG = "Failed to get RAM session credentials from ECS metadata service."; // stands for 3600 s private const int DEFAULT_ECS_SESSION_TOKEN_DURATION_SECONDS = 3600; private string credentialUrl; private string metadataServiceHost = "100.100.100.200"; private string roleName; private readonly bool disableIMDSv1; private const int metadataTokenDuration = 21600; private int connectionTimeout; private int readTimeout; public ECSMetadataServiceCredentialsFetcher() { this.connectionTimeout = DEFAULT_TIMEOUT_IN_MILLISECONDS; this.readTimeout = DEFAULT_TIMEOUT_IN_MILLISECONDS; this.disableIMDSv1 = false; } public ECSMetadataServiceCredentialsFetcher(string roleName, bool? disableIMDSv1, int? connectionTimeout, int? readTimeout) { this.roleName = roleName; this.disableIMDSv1 = disableIMDSv1 != null && (bool)disableIMDSv1; this.connectionTimeout = connectionTimeout == null ? 1000 : connectionTimeout.Value; this.readTimeout = readTimeout == null ? 1000 : readTimeout.Value; } public AlibabaCloudCredentials GetCredentials() { return Fetch(); } [Obsolete] public void SetRoleName(string roleName) { if (string.IsNullOrEmpty(roleName)) { throw new ArgumentNullException("You must specify a valid role name."); } this.roleName = roleName; SetCredentialUrl(); } public string GetRoleName() { return roleName; } private void SetCredentialUrl() { credentialUrl = "http://" + metadataServiceHost + URL_IN_ECS_METADATA + roleName; } [Obsolete] public void WithECSMetadataServiceHost(string host) { metadataServiceHost = host; SetCredentialUrl(); } [Obsolete] public void WithConnectionTimeout(int milliseconds) { this.connectionTimeout = milliseconds; this.readTimeout = milliseconds; } [Obsolete] public string GetMetadata() { return GetMetadata(credentialUrl); } private string GetMetadata(string url) { var request = new HttpRequest(url); request.Method = MethodType.GET; request.SetConnectTimeoutInMilliSeconds(this.connectionTimeout); request.SetReadTimeoutInMilliSeconds(this.readTimeout); var metadataToken = this.GetMetadataToken(); if (metadataToken != null) { request.Headers.Add("X-aliyun-ecs-metadata-token", metadataToken); } HttpResponse response; try { response = GetResponse(request); } catch (WebException e) { throw new ClientException("Failed to connect ECS Metadata Service: " + e); } if (404 == response.Status) { throw new ClientException("The role name was not found in the instance."); } if (response.Status != 200) { throw new ClientException(ECS_METADAT_FETCH_ERROR_MSG + " HttpCode=" + response.Status); } return Encoding.UTF8.GetString(response.Content); } private string GetMetadataToken() { try { HttpRequest httpRequest = new HttpRequest("http://" + metadataServiceHost + URL_IN_ECS_METADATA_TOKEN) { Method = MethodType.PUT }; httpRequest.SetConnectTimeoutInMilliSeconds(this.connectionTimeout); httpRequest.SetReadTimeoutInMilliSeconds(this.readTimeout); httpRequest.Headers.Add("X-aliyun-ecs-metadata-token-ttl-seconds", metadataTokenDuration.ToString()); HttpResponse httpResponse; try { httpResponse = GetResponse(httpRequest); } catch (Exception ex) { throw new ClientException("Failed to connect ECS Metadata Service: " + ex); } if (httpResponse != null && httpResponse.Status != 200) { throw new ClientException("Failed to get token from ECS Metadata Service. HttpCode=" + httpResponse.Status + ", ResponseMessage=" + httpResponse.GetHttpContentString()); } return httpResponse.GetHttpContentString(); } catch (Exception ex) { return ThrowErrorOrReturn(ex); } } private string ThrowErrorOrReturn(Exception e) { if (this.disableIMDSv1) { throw new ClientException("Failed to get token from ECS Metadata Service, and fallback to IMDS v1 is disabled via the disableIMDSv1 configuration is turned on. Original error: " + e.Message); } return null; } public virtual InstanceProfileCredentials Fetch() { Dictionary<string, string> dic; try { var roleName = this.roleName; if (string.IsNullOrEmpty(this.roleName)) { roleName = GetMetadata("http://" + metadataServiceHost + URL_IN_ECS_METADATA); } var jsonContent = GetMetadata("http://" + metadataServiceHost + URL_IN_ECS_METADATA + roleName); IReader reader = new JsonReader(); dic = reader.Read(jsonContent, ""); } catch (Exception e) { throw new ClientException(ECS_METADAT_FETCH_ERROR_MSG + " Reason: " + e); } if ( DictionaryUtil.Get(dic, ".Code") == null || DictionaryUtil.Get(dic, ".AccessKeyId") == null || DictionaryUtil.Get(dic, ".AccessKeySecret") == null || DictionaryUtil.Get(dic, ".SecurityToken") == null || DictionaryUtil.Get(dic, ".Expiration") == null ) { throw new ClientException("Invalid json got from ECS Metadata service."); } if (!"Success".Equals(DictionaryUtil.Get(dic, ".Code"))) { throw new ClientException(ECS_METADAT_FETCH_ERROR_MSG); } return new InstanceProfileCredentials( DictionaryUtil.Get(dic, ".AccessKeyId"), DictionaryUtil.Get(dic, ".AccessKeySecret"), DictionaryUtil.Get(dic, ".SecurityToken"), DictionaryUtil.Get(dic, ".Expiration"), DEFAULT_ECS_SESSION_TOKEN_DURATION_SECONDS ); } public InstanceProfileCredentials Fetch(int retryTimes) { for (var i = 0; i <= retryTimes; i++) { try { return Fetch(); } catch (ClientException e) { if (i == retryTimes) { throw new ClientException(e.ErrorCode, e.ErrorMessage); } } } throw new ClientException("Failed to connect ECS Metadata Service: Max retry times exceeded."); } public virtual HttpResponse GetResponse(HttpRequest request) { return HttpResponse.GetResponse(request); } } }