Skip to content

Commit df009e0

Browse files
HarshitGuptasteveloughran
authored andcommitted
HADOOP-19004. S3A: Support Authentication through HttpSigner API
Move to the new auth flow based signers for aws. * Implement a new Signer Initialization Chain * Add a new instantiation method * Add a new test * Fix Reflection Code for SignerInitialization (+steve changes) * move instantiation code into SignerFactory * CustomHttpSigner goes into production code for broader testing * some tuning of property values and names of constants/methods. Change-Id: I8f34af4bd958d631bcb03d7c073ed1da01cb8205
1 parent 931fa49 commit df009e0

File tree

8 files changed

+314
-1
lines changed

8 files changed

+314
-1
lines changed

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,4 +1525,19 @@ private Constants() {
15251525
public static final String STORE_CAPABILITY_AWS_V2 =
15261526
"fs.s3a.capability.aws.v2";
15271527

1528+
/**
1529+
* Flag to switch to a v2 SDK HTTP signer. Value {@value}.
1530+
*/
1531+
public static final String HTTP_SIGNER_ENABLED = "fs.s3a.http.signer.enabled";
1532+
1533+
/**
1534+
* Default value of {@link #HTTP_SIGNER_ENABLED}: {@value}.
1535+
*/
1536+
public static final boolean HTTP_SIGNER_ENABLED_DEFAULT = false;
1537+
1538+
/**
1539+
* Classname of the http signer to use when {@link #HTTP_SIGNER_ENABLED}
1540+
* is true: {@value}.
1541+
*/
1542+
public static String HTTP_SIGNER_CLASS_NAME = "fs.s3a.http.signer.class";
15281543
}

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@
3232
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
3333
import software.amazon.awssdk.core.retry.RetryPolicy;
3434
import software.amazon.awssdk.http.apache.ApacheHttpClient;
35+
import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme;
36+
import software.amazon.awssdk.http.auth.spi.signer.HttpSigner;
3537
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
38+
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
3639
import software.amazon.awssdk.regions.Region;
3740
import software.amazon.awssdk.services.s3.S3AsyncClient;
3841
import software.amazon.awssdk.services.s3.S3BaseClientBuilder;
@@ -52,10 +55,15 @@
5255
import static org.apache.hadoop.fs.s3a.Constants.AWS_REGION;
5356
import static org.apache.hadoop.fs.s3a.Constants.AWS_S3_DEFAULT_REGION;
5457
import static org.apache.hadoop.fs.s3a.Constants.CENTRAL_ENDPOINT;
58+
import static org.apache.hadoop.fs.s3a.Constants.HTTP_SIGNER_CLASS_NAME;
59+
import static org.apache.hadoop.fs.s3a.Constants.HTTP_SIGNER_ENABLED;
60+
import static org.apache.hadoop.fs.s3a.Constants.HTTP_SIGNER_ENABLED_DEFAULT;
61+
import static org.apache.hadoop.fs.s3a.auth.SignerFactory.createHttpSigner;
5562
import static org.apache.hadoop.fs.s3a.impl.AWSHeaders.REQUESTER_PAYS_HEADER;
5663
import static org.apache.hadoop.fs.s3a.Constants.DEFAULT_SECURE_CONNECTIONS;
5764
import static org.apache.hadoop.fs.s3a.Constants.SECURE_CONNECTIONS;
5865
import static org.apache.hadoop.fs.s3a.Constants.AWS_SERVICE_IDENTIFIER_S3;
66+
import static org.apache.hadoop.fs.s3a.impl.InternalConstants.AUTH_SCHEME_AWS_SIGV_4;
5967

6068

