Skip to content

Commit

Permalink
Merge pull request #81 from rittneje/choose-role-at-runtime
Browse files Browse the repository at this point in the history
Allow specifying AWS role at runtime
  • Loading branch information
escoem committed Aug 9, 2021
2 parents 7a6af1e + bda0dac commit c748e81
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.SdkClientException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.BasicSessionCredentials;
Expand Down Expand Up @@ -131,35 +131,16 @@ public AWSCredentials getCredentials() {
if (StringUtils.isBlank(iamRoleArn)) {
return initialCredentials;
} else {
// Check for available region from the SDK, otherwise specify default
String clientRegion = null;
DefaultAwsRegionProviderChain sdkRegionLookup = new DefaultAwsRegionProviderChain();
try {
clientRegion = sdkRegionLookup.getRegion();
} catch (RuntimeException e) {
LOGGER.log(Level.WARNING, "Could not find default region using SDK lookup.", e);
}
if (clientRegion == null) {
clientRegion = Regions.DEFAULT_REGION.getName();
}

ClientConfiguration clientConfiguration = getClientConfiguration();

AWSSecurityTokenService client;
AWSCredentialsProvider baseProvider;
// Handle the case of delegation to instance profile
if (StringUtils.isBlank(accessKey) && StringUtils.isBlank(secretKey.getPlainText())) {
client = AWSSecurityTokenServiceClientBuilder.standard()
.withRegion(clientRegion)
.withClientConfiguration(clientConfiguration)
.build();
baseProvider = null;
} else {
client = AWSSecurityTokenServiceClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(initialCredentials))
.withRegion(clientRegion)
.withClientConfiguration(clientConfiguration)
.build();
baseProvider = new AWSStaticCredentialsProvider(initialCredentials);
}

AWSSecurityTokenService client = buildStsClient(baseProvider);

AssumeRoleRequest assumeRequest = createAssumeRoleRequest(iamRoleArn)
.withDurationSeconds(this.getStsTokenDuration());

Expand Down Expand Up @@ -200,6 +181,30 @@ public String getDisplayName() {
return accessKey + ":" + iamRoleArn;
}

/*package*/ static AWSSecurityTokenService buildStsClient(AWSCredentialsProvider provider) {
// Check for available region from the SDK, otherwise specify default
String clientRegion = null;
DefaultAwsRegionProviderChain sdkRegionLookup = new DefaultAwsRegionProviderChain();
try {
clientRegion = sdkRegionLookup.getRegion();
} catch(RuntimeException e) {
LOGGER.log(Level.WARNING, "Could not find default region using SDK lookup.", e);
}
if (clientRegion == null) {
clientRegion = Regions.DEFAULT_REGION.getName();
}

AWSSecurityTokenServiceClientBuilder builder = AWSSecurityTokenServiceClientBuilder.standard()
.withRegion(clientRegion)
.withClientConfiguration(getClientConfiguration());

if (provider != null) {
builder = builder.withCredentials(provider);
}

return builder.build();
}

private static AssumeRoleRequest createAssumeRoleRequest(String iamRoleArn) {
return new AssumeRoleRequest()
.withRoleArn(iamRoleArn)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSSessionCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSSessionCredentialsProvider;
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import hudson.Extension;
Expand All @@ -39,6 +43,7 @@
import org.jenkinsci.plugins.credentialsbinding.MultiBinding;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

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

private String roleArn;
private String roleSessionName;
private int roleSessionDurationSeconds;

/**
*
* @param accessKeyVariable if {@code null}, {@value DEFAULT_ACCESS_KEY_ID_VARIABLE_NAME} will be used.
Expand All @@ -85,14 +94,35 @@ public String getSecretKeyVariable() {
return secretKeyVariable;
}

@DataBoundSetter
public void setRoleArn(String roleArn) {
this.roleArn = roleArn;
}

@DataBoundSetter
public void setRoleSessionName(String roleSessionName) {
this.roleSessionName = roleSessionName;
}

@DataBoundSetter
public void setRoleSessionDurationSeconds(int roleSessionDurationSeconds) {
this.roleSessionDurationSeconds = roleSessionDurationSeconds;
}

@Override
protected Class<AmazonWebServicesCredentials> type() {
return AmazonWebServicesCredentials.class;
}

@Override
public MultiEnvironment bind(@Nonnull Run<?, ?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException {
AWSCredentials credentials = getCredentials(build).getCredentials();
AWSCredentialsProvider provider = getCredentials(build);
if (!StringUtils.isEmpty(this.roleArn)) {
provider = this.assumeRoleProvider(provider);
}

AWSCredentials credentials = provider.getCredentials();

Map<String,String> m = new HashMap<String,String>();
m.put(accessKeyVariable, credentials.getAWSAccessKeyId());
m.put(secretKeyVariable, credentials.getAWSSecretKey());
Expand All @@ -104,9 +134,26 @@ public MultiEnvironment bind(@Nonnull Run<?, ?> build, FilePath workspace, Launc
return new MultiEnvironment(m);
}

private AWSSessionCredentialsProvider assumeRoleProvider(AWSCredentialsProvider baseProvider) {
AWSSecurityTokenService stsClient = AWSCredentialsImpl.buildStsClient(baseProvider);

String roleSessionName = StringUtils.defaultIfBlank(this.roleSessionName, "Jenkins");

STSAssumeRoleSessionCredentialsProvider.Builder assumeRoleProviderBuilder =
new STSAssumeRoleSessionCredentialsProvider.Builder(this.roleArn, roleSessionName)
.withStsClient(stsClient);

if (this.roleSessionDurationSeconds > 0) {
assumeRoleProviderBuilder = assumeRoleProviderBuilder
.withRoleSessionDurationSeconds(this.roleSessionDurationSeconds);
}

return assumeRoleProviderBuilder.build();
}

@Override
public Set<String> variables() {
return new HashSet<String>(Arrays.asList(accessKeyVariable, secretKeyVariable));
return new HashSet<String>(Arrays.asList(accessKeyVariable, secretKeyVariable, SESSION_TOKEN_VARIABLE_NAME));
}

@Symbol("aws")
Expand Down

0 comments on commit c748e81

Please sign in to comment.