1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */

15
16 package software.amazon.awssdk.auth.credentials;
17
18 import java.io.IOException;
19 import java.net.URI;
20 import java.util.HashMap;
21 import java.util.Map;
22 import software.amazon.awssdk.annotations.SdkPublicApi;
23 import software.amazon.awssdk.core.SdkSystemSetting;
24 import software.amazon.awssdk.core.exception.SdkClientException;
25 import software.amazon.awssdk.core.internal.util.UserAgentUtils;
26 import software.amazon.awssdk.regions.internal.util.EC2MetadataUtils;
27 import software.amazon.awssdk.regions.util.HttpResourcesUtils;
28 import software.amazon.awssdk.regions.util.ResourcesEndpointProvider;
29 import software.amazon.awssdk.utils.ToString;
30
31 /**
32  * Credentials provider implementation that loads credentials from the Amazon EC2 Instance Metadata Service.
33  *
34  * <P>
35  * If {@link SdkSystemSetting#AWS_EC2_METADATA_DISABLED} is set to true, it will not try to load
36  * credentials from EC2 metadata service and will return null.
37  */

38 @SdkPublicApi
39 public final class InstanceProfileCredentialsProvider extends HttpCredentialsProvider {
40     private static final String EC2_METADATA_TOKEN_HEADER = "x-aws-ec2-metadata-token";
41
42     private static final String SECURITY_CREDENTIALS_RESOURCE = "/latest/meta-data/iam/security-credentials/";
43
44     /**
45      * @see #builder()
46      */

47     private InstanceProfileCredentialsProvider(BuilderImpl builder) {
48         super(builder);
49     }
50
51     /**
52      * Create a builder for creating a {@link InstanceProfileCredentialsProvider}.
53      */

54     public static Builder builder() {
55         return new BuilderImpl();
56     }
57
58     /**
59      * Create a {@link InstanceProfileCredentialsProvider} with default values.
60      *
61      * @return a {@link InstanceProfileCredentialsProvider}
62      */

63     public static InstanceProfileCredentialsProvider create() {
64         return builder().build();
65     }
66
67     @Override
68     protected ResourcesEndpointProvider getCredentialsEndpointProvider() {
69         return new InstanceProviderCredentialsEndpointProvider(getToken());
70     }
71
72     @Override
73     protected boolean isLocalCredentialLoadingDisabled() {
74         return SdkSystemSetting.AWS_EC2_METADATA_DISABLED.getBooleanValueOrThrow();
75     }
76
77     @Override
78     public String toString() {
79         return ToString.create("InstanceProfileCredentialsProvider");
80     }
81
82     private String getToken() {
83         return EC2MetadataUtils.getToken();
84     }
85
86     private static ResourcesEndpointProvider includeTokenHeader(ResourcesEndpointProvider provider, String token) {
87         return new ResourcesEndpointProvider() {
88             @Override
89             public URI endpoint() throws IOException {
90                 return provider.endpoint();
91             }
92
93             @Override
94             public Map<String, String> headers() {
95                 Map<String, String> headers = new HashMap<>(provider.headers());
96                 headers.put(EC2_METADATA_TOKEN_HEADER, token);
97                 return headers;
98             }
99         };
100     }
101
102     private static final class InstanceProviderCredentialsEndpointProvider implements ResourcesEndpointProvider {
103         private final String metadataToken;
104
105         private InstanceProviderCredentialsEndpointProvider(String metadataToken) {
106             this.metadataToken = metadataToken;
107         }
108
109         @Override
110         public URI endpoint() throws IOException {
111             String host = SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT.getStringValueOrThrow();
112
113             URI endpoint = URI.create(host + SECURITY_CREDENTIALS_RESOURCE);
114             ResourcesEndpointProvider endpointProvider = () -> endpoint;
115
116             if (metadataToken != null) {
117                 endpointProvider = includeTokenHeader(endpointProvider, metadataToken);
118             }
119
120             String securityCredentialsList = HttpResourcesUtils.instance().readResource(endpointProvider);
121             String[] securityCredentials = securityCredentialsList.trim().split("\n");
122
123             if (securityCredentials.length == 0) {
124                 throw SdkClientException.builder().message("Unable to load credentials path").build();
125             }
126
127             return URI.create(host + SECURITY_CREDENTIALS_RESOURCE + securityCredentials[0]);
128         }
129
130         @Override
131         public Map<String, String> headers() {
132             Map<String, String> requestHeaders = new HashMap<>();
133             requestHeaders.put("User-Agent", UserAgentUtils.getUserAgent());
134             requestHeaders.put("Accept""*/*");
135             requestHeaders.put("Connection""keep-alive");
136
137             if (metadataToken != null) {
138                 requestHeaders.put(EC2_METADATA_TOKEN_HEADER, metadataToken);
139             }
140
141             return requestHeaders;
142         }
143     }
144
145
146     /**
147      * A builder for creating a custom a {@link InstanceProfileCredentialsProvider}.
148      */

149     public interface Builder extends HttpCredentialsProvider.Builder<InstanceProfileCredentialsProvider, Builder> {
150
151         /**
152          * Build a {@link InstanceProfileCredentialsProvider} from the provided configuration.
153          */

154         @Override
155         InstanceProfileCredentialsProvider build();
156     }
157
158     private static final class BuilderImpl
159         extends HttpCredentialsProvider.BuilderImpl<InstanceProfileCredentialsProvider, Builder>
160         implements Builder {
161
162         private BuilderImpl() {
163             super.asyncThreadName("instance-profile-credentials-provider");
164         }
165
166         @Override
167         public InstanceProfileCredentialsProvider build() {
168             return new InstanceProfileCredentialsProvider(this);
169         }
170     }
171 }
172