Skip to content

Commit bffc3bb

Browse files
committed
Adopt reusable authentication with temporeary credentials assuming role from STS.
1 parent 0e32359 commit bffc3bb

File tree

2 files changed

+39
-144
lines changed

2 files changed

+39
-144
lines changed

hub/src/main/java/cloud/katta/protocols/s3/S3AssumeRoleSession.java

Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,20 @@
77
import ch.cyberduck.core.Host;
88
import ch.cyberduck.core.LoginCallback;
99
import ch.cyberduck.core.OAuthTokens;
10-
import ch.cyberduck.core.aws.CustomClientConfiguration;
1110
import ch.cyberduck.core.exception.LoginCanceledException;
12-
import ch.cyberduck.core.http.CustomServiceUnavailableRetryStrategy;
13-
import ch.cyberduck.core.http.ExecutionCountServiceUnavailableRetryStrategy;
1411
import ch.cyberduck.core.oauth.OAuth2AuthorizationService;
1512
import ch.cyberduck.core.oauth.OAuth2RequestInterceptor;
16-
import ch.cyberduck.core.preferences.HostPreferences;
17-
import ch.cyberduck.core.proxy.ProxyFinder;
18-
import ch.cyberduck.core.s3.S3AuthenticationResponseInterceptor;
13+
import ch.cyberduck.core.preferences.HostPreferencesFactory;
1914
import ch.cyberduck.core.s3.S3CredentialsStrategy;
2015
import ch.cyberduck.core.s3.S3Session;
21-
import ch.cyberduck.core.ssl.ThreadLocalHostnameDelegatingTrustManager;
2216
import ch.cyberduck.core.ssl.X509KeyManager;
2317
import ch.cyberduck.core.ssl.X509TrustManager;
24-
import ch.cyberduck.core.sts.STSAssumeRoleCredentialsRequestInterceptor;
18+
import ch.cyberduck.core.sts.STSRequestInterceptor;
2519

26-
import org.apache.commons.lang3.StringUtils;
2720
import org.apache.http.impl.client.HttpClientBuilder;
2821
import org.apache.logging.log4j.LogManager;
2922
import org.apache.logging.log4j.Logger;
3023

31-
import com.amazonaws.auth.AWSStaticCredentialsProvider;
32-
import com.amazonaws.auth.AnonymousAWSCredentials;
33-
import com.amazonaws.client.builder.AwsClientBuilder;
34-
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
35-
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
36-
3724
import static cloud.katta.protocols.s3.S3AssumeRoleProtocol.OAUTH_TOKENEXCHANGE;
3825

