Skip to content

Commit 2f1e155

Browse files
HADOOP-19004. S3A: Support Authentication through HttpSigner API (#6324)
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 Contributed by Harshit Gupta
1 parent 453e264 commit 2f1e155

File tree

7 files changed

+330
-2
lines changed

7 files changed

+330
-2
lines changed

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,4 +1543,20 @@ private Constants() {
15431543
* Value: {@value}.
15441544
*/
15451545
public static final boolean S3EXPRESS_CREATE_SESSION_DEFAULT = true;
1546+
1547+
/**
1548+
* Flag to switch to a v2 SDK HTTP signer. Value {@value}.
1549+
*/
1550+
public static final String HTTP_SIGNER_ENABLED = "fs.s3a.http.signer.enabled";
1551+
1552+
/**
1553+
* Default value of {@link #HTTP_SIGNER_ENABLED}: {@value}.
1554+
*/
1555+
public static final boolean HTTP_SIGNER_ENABLED_DEFAULT = false;
1556+
1557+
/**
1558+
* Classname of the http signer to use when {@link #HTTP_SIGNER_ENABLED}
1559+
* is true: {@value}.
1560+
*/
1561+
public static final String HTTP_SIGNER_CLASS_NAME = "fs.s3a.http.signer.class";
15461562
}

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
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;
3536
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
37+
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
3638
import software.amazon.awssdk.regions.Region;
3739
import software.amazon.awssdk.services.s3.S3AsyncClient;
3840
import software.amazon.awssdk.services.s3.S3BaseClientBuilder;
@@ -52,10 +54,15 @@
5254
import static org.apache.hadoop.fs.s3a.Constants.AWS_REGION;
5355
import static org.apache.hadoop.fs.s3a.Constants.AWS_S3_DEFAULT_REGION;
5456
import static org.apache.hadoop.fs.s3a.Constants.CENTRAL_ENDPOINT;
55-
import static org.apache.hadoop.fs.s3a.impl.AWSHeaders.REQUESTER_PAYS_HEADER;
57+
import static org.apache.hadoop.fs.s3a.Constants.HTTP_SIGNER_CLASS_NAME;
58+
import static org.apache.hadoop.fs.s3a.Constants.HTTP_SIGNER_ENABLED;
59+
import static org.apache.hadoop.fs.s3a.Constants.HTTP_SIGNER_ENABLED_DEFAULT;
5660
import static org.apache.hadoop.fs.s3a.Constants.DEFAULT_SECURE_CONNECTIONS;
5761
import static org.apache.hadoop.fs.s3a.Constants.SECURE_CONNECTIONS;
5862
import static org.apache.hadoop.fs.s3a.Constants.AWS_SERVICE_IDENTIFIER_S3;
63+
import static org.apache.hadoop.fs.s3a.auth.SignerFactory.createHttpSigner;
64+
import static org.apache.hadoop.fs.s3a.impl.AWSHeaders.REQUESTER_PAYS_HEADER;
65+
import static org.apache.hadoop.fs.s3a.impl.InternalConstants.AUTH_SCHEME_AWS_SIGV_4;
5966

6067

6168
/**
@@ -165,18 +172,27 @@ private <BuilderT extends S3BaseClientBuilder<BuilderT, ClientT>, ClientT> Build
165172
.pathStyleAccessEnabled(parameters.isPathStyleAccess())
166173
.build();
167174

168-
return builder
175+
S3BaseClientBuilder s3BaseClientBuilder = builder
169176
.overrideConfiguration(createClientOverrideConfiguration(parameters, conf))
170177
.credentialsProvider(parameters.getCredentialSet())
171178
.disableS3ExpressSessionAuth(!parameters.isExpressCreateSession())
172179
.serviceConfiguration(serviceConfiguration);
180+
181+
if (conf.getBoolean(HTTP_SIGNER_ENABLED, HTTP_SIGNER_ENABLED_DEFAULT)) {
182+
// use an http signer through an AuthScheme
183+
final AuthScheme<AwsCredentialsIdentity> signer =
184+
createHttpSigner(conf, AUTH_SCHEME_AWS_SIGV_4, HTTP_SIGNER_CLASS_NAME);
185+
builder.putAuthScheme(signer);
186+
}
187+
return (BuilderT) s3BaseClientBuilder;
173188
}
174189

175190
/**
176191
* Create an override configuration for an S3 client.
177192
* @param parameters parameter object
178193
* @param conf configuration object
179194
* @throws IOException any IOE raised, or translated exception
195+
* @throws RuntimeException some failures creating an http signer
180196
* @return the override configuration
181197
*/
182198
protected ClientOverrideConfiguration createClientOverrideConfiguration(
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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(
65+
final AsyncSignRequest<? extends AwsCredentialsIdentity> request) {
66+
67+
LOG.trace("Signing async request:{}", request.request());
68+
return delegateSigner.signAsync(request);
69+
}
70+
}

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

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

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: {@value}.
292+
*/
293+
public static final String AUTH_SCHEME_AWS_SIGV_4 = "aws.auth#sigv4";
294+
289295
}

0 commit comments

Comments
 (0)