Skip to content

Commit

Permalink
feat(codebuild): Add support for static credentials (#643)
Browse files Browse the repository at this point in the history
* feat(codebuild): Add support for static credentials

After this change, user should be able to specify static credential for codebuild
and all codebuild accounts will use this credential to assume role. If assume role
is not present, the static credential itself will be used to call CodeBuild APIs.

* address comments
  • Loading branch information
Kaixiang-AWS authored Feb 28, 2020
1 parent 9ec47d3 commit eced2d2
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package com.netflix.spinnaker.igor.codebuild;

import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.services.codebuild.AWSCodeBuildClient;
import com.amazonaws.services.codebuild.AWSCodeBuildClientBuilder;
import com.amazonaws.services.codebuild.model.BatchGetBuildsRequest;
Expand All @@ -27,28 +27,17 @@
import com.amazonaws.services.codebuild.model.ProjectSortByType;
import com.amazonaws.services.codebuild.model.StartBuildRequest;
import com.amazonaws.services.codebuild.model.StopBuildRequest;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient;
import com.netflix.spinnaker.kork.artifacts.model.Artifact;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;

/** Generates authenticated requests to AWS CodeBuild API for a single configured account */
@RequiredArgsConstructor
public class AwsCodeBuildAccount {
private final AWSCodeBuildClient client;

@Autowired AWSSecurityTokenServiceClient stsClient;

public AwsCodeBuildAccount(String accountId, String assumeRole, String region) {
STSAssumeRoleSessionCredentialsProvider credentialsProvider =
new STSAssumeRoleSessionCredentialsProvider.Builder(
getRoleArn(accountId, assumeRole), "spinnaker-session")
.withStsClient(stsClient)
.build();

public AwsCodeBuildAccount(AWSCredentialsProvider credentialsProvider, String region) {
// TODO: Add client-side rate limiting to avoid getting throttled if necessary
this.client =
(AWSCodeBuildClient)
Expand Down Expand Up @@ -95,21 +84,6 @@ public Build stopBuild(String buildId) {
return client.stopBuild(new StopBuildRequest().withId(buildId)).getBuild();
}

private String getRoleArn(String accountId, String assumeRole) {
String assumeRoleValue = Objects.requireNonNull(assumeRole, "assumeRole");
if (!assumeRoleValue.startsWith("arn:")) {
/**
* GovCloud and China regions need to have the full arn passed because of differing formats
* Govcloud: arn:aws-us-gov:iam China: arn:aws-cn:iam
*/
assumeRoleValue =
String.format(
"arn:aws:iam::%s:%s",
Objects.requireNonNull(accountId, "accountId"), assumeRoleValue);
}
return assumeRoleValue;
}

private List<Artifact> extractArtifactsFromBuild(Build build) {
ArrayList<Artifact> artifactsList = new ArrayList<>();
BuildArtifacts primaryArtifacts = build.getArtifacts();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,91 @@

package com.netflix.spinnaker.igor.config;

import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
import com.netflix.spinnaker.igor.codebuild.AwsCodeBuildAccount;
import com.netflix.spinnaker.igor.codebuild.AwsCodeBuildAccountRepository;
import java.util.Objects;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

@Configuration
@ConditionalOnProperty("codebuild.enabled")
@EnableConfigurationProperties({AwsCodeBuildProperties.class})
public class AwsCodeBuildConfig {
@Bean
@Bean("awsCodeBuildAccountRepository")
@DependsOn({"awsSecurityTokenServiceClient"})
AwsCodeBuildAccountRepository awsCodeBuildAccountRepository(
AwsCodeBuildProperties awsCodeBuildProperties) {
AwsCodeBuildProperties awsCodeBuildProperties,
AWSSecurityTokenServiceClient awsSecurityTokenServiceClient,
AWSCredentialsProvider awsCredentialsProvider) {
AwsCodeBuildAccountRepository accounts = new AwsCodeBuildAccountRepository();
awsCodeBuildProperties
.getAccounts()
.forEach(
a -> {
AwsCodeBuildAccount account =
new AwsCodeBuildAccount(a.getAccountId(), a.getAssumeRole(), a.getRegion());
new AwsCodeBuildAccount(awsCredentialsProvider, a.getRegion());
if (a.getAccountId() != null && a.getAssumeRole() != null) {
STSAssumeRoleSessionCredentialsProvider stsAssumeRoleSessionCredentialsProvider =
new STSAssumeRoleSessionCredentialsProvider.Builder(
getRoleArn(a.getAccountId(), a.getAssumeRole()), "spinnaker-session")
.withStsClient(awsSecurityTokenServiceClient)
.build();
account =
new AwsCodeBuildAccount(stsAssumeRoleSessionCredentialsProvider, a.getRegion());
}
accounts.addAccount(a.getName(), account);
});
return accounts;
}

@Bean
AWSSecurityTokenServiceClient awsSecurityTokenServiceClient() {
@Bean("awsSecurityTokenServiceClient")
@DependsOn({"awsCredentialsProvider"})
AWSSecurityTokenServiceClient awsSecurityTokenServiceClient(
AWSCredentialsProvider awsCredentialsProvider) {
return (AWSSecurityTokenServiceClient)
AWSSecurityTokenServiceClientBuilder.standard()
.withCredentials(DefaultAWSCredentialsProviderChain.getInstance())
.withCredentials(awsCredentialsProvider)
.build();
}

@Bean("awsCredentialsProvider")
AWSCredentialsProvider awsCredentialsProvider(AwsCodeBuildProperties awsCodeBuildProperties) {
AWSCredentialsProvider credentialsProvider = DefaultAWSCredentialsProviderChain.getInstance();
if (awsCodeBuildProperties.getAccessKeyId() != null
&& !awsCodeBuildProperties.getAccessKeyId().isEmpty()
&& awsCodeBuildProperties.getSecretAccessKey() != null
&& !awsCodeBuildProperties.getSecretAccessKey().isEmpty()) {
credentialsProvider =
new AWSStaticCredentialsProvider(
new BasicAWSCredentials(
awsCodeBuildProperties.getAccessKeyId(),
awsCodeBuildProperties.getSecretAccessKey()));
}
return credentialsProvider;
}

private String getRoleArn(String accountId, String assumeRole) {
String assumeRoleValue = Objects.requireNonNull(assumeRole, "assumeRole");
if (!assumeRoleValue.startsWith("arn:")) {
/**
* GovCloud and China regions need to have the full arn passed because of differing formats
* Govcloud: arn:aws-us-gov:iam China: arn:aws-cn:iam
*/
assumeRoleValue =
String.format(
"arn:aws:iam::%s:%s",
Objects.requireNonNull(accountId, "accountId"), assumeRoleValue);
}
return assumeRoleValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
@ConfigurationProperties(prefix = "codebuild")
public class AwsCodeBuildProperties {
private List<Account> accounts;
private String accessKeyId;
private String secretAccessKey;

@Data
public static class Account {
Expand Down

0 comments on commit eced2d2

Please sign in to comment.