Skip to content

Commit c748e81

Browse files
authored
Merge pull request #81 from rittneje/choose-role-at-runtime
Allow specifying AWS role at runtime
2 parents 7a6af1e + bda0dac commit c748e81

File tree

2 files changed

+79
-27
lines changed

2 files changed

+79
-27
lines changed

src/main/java/com/cloudbees/jenkins/plugins/awscredentials/AWSCredentialsImpl.java

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828
import com.amazonaws.AmazonClientException;
2929
import com.amazonaws.AmazonServiceException;
3030
import com.amazonaws.ClientConfiguration;
31-
import com.amazonaws.SdkClientException;
3231
import com.amazonaws.auth.AWSCredentials;
32+
import com.amazonaws.auth.AWSCredentialsProvider;
3333
import com.amazonaws.auth.AWSStaticCredentialsProvider;
3434
import com.amazonaws.auth.BasicAWSCredentials;
3535
import com.amazonaws.auth.BasicSessionCredentials;
@@ -131,35 +131,16 @@ public AWSCredentials getCredentials() {
131131
if (StringUtils.isBlank(iamRoleArn)) {
132132
return initialCredentials;
133133
} else {
134-
// Check for available region from the SDK, otherwise specify default
135-
String clientRegion = null;
136-
DefaultAwsRegionProviderChain sdkRegionLookup = new DefaultAwsRegionProviderChain();
137-
try {
138-
clientRegion = sdkRegionLookup.getRegion();
139-
} catch (RuntimeException e) {
140-
LOGGER.log(Level.WARNING, "Could not find default region using SDK lookup.", e);
141-
}
142-
if (clientRegion == null) {
143-
clientRegion = Regions.DEFAULT_REGION.getName();
144-
}
145-
146-
ClientConfiguration clientConfiguration = getClientConfiguration();
147-
148-
AWSSecurityTokenService client;
134+
AWSCredentialsProvider baseProvider;
149135
// Handle the case of delegation to instance profile
150136
if (StringUtils.isBlank(accessKey) && StringUtils.isBlank(secretKey.getPlainText())) {
151-
client = AWSSecurityTokenServiceClientBuilder.standard()
152-
.withRegion(clientRegion)
153-
.withClientConfiguration(clientConfiguration)
154-
.build();
137+
baseProvider = null;
155138
} else {
156-
client = AWSSecurityTokenServiceClientBuilder.standard()
157-
.withCredentials(new AWSStaticCredentialsProvider(initialCredentials))
158-
.withRegion(clientRegion)
159-
.withClientConfiguration(clientConfiguration)
160-
.build();
139+
baseProvider = new AWSStaticCredentialsProvider(initialCredentials);
161140
}
162141

142+
AWSSecurityTokenService client = buildStsClient(baseProvider);
143+
163144
AssumeRoleRequest assumeRequest = createAssumeRoleRequest(iamRoleArn)
164145
.withDurationSeconds(this.getStsTokenDuration());
165146

@@ -200,6 +181,30 @@ public String getDisplayName() {
200181
return accessKey + ":" + iamRoleArn;
201182
}
202183

184+
/*package*/ static AWSSecurityTokenService buildStsClient(AWSCredentialsProvider provider) {
185+
// Check for available region from the SDK, otherwise specify default
186+
String clientRegion = null;
187+
DefaultAwsRegionProviderChain sdkRegionLookup = new DefaultAwsRegionProviderChain();
188+
try {
189+
clientRegion = sdkRegionLookup.getRegion();
190+
} catch(RuntimeException e) {
191+
LOGGER.log(Level.WARNING, "Could not find default region using SDK lookup.", e);
192+
}
193+
if (clientRegion == null) {
194+
clientRegion = Regions.DEFAULT_REGION.getName();
195+
}
196+
197+
AWSSecurityTokenServiceClientBuilder builder = AWSSecurityTokenServiceClientBuilder.standard()
198+
.withRegion(clientRegion)
199+
.withClientConfiguration(getClientConfiguration());
200+
201+
if (provider != null) {
202+
builder = builder.withCredentials(provider);
203+
}
204+
205+
return builder.build();
206+
}
207+
203208
private static AssumeRoleRequest createAssumeRoleRequest(String iamRoleArn) {
204209
return new AssumeRoleRequest()
205210
.withRoleArn(iamRoleArn)

src/main/java/com/cloudbees/jenkins/plugins/awscredentials/AmazonWebServicesCredentialsBinding.java

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727

2828
import com.amazonaws.auth.AWSCredentials;
2929
import com.amazonaws.auth.AWSSessionCredentials;
30+
import com.amazonaws.auth.AWSCredentialsProvider;
31+
import com.amazonaws.auth.AWSSessionCredentialsProvider;
32+
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
33+
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
3034
import edu.umd.cs.findbugs.annotations.NonNull;
3135
import edu.umd.cs.findbugs.annotations.Nullable;
3236
import hudson.Extension;
@@ -39,6 +43,7 @@
3943
import org.jenkinsci.plugins.credentialsbinding.MultiBinding;
4044
import org.jenkinsci.Symbol;
4145
import org.kohsuke.stapler.DataBoundConstructor;
46+
import org.kohsuke.stapler.DataBoundSetter;
4247

4348
import javax.annotation.Nonnull;
4449
import java.io.IOException;
@@ -62,6 +67,10 @@ public class AmazonWebServicesCredentialsBinding extends MultiBinding<AmazonWebS
6267
@NonNull
6368
private final String secretKeyVariable;
6469

70+
private String roleArn;
71+
private String roleSessionName;
72+
private int roleSessionDurationSeconds;
73+
6574
/**
6675
*
6776
* @param accessKeyVariable if {@code null}, {@value DEFAULT_ACCESS_KEY_ID_VARIABLE_NAME} will be used.
@@ -85,14 +94,35 @@ public String getSecretKeyVariable() {
8594
return secretKeyVariable;
8695
}
8796

97+
@DataBoundSetter
98+
public void setRoleArn(String roleArn) {
99+
this.roleArn = roleArn;
100+
}
101+
102+
@DataBoundSetter
103+
public void setRoleSessionName(String roleSessionName) {
104+
this.roleSessionName = roleSessionName;
105+
}
106+
107+
@DataBoundSetter
108+
public void setRoleSessionDurationSeconds(int roleSessionDurationSeconds) {
109+
this.roleSessionDurationSeconds = roleSessionDurationSeconds;
110+
}
111+
88112
@Override
89113
protected Class<AmazonWebServicesCredentials> type() {
90114
return AmazonWebServicesCredentials.class;
91115
}
92116

93117
@Override
94118
public MultiEnvironment bind(@Nonnull Run<?, ?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException {
95-
AWSCredentials credentials = getCredentials(build).getCredentials();
119+
AWSCredentialsProvider provider = getCredentials(build);
120+
if (!StringUtils.isEmpty(this.roleArn)) {
121+
provider = this.assumeRoleProvider(provider);
122+
}
123+
124+
AWSCredentials credentials = provider.getCredentials();
125+
96126
Map<String,String> m = new HashMap<String,String>();
97127
m.put(accessKeyVariable, credentials.getAWSAccessKeyId());
98128
m.put(secretKeyVariable, credentials.getAWSSecretKey());
@@ -104,9 +134,26 @@ public MultiEnvironment bind(@Nonnull Run<?, ?> build, FilePath workspace, Launc
104134
return new MultiEnvironment(m);
105135
}
106136

137+
private AWSSessionCredentialsProvider assumeRoleProvider(AWSCredentialsProvider baseProvider) {
138+
AWSSecurityTokenService stsClient = AWSCredentialsImpl.buildStsClient(baseProvider);
139+
140+
String roleSessionName = StringUtils.defaultIfBlank(this.roleSessionName, "Jenkins");
141+
142+
STSAssumeRoleSessionCredentialsProvider.Builder assumeRoleProviderBuilder =
143+
new STSAssumeRoleSessionCredentialsProvider.Builder(this.roleArn, roleSessionName)
144+
.withStsClient(stsClient);
145+
146+
if (this.roleSessionDurationSeconds > 0) {
147+
assumeRoleProviderBuilder = assumeRoleProviderBuilder
148+
.withRoleSessionDurationSeconds(this.roleSessionDurationSeconds);
149+
}
150+
151+
return assumeRoleProviderBuilder.build();
152+
}
153+
107154
@Override
108155
public Set<String> variables() {
109-
return new HashSet<String>(Arrays.asList(accessKeyVariable, secretKeyVariable));
156+
return new HashSet<String>(Arrays.asList(accessKeyVariable, secretKeyVariable, SESSION_TOKEN_VARIABLE_NAME));
110157
}
111158

112159
@Symbol("aws")

0 commit comments

Comments
 (0)