Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable Session Token Duration #48

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import com.cloudbees.plugins.credentials.CredentialsDescriptor;
import com.cloudbees.plugins.credentials.CredentialsScope;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.ProxyConfiguration;
import hudson.Util;
Expand All @@ -53,6 +54,7 @@
import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;

import java.net.HttpURLConnection;
Expand All @@ -66,16 +68,17 @@ public class AWSCredentialsImpl extends BaseAmazonWebServicesCredentials impleme
private static final Logger LOGGER = Logger.getLogger(BaseAmazonWebServicesCredentials.class.getName());

public static final int STS_CREDENTIALS_DURATION_SECONDS = 3600;

private final String accessKey;

private final Secret secretKey;

private final String iamRoleArn;
private final String iamMfaSerialNumber;

/**
* Old data bound constructor. It is maintained to keep binary compatibility with clients that were using it directly.
*/
private volatile Integer stsTokenDuration;

// Old data bound constructor. It is maintained to keep binary compatibility with clients that were using it directly.
public AWSCredentialsImpl(@CheckForNull CredentialsScope scope, @CheckForNull String id,
@CheckForNull String accessKey, @CheckForNull String secretKey, @CheckForNull String description) {
this(scope, id, accessKey, secretKey, description, null, null);
Expand Down Expand Up @@ -108,6 +111,16 @@ public String getIamMfaSerialNumber() {
return iamMfaSerialNumber;
}

@NonNull
public Integer getStsTokenDuration() {
return stsTokenDuration == null ? DescriptorImpl.DEFAULT_STS_TOKEN_DURATION : stsTokenDuration;
}

@DataBoundSetter
public void setStsTokenDuration(Integer stsTokenDuration) {
this.stsTokenDuration = stsTokenDuration == null || stsTokenDuration.equals(DescriptorImpl.DEFAULT_STS_TOKEN_DURATION) ? null : stsTokenDuration;
}

public boolean requiresToken() {
return !StringUtils.isBlank(iamMfaSerialNumber);
}
Expand Down Expand Up @@ -144,7 +157,8 @@ public AWSCredentials getCredentials() {
.build();
}

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

AssumeRoleResult assumeResult = client.assumeRole(assumeRequest);

Expand All @@ -160,7 +174,8 @@ public AWSCredentials getCredentials(String mfaToken) {

AssumeRoleRequest assumeRequest = createAssumeRoleRequest(iamRoleArn)
.withSerialNumber(iamMfaSerialNumber)
.withTokenCode(mfaToken);
.withTokenCode(mfaToken)
.withDurationSeconds(this.getStsTokenDuration());

AssumeRoleResult assumeResult = new AWSSecurityTokenServiceClient(initialCredentials).assumeRole(assumeRequest);

Expand All @@ -184,7 +199,6 @@ public String getDisplayName() {
private static AssumeRoleRequest createAssumeRoleRequest(String iamRoleArn) {
return new AssumeRoleRequest()
.withRoleArn(iamRoleArn)
.withDurationSeconds(STS_CREDENTIALS_DURATION_SECONDS)
.withRoleSessionName(Jenkins.getActiveInstance().getDisplayName());
}

Expand All @@ -196,10 +210,13 @@ public String getDisplayName() {
return Messages.AWSCredentialsImpl_DisplayName();
}

public static final Integer DEFAULT_STS_TOKEN_DURATION = STS_CREDENTIALS_DURATION_SECONDS;

public FormValidation doCheckSecretKey(@QueryParameter("accessKey") final String accessKey,
@QueryParameter("iamRoleArn") final String iamRoleArn,
@QueryParameter("iamMfaSerialNumber") final String iamMfaSerialNumber,
@QueryParameter("iamMfaToken") final String iamMfaToken,
@QueryParameter("stsTokenDuration") final Integer stsTokenDuration,
@QueryParameter final String secretKey) {
if (StringUtils.isBlank(accessKey) && StringUtils.isBlank(secretKey)) {
return FormValidation.ok();
Expand All @@ -225,7 +242,8 @@ public FormValidation doCheckSecretKey(@QueryParameter("accessKey") final String
// If iamRoleArn is specified, swap out the credentials.
if (!StringUtils.isBlank(iamRoleArn)) {

AssumeRoleRequest assumeRequest = createAssumeRoleRequest(iamRoleArn);
AssumeRoleRequest assumeRequest = createAssumeRoleRequest(iamRoleArn)
.withDurationSeconds(stsTokenDuration);

if(!StringUtils.isBlank(iamMfaSerialNumber)) {
if(StringUtils.isBlank(iamMfaToken)) {
Expand All @@ -245,9 +263,8 @@ public FormValidation doCheckSecretKey(@QueryParameter("accessKey") final String
assumeResult.getCredentials().getSessionToken());
} catch(AmazonServiceException e) {
LOGGER.log(Level.WARNING, "Unable to assume role [" + iamRoleArn + "] with request [" + assumeRequest + "]", e);
return FormValidation.error(Messages.AWSCredentialsImpl_NotAbleToAssumeRole());
return FormValidation.error(Messages.AWSCredentialsImpl_NotAbleToAssumeRole() + " Check the Jenkins log for more details");
}

}

AmazonEC2 ec2 = new AmazonEC2Client(awsCredentials,clientConfiguration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public class AmazonWebServicesCredentialsBinding extends MultiBinding<AmazonWebS
*
* @param accessKeyVariable if {@code null}, {@value DEFAULT_ACCESS_KEY_ID_VARIABLE_NAME} will be used.
* @param secretKeyVariable if {@code null}, {@value DEFAULT_SECRET_ACCESS_KEY_VARIABLE_NAME} will be used.
* @param credentialsId
* @param credentialsId identifier which should be referenced when accessing the credentials from a job/pipeline.
*/
@DataBoundConstructor
public AmazonWebServicesCredentialsBinding(@Nullable String accessKeyVariable, @Nullable String secretKeyVariable, String credentialsId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
<f:entry title="${%MFA Token}" field="iamMfaToken">
<f:textbox/>
</f:entry>
<f:entry title="${%STS Token Duration (sec)}" field="stsTokenDuration">
<f:textbox default="${descriptor.DEFAULT_STS_TOKEN_DURATION}"/>
</f:entry>
</f:advanced>
</f:section>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div>
The duration, in seconds, for how long the obtained session token will be valid for.
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
AWSCredentialsImpl_DisplayName=AWS Credentials
AWSCredentialsImpl.SpecifyAccessKeyId=Please specify the Access Key ID
AWSCredentialsImpl.SpecifySecretAccessKey=Please specify the Secret Access Key
AWSCredentialsImpl.NotAbleToAssumeRole=There was an error assuming the specified IAM role, a MFA may be required by your organization
AWSCredentialsImpl.NotAbleToAssumeRole=There was an error assuming the specified IAM role, a MFA may be required by your organization.
AWSCredentialsImpl.SpecifyMFAToken=If the MFA Serial Number/ARN is specified, then a one time token is necessary to validate these credentials
AWSCredentialsImpl.CredentialsValidWithAccessToNZones=These credentials are valid and have access to {0} availability zones
AWSCredentialsImpl.CredentialsValidWithoutAccessToAwsServiceInZone=These credentials are valid but do not have access to the "{0}" service in the region "{1}". This message is not a problem if you need to access to other services or to other regions. Message: "{2}"
Expand Down