3926
public class S3AssumeRoleSession extends S3Session {
@@ -45,18 +32,17 @@ public S3AssumeRoleSession(final Host host, final X509TrustManager trust, final
4532

4633
/**
4734
* Configured by default with credentials strategy using assume role with web identity followed by
48-
* exchaing the retrieved OIDC token with scoped OAuth tokens to obtain temporary credentials from security
35+
* exchanging the retrieved OIDC token with scoped OAuth tokens to obtain temporary credentials from security
4936
* token server (STS)
5037
*
5138
* @see S3AssumeRoleProtocol#OAUTH_TOKENEXCHANGE
5239
* @see S3AssumeRoleProtocol#S3_ASSUMEROLE_ROLEARN_2
5340
*/
5441
@Override
55-
protected S3CredentialsStrategy configureCredentialsStrategy(final ProxyFinder proxy, final HttpClientBuilder configuration,
56-
final LoginCallback prompt) throws LoginCanceledException {
42+
protected S3CredentialsStrategy configureCredentialsStrategy(final HttpClientBuilder configuration, final LoginCallback prompt) throws LoginCanceledException {
5743
if(host.getProtocol().isOAuthConfigurable()) {
5844
final OAuth2RequestInterceptor oauth;
59-
if(new HostPreferences(host).getBoolean(OAUTH_TOKENEXCHANGE)) {
45+
if(HostPreferencesFactory.get(host).getBoolean(OAUTH_TOKENEXCHANGE)) {
6046
oauth = new TokenExchangeRequestInterceptor(configuration.build(), host, prompt);
6147
}
6248
else {
@@ -68,37 +54,16 @@ protected S3CredentialsStrategy configureCredentialsStrategy(final ProxyFinder p
6854
}
6955
log.debug("Register interceptor {}", oauth);
7056
configuration.addInterceptorLast(oauth);
71-
final AWSSecurityTokenService tokenService = AWSSecurityTokenServiceClientBuilder
72-
.standard()
73-
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(host.getProtocol().getSTSEndpoint(), null))
74-
.withCredentials(new AWSStaticCredentialsProvider(new AnonymousAWSCredentials()))
75-
.withClientConfiguration(new CustomClientConfiguration(host,
76-
new ThreadLocalHostnameDelegatingTrustManager(trust, host.getProtocol().getSTSEndpoint()), key))
77-
.build();
78-
final STSAssumeRoleCredentialsRequestInterceptor sts;
79-
if(StringUtils.isNotBlank(host.getProperty(S3AssumeRoleProtocol.S3_ASSUMEROLE_ROLEARN_2))) {
80-
sts = new STSChainedAssumeRoleRequestInterceptor(oauth, this, tokenService, prompt) {
81-
@Override
82-
protected String getWebIdentityToken(final OAuthTokens oauth) {
83-
return oauth.getAccessToken();
84-
}
85-
};
86-
}
87-
else {
88-
sts = new STSAssumeRoleCredentialsRequestInterceptor(oauth, this, tokenService, prompt) {
89-
@Override
90-
protected String getWebIdentityToken(final OAuthTokens oauth) {
91-
return oauth.getAccessToken();
92-
}
93-
};
94-
}
57+
final STSRequestInterceptor sts = new STSChainedAssumeRoleRequestInterceptor(oauth, host, trust, key, prompt) {
58+
@Override
59+
protected String getWebIdentityToken(final OAuthTokens oauth) {
60+
return oauth.getAccessToken();
61+
}
62+
};
9563
log.debug("Register interceptor {}", sts);
9664
configuration.addInterceptorLast(sts);
97-
final S3AuthenticationResponseInterceptor interceptor = new S3AuthenticationResponseInterceptor(this, sts);
98-
configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host,
99-
new ExecutionCountServiceUnavailableRetryStrategy(interceptor)));
10065
return sts;
10166
}
102-
return super.configureCredentialsStrategy(proxy, configuration, prompt);
67+
return super.configureCredentialsStrategy(configuration, prompt);
10368
}
10469
}

hub/src/main/java/cloud/katta/protocols/s3/STSChainedAssumeRoleRequestInterceptor.java

Lines changed: 27 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -4,129 +4,59 @@
44

55
package cloud.katta.protocols.s3;
66

7-
import ch.cyberduck.core.AsciiRandomStringService;
7+
import ch.cyberduck.core.Credentials;
88
import ch.cyberduck.core.Host;
99
import ch.cyberduck.core.LoginCallback;
10-
import ch.cyberduck.core.OAuthTokens;
10+
import ch.cyberduck.core.Profile;
1111
import ch.cyberduck.core.TemporaryAccessTokens;
1212
import ch.cyberduck.core.exception.BackgroundException;
13-
import ch.cyberduck.core.exception.LoginFailureException;
1413
import ch.cyberduck.core.oauth.OAuth2RequestInterceptor;
15-
import ch.cyberduck.core.preferences.HostPreferences;
1614
import ch.cyberduck.core.preferences.PreferencesReader;
17-
import ch.cyberduck.core.s3.S3Session;
18-
import ch.cyberduck.core.sts.STSAssumeRoleCredentialsRequestInterceptor;
19-
import ch.cyberduck.core.sts.STSExceptionMappingService;
15+
import ch.cyberduck.core.preferences.ProxyPreferencesReader;
16+
import ch.cyberduck.core.ssl.X509KeyManager;
17+
import ch.cyberduck.core.ssl.X509TrustManager;
18+
import ch.cyberduck.core.sts.STSAssumeRoleWithWebIdentityRequestInterceptor;
2019

2120
import org.apache.commons.lang3.StringUtils;
2221
import org.apache.logging.log4j.LogManager;
2322
import org.apache.logging.log4j.Logger;
2423

25-
import java.util.Collections;
26-
27-
import com.amazonaws.auth.AWSSessionCredentials;
28-
import com.amazonaws.auth.AWSSessionCredentialsProvider;
29-
import com.amazonaws.auth.BasicSessionCredentials;
30-
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
31-
import com.amazonaws.services.securitytoken.model.AWSSecurityTokenServiceException;
32-
import com.amazonaws.services.securitytoken.model.AssumeRoleRequest;
33-
import com.amazonaws.services.securitytoken.model.AssumeRoleResult;
34-
import com.amazonaws.services.securitytoken.model.Tag;
35-
import com.auth0.jwt.JWT;
36-
import com.auth0.jwt.exceptions.JWTDecodeException;
37-
3824
/**
3925
* Assume role with temporary credentials obtained using OIDC token from security token service (STS)
4026
*/
41-
public class STSChainedAssumeRoleRequestInterceptor extends STSAssumeRoleCredentialsRequestInterceptor {
27+
public class STSChainedAssumeRoleRequestInterceptor extends STSAssumeRoleWithWebIdentityRequestInterceptor {
4228
private static final Logger log = LogManager.getLogger(STSChainedAssumeRoleRequestInterceptor.class);
4329

4430
private final Host bookmark;
45-
private final AWSSecurityTokenService service;
46-
47-
public STSChainedAssumeRoleRequestInterceptor(final OAuth2RequestInterceptor oauth, final S3Session session,
48-
final AWSSecurityTokenService service, final LoginCallback prompt) {
49-
super(oauth, session, service, prompt);
50-
this.service = service;
51-
this.bookmark = session.getHost();
52-
}
5331

54-
@Override
55-
public TemporaryAccessTokens authorize(final OAuthTokens oauth) throws BackgroundException {
56-
return this.authorize(oauth, super.authorize(oauth));
32+
public STSChainedAssumeRoleRequestInterceptor(final OAuth2RequestInterceptor oauth, final Host host,
33+
final X509TrustManager trust, final X509KeyManager key,
34+
final LoginCallback prompt) {
35+
super(oauth, host, trust, key, prompt);
36+
this.bookmark = host;
5737
}
5838

5939
/**
6040
* Assume role with previously obtained temporary access token
6141
*
62-
* @param tokens Session credentials
42+
* @param credentials Session credentials
6343
* @return Temporary scoped access tokens
6444
* @throws ch.cyberduck.core.exception.ExpiredTokenException Expired identity
6545
* @throws ch.cyberduck.core.exception.LoginFailureException Authorization failure
6646
*/
67-
public TemporaryAccessTokens authorize(final OAuthTokens oauth, final TemporaryAccessTokens tokens) throws BackgroundException {
68-
final AssumeRoleRequest request = new AssumeRoleRequest()
69-
.withRequestCredentialsProvider(new AWSSessionCredentialsProvider() {
70-
@Override
71-
public AWSSessionCredentials getCredentials() {
72-
return new BasicSessionCredentials(
73-
tokens.getAccessKeyId(),
74-
tokens.getSecretAccessKey(),
75-
tokens.getSessionToken());
76-
}
77-
78-
@Override
79-
public void refresh() {
80-
// nothing to do
81-
}
82-
});
83-
log.debug("Chained assume role for {}", bookmark);
84-
log.debug("Assume role with temporary credentials {}", tokens);
85-
final PreferencesReader preferences = new HostPreferences(bookmark);
86-
if(preferences.getInteger(S3AssumeRoleProtocol.S3_ASSUMEROLE_DURATIONSECONDS) != -1) {
87-
request.setDurationSeconds(preferences.getInteger(S3AssumeRoleProtocol.S3_ASSUMEROLE_DURATIONSECONDS));
88-
}
89-
if(StringUtils.isNotBlank(preferences.getProperty(S3AssumeRoleProtocol.S3_ASSUMEROLE_POLICY))) {
90-
request.setPolicy(preferences.getProperty(S3AssumeRoleProtocol.S3_ASSUMEROLE_POLICY));
91-
}
92-
request.setRoleArn(preferences.getProperty(S3AssumeRoleProtocol.S3_ASSUMEROLE_ROLEARN_2));
93-
final String sub;
94-
final String identityToken = this.getWebIdentityToken(oauth);
95-
try {
96-
sub = JWT.decode(identityToken).getSubject();
97-
}
98-
catch(JWTDecodeException e) {
99-
log.warn("Failure {} decoding JWT {}", e, identityToken);
100-
throw new LoginFailureException("Invalid JWT or JSON format in authentication token", e);
101-
}
102-
if(StringUtils.isNotBlank(preferences.getProperty(S3AssumeRoleProtocol.S3_ASSUMEROLE_ROLESESSIONNAME))) {
103-
request.setRoleSessionName(preferences.getProperty(S3AssumeRoleProtocol.S3_ASSUMEROLE_ROLESESSIONNAME));
104-
}
105-
else {
106-
if(StringUtils.isNotBlank(sub)) {
107-
request.setRoleSessionName(sub);
108-
}
109-
else {
110-
log.warn("Missing subject in decoding JWT {}", identityToken);
111-
request.setRoleSessionName(new AsciiRandomStringService().random());
112-
}
113-
}
114-
if(StringUtils.isNotBlank(preferences.getProperty("s3.assumerole.tag"))) {
115-
request.setTags(Collections.singletonList(new Tag()
116-
.withKey(preferences.getProperty("s3.assumerole.tag"))
117-
.withValue(preferences.getProperty(S3AssumeRoleProtocol.OAUTH_TOKENEXCHANGE_VAULT))));
118-
}
119-
try {
120-
log.debug("Use request {}", request);
121-
final AssumeRoleResult result = service.assumeRole(request);
122-
log.debug("Received assume role result {} for host {}", result, bookmark);
123-
return new TemporaryAccessTokens(result.getCredentials().getAccessKeyId(),
124-
result.getCredentials().getSecretAccessKey(),
125-
result.getCredentials().getSessionToken(),
126-
result.getCredentials().getExpiration().getTime());
127-
}
128-
catch(AWSSecurityTokenServiceException e) {
129-
throw new STSExceptionMappingService().map(e);
130-
}
47+
@Override
48+
public TemporaryAccessTokens assumeRoleWithWebIdentity(final Credentials credentials) throws BackgroundException {
49+
final TemporaryAccessTokens tokens = super.assumeRoleWithWebIdentity(credentials
50+
.withProperty(Profile.STS_ROLE_ARN_PROPERTY_KEY, bookmark.getProperty(Profile.STS_ROLE_ARN_PROPERTY_KEY)));
51+
final PreferencesReader settings = new ProxyPreferencesReader(bookmark, credentials);
52+
if(StringUtils.isNotBlank(settings.getProperty(S3AssumeRoleProtocol.S3_ASSUMEROLE_ROLEARN_2))) {
53+
log.debug("Assume role with temporary credentials {}", tokens);
54+
// Assume role with previously obtained temporary access token
55+
return super.assumeRole(credentials.withTokens(tokens)
56+
.withProperty(Profile.STS_ROLE_ARN_PROPERTY_KEY, settings.getProperty(S3AssumeRoleProtocol.S3_ASSUMEROLE_ROLEARN_2))
57+
.withProperty(Profile.STS_TAGS_PROPERTY_KEY, String.format("%s=%s", "VaultRequested", settings.getProperty(S3AssumeRoleProtocol.OAUTH_TOKENEXCHANGE_VAULT)))
58+
);
59+
}
60+
return tokens;
13161
}
13262
}

0 commit comments

Comments
 (0)