6169
/**
@@ -163,17 +171,26 @@ private <BuilderT extends S3BaseClientBuilder<BuilderT, ClientT>, ClientT> Build
163171
.pathStyleAccessEnabled(parameters.isPathStyleAccess())
164172
.build();
165173

166-
return builder
174+
S3BaseClientBuilder s3BaseClientBuilder = builder
167175
.overrideConfiguration(createClientOverrideConfiguration(parameters, conf))
168176
.credentialsProvider(parameters.getCredentialSet())
169177
.serviceConfiguration(serviceConfiguration);
178+
179+
if (conf.getBoolean(HTTP_SIGNER_ENABLED, HTTP_SIGNER_ENABLED_DEFAULT)) {
180+
// use an http signer through an AuthScheme
181+
final AuthScheme<AwsCredentialsIdentity> signer =
182+
createHttpSigner(conf, AUTH_SCHEME_AWS_SIGV_4, HTTP_SIGNER_CLASS_NAME);
183+
builder.putAuthScheme(signer);
184+
}
185+
return (BuilderT) s3BaseClientBuilder;
170186
}
171187

172188
/**
173189
* Create an override configuration for an S3 client.
174190
* @param parameters parameter object
175191
* @param conf configuration object
176192
* @throws IOException any IOE raised, or translated exception
193+
* @throws RuntimeException some failures creating an http signer
177194
* @return the override configuration
178195
*/
179196
protected ClientOverrideConfiguration createClientOverrideConfiguration(

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AFileSystem.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
import software.amazon.awssdk.services.s3.S3Client;
5959
import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest;
6060
import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse;
61+
import software.amazon.awssdk.services.s3.model.CreateSessionRequest;
62+
import software.amazon.awssdk.services.s3.model.CreateSessionResponse;
6163
import software.amazon.awssdk.services.s3.model.GetBucketLocationRequest;
6264
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
6365
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
@@ -5751,6 +5753,10 @@ public StoreContext createStoreContext() {
57515753
.build();
57525754
}
57535755

5756+
public CreateSessionResponse createSessionInternal(CreateSessionRequest createSessionRequest){
5757+
return this.s3Client.createSession(createSessionRequest);
5758+
}
5759+
57545760
/**
57555761
* Create a marker tools operations binding for this store.
57565762
* Auditing:
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.fs.s3a.auth;
20+
21+
import java.util.concurrent.CompletableFuture;
22+
23+
import org.slf4j.Logger;
24+
import org.slf4j.LoggerFactory;
25+
import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner;
26+
import software.amazon.awssdk.http.auth.spi.signer.AsyncSignRequest;
27+
import software.amazon.awssdk.http.auth.spi.signer.AsyncSignedRequest;
28+
import software.amazon.awssdk.http.auth.spi.signer.HttpSigner;
29+
import software.amazon.awssdk.http.auth.spi.signer.SignRequest;
30+
import software.amazon.awssdk.http.auth.spi.signer.SignedRequest;
31+
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
32+
33+
/**
34+
* Custom signer that delegates to the AWS V4 signer.
35+
* Logs at TRACE the string value of any request.
36+
* This is in the production code to support testing the signer plugin mechansim.
37+
* To use
38+
* <pre>
39+
* fs.s3a.http.signer.enabled = true
40+
* fs.s3a.http.signer.class = org.apache.hadoop.fs.s3a.auth.CustomHttpSigner
41+
* <pre>
42+
*/
43+
public final class CustomHttpSigner implements HttpSigner<AwsCredentialsIdentity> {
44+
private static final Logger LOG = LoggerFactory
45+
.getLogger(CustomHttpSigner.class);
46+
47+
/**
48+
* The delegate signer.
49+
*/
50+
private final HttpSigner<AwsCredentialsIdentity> delegateSigner;
51+
52+
public CustomHttpSigner() {
53+
delegateSigner = AwsV4HttpSigner.create();
54+
}
55+
56+
@Override
57+
public SignedRequest sign(SignRequest<? extends AwsCredentialsIdentity>
58+
request) {
59+
LOG.trace("Signing request:{}", request.request());
60+
return delegateSigner.sign(request);
61+
}
62+
63+
@Override
64+
public CompletableFuture<AsyncSignedRequest> signAsync(final AsyncSignRequest<? extends AwsCredentialsIdentity> request) {
65+
LOG.trace("Signing async request:{}", request.request());
66+
return delegateSigner.signAsync(request);
67+
}
68+
}

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/auth/SignerFactory.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,20 @@
2929
import software.amazon.awssdk.auth.signer.AwsS3V4Signer;
3030
import software.amazon.awssdk.core.signer.NoOpSigner;
3131
import software.amazon.awssdk.core.signer.Signer;
32+
import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme;
33+
import software.amazon.awssdk.http.auth.spi.signer.HttpSigner;
34+
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
35+
import software.amazon.awssdk.identity.spi.IdentityProvider;
36+
import software.amazon.awssdk.identity.spi.IdentityProviders;
3237

38+
import org.apache.hadoop.conf.Configuration;
3339
import org.apache.hadoop.fs.s3a.S3AUtils;
3440
import org.apache.hadoop.fs.s3a.impl.InstantiationIOException;
3541

42+
import static org.apache.hadoop.fs.s3a.Constants.HTTP_SIGNER_CLASS_NAME;
3643
import static org.apache.hadoop.fs.s3a.impl.InstantiationIOException.unavailable;
3744
import static org.apache.hadoop.util.Preconditions.checkArgument;
45+
import static org.apache.hadoop.util.Preconditions.checkState;
3846

3947
/**
4048
* Signer factory used to register and create signers.
@@ -119,4 +127,61 @@ public static Signer createSigner(String signerType, String configKey) throws IO
119127

120128
return signer;
121129
}
130+
131+
/**
132+
* Create an auth scheme instance from an ID and a signer.
133+
* @param schemeId scheme id
134+
* @param signer signer
135+
* @return the auth scheme
136+
*/
137+
public static AuthScheme<AwsCredentialsIdentity> createAuthScheme(String schemeId,
138+
HttpSigner<AwsCredentialsIdentity> signer) {
139+
return new AuthScheme<AwsCredentialsIdentity>() {
140+
@Override
141+
public String schemeId() {
142+
return schemeId;
143+
}
144+
@Override
145+
public IdentityProvider<AwsCredentialsIdentity> identityProvider(IdentityProviders providers) {
146+
return providers.identityProvider(AwsCredentialsIdentity.class);
147+
}
148+
@Override
149+
public HttpSigner<AwsCredentialsIdentity> signer() {
150+
return signer;
151+
}
152+
};
153+
}
154+
155+
/**
156+
* Create an auth scheme by looking up the signer class in the configuration,
157+
* loading and instantiating it.
158+
* @param conf configuration
159+
* @param scheme scheme to bond to
160+
* @param configKey configuration key
161+
* @return the auth scheme
162+
* @throws InstantiationIOException failure to instantiate
163+
* @throws IllegalStateException if the signer class is not defined
164+
* @throws RuntimeException other configuration problems
165+
*/
166+
public static AuthScheme<AwsCredentialsIdentity> createHttpSigner(
167+
Configuration conf, String scheme, String configKey) throws IOException {
168+
169+
final Class<? extends HttpSigner> clazz = conf.getClass(HTTP_SIGNER_CLASS_NAME,
170+
null, HttpSigner.class);
171+
checkState(clazz != null, "No http signer class defined in %s", configKey);
172+
LOG.debug("Creating http signer {} from {}", clazz, configKey);
173+
try {
174+
return createAuthScheme(scheme, clazz.newInstance());
175+
176+
} catch (InstantiationException | IllegalAccessException e) {
177+
throw new InstantiationIOException(
178+
InstantiationIOException.Kind.InstantiationFailure,
179+
null,
180+
clazz.getName(),
181+
HTTP_SIGNER_CLASS_NAME,
182+
e.toString(),
183+
e);
184+
}
185+
}
186+
122187
}

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/impl/AWSClientConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ private AWSClientConfig() {
105105
* @param awsServiceIdentifier service
106106
* @return the builder inited with signer, timeouts and UA.
107107
* @throws IOException failure.
108+
* @throws RuntimeException some failures creating an http signer
108109
*/
109110
public static ClientOverrideConfiguration.Builder createClientConfigBuilder(Configuration conf,
110111
String awsServiceIdentifier) throws IOException {

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/impl/InternalConstants.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,4 +286,10 @@ private InternalConstants() {
286286
FS_S3A_CREATE_PERFORMANCE_ENABLED,
287287
DIRECTORY_OPERATIONS_PURGE_UPLOADS,
288288
ENABLE_MULTI_DELETE));
289+
290+
/**
291+
* AWS V4 Auth Scheme to use when creating signers.
292+
*/
293+
public static final String AUTH_SCHEME_AWS_SIGV_4 = "aws.auth#sigv4";
294+
289295
}

0 commit comments

Comments
 (0)