| /* |
| * Copyright 2012-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"). |
| * You may not use this file except in compliance with the License. |
| * A copy of the License is located at |
| * |
| * http://aws.amazon.com/apache2.0 |
| * |
| * or in the "license" file accompanying this file. This file 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. |
| */ |
| package com.amazonaws.auth; |
| |
| import com.amazonaws.AmazonClientException; |
| import com.amazonaws.SDKGlobalConfiguration; |
| import com.amazonaws.SdkClientException; |
| import com.amazonaws.internal.CredentialsEndpointProvider; |
| import com.amazonaws.internal.EC2CredentialsUtils; |
| import com.amazonaws.util.EC2MetadataUtils; |
| import java.io.Closeable; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.concurrent.TimeUnit; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| /** |
| * Credentials provider implementation that loads credentials from the Amazon EC2 Instance Metadata Service. |
| * |
| * <p>When using {@link InstanceProfileCredentialsProvider} with asynchronous refreshing it is |
| * <b>strongly</b> recommended to explicitly call {@link #close()} to release the async thread.</p> |
| */ |
| public class InstanceProfileCredentialsProvider implements AWSCredentialsProvider, Closeable { |
| |
| private static final Log LOG = LogFactory.getLog(InstanceProfileCredentialsProvider.class); |
| |
| /** |
| * The wait time, after which the background thread initiates a refresh to |
| * load latest credentials if needed. |
| */ |
| private static final int ASYNC_REFRESH_INTERVAL_TIME_MINUTES = 1; |
| |
| /** |
| * The default InstanceProfileCredentialsProvider that can be shared by |
| * multiple CredentialsProvider instance threads to shrink the amount of |
| * requests to EC2 metadata service. |
| */ |
| private static final InstanceProfileCredentialsProvider INSTANCE = new InstanceProfileCredentialsProvider(); |
| |
| private final EC2CredentialsFetcher credentialsFetcher; |
| |
| /** |
| * The executor service used for refreshing the credentials in the |
| * background. |
| */ |
| private volatile ScheduledExecutorService executor; |
| |
| private volatile boolean shouldRefresh = false; |
| |
| /** |
| * @deprecated for the singleton method {@link #getInstance()}. |
| */ |
| @Deprecated |
| public InstanceProfileCredentialsProvider() { |
| this(false); |
| } |
| |
| /** |
| * Spins up a new thread to refresh the credentials asynchronously if |
| * refreshCredentialsAsync is set to true, otherwise the credentials will be |
| * refreshed from the instance metadata service synchronously, |
| * |
| * <p>It is <b>strongly</b> recommended to reuse instances of this credentials provider, especially |
| * when async refreshing is used since a background thread is created.</p> |
| * |
| * @param refreshCredentialsAsync |
| * true if credentials needs to be refreshed asynchronously else |
| * false. |
| */ |
| public InstanceProfileCredentialsProvider(boolean refreshCredentialsAsync) { |
| this(refreshCredentialsAsync, true); |
| } |
| |
| /** |
| * Spins up a new thread to refresh the credentials asynchronously. |
| * |
| * <p>It is <b>strongly</b> recommended to reuse instances of this credentials provider, especially |
| * when async refreshing is used since a background thread is created.</p> |
| * |
| * @param eagerlyRefreshCredentialsAsync |
| * when set to false will not attempt to refresh credentials asynchronously |
| * until after a call has been made to {@link #getCredentials()} - ensures that |
| * {@link EC2CredentialsFetcher#getCredentials()} is only hit when this CredentialProvider is actually required |
| */ |
| public static InstanceProfileCredentialsProvider createAsyncRefreshingProvider(final boolean eagerlyRefreshCredentialsAsync) { |
| return new InstanceProfileCredentialsProvider(true, eagerlyRefreshCredentialsAsync); |
| } |
| |
| private InstanceProfileCredentialsProvider(boolean refreshCredentialsAsync, final boolean eagerlyRefreshCredentialsAsync) { |
| |
| credentialsFetcher = new EC2CredentialsFetcher(new InstanceMetadataCredentialsEndpointProvider()); |
| |
| if (!SDKGlobalConfiguration.isEc2MetadataDisabled()) { |
| if (refreshCredentialsAsync) { |
| executor = Executors.newScheduledThreadPool(1); |
| executor.scheduleWithFixedDelay(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| if (shouldRefresh) credentialsFetcher.getCredentials(); |
| } catch (AmazonClientException ace) { |
| handleError(ace); |
| } catch (RuntimeException re) { |
| handleError(re); |
| } |
| } |
| }, 0, ASYNC_REFRESH_INTERVAL_TIME_MINUTES, TimeUnit.MINUTES); |
| } |
| } |
| } |
| |
| /** |
| * Returns a singleton {@link InstanceProfileCredentialsProvider} that does not refresh credentials asynchronously. |
| * |
| * <p> |
| * See {@link #InstanceProfileCredentialsProvider(boolean)} or {@link #createAsyncRefreshingProvider(boolean)} for |
| * asynchronous credentials refreshing. |
| * </p> |
| */ |
| public static InstanceProfileCredentialsProvider getInstance() { |
| return INSTANCE; |
| } |
| |
| private void handleError(Throwable t) { |
| refresh(); |
| LOG.error(t.getMessage(), t); |
| } |
| |
| @Override |
| protected void finalize() throws Throwable { |
| if (executor != null) { |
| executor.shutdownNow(); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @throws AmazonClientException if {@link SDKGlobalConfiguration#isEc2MetadataDisabled()} is true |
| */ |
| @Override |
| public AWSCredentials getCredentials() { |
| if (SDKGlobalConfiguration.isEc2MetadataDisabled()) { |
| throw new AmazonClientException("AWS_EC2_METADATA_DISABLED is set to true, not loading credentials from EC2 Instance " |
| + "Metadata service"); |
| } |
| AWSCredentials creds = credentialsFetcher.getCredentials(); |
| shouldRefresh = true; |
| return creds; |
| } |
| |
| @Override |
| public void refresh() { |
| if (credentialsFetcher != null) { |
| credentialsFetcher.refresh(); |
| } |
| } |
| |
| @Override |
| public void close() throws IOException { |
| if (executor != null) { |
| executor.shutdownNow(); |
| executor = null; |
| } |
| } |
| |
| private static class InstanceMetadataCredentialsEndpointProvider extends CredentialsEndpointProvider { |
| @Override |
| public URI getCredentialsEndpoint() throws URISyntaxException, IOException { |
| String host = EC2MetadataUtils.getHostAddressForEC2MetadataService(); |
| |
| String securityCredentialsList = EC2CredentialsUtils.getInstance().readResource(new URI(host + EC2MetadataUtils.SECURITY_CREDENTIALS_RESOURCE)); |
| String[] securityCredentials = securityCredentialsList.trim().split("\n"); |
| if (securityCredentials.length == 0) { |
| throw new SdkClientException("Unable to load credentials path"); |
| } |
| |
| return new URI(host + EC2MetadataUtils.SECURITY_CREDENTIALS_RESOURCE + securityCredentials[0]); |
| } |
| } |
| } |