Skip to content

Commit

Permalink
GCP Dynamic provider credentials support (#821)
Browse files Browse the repository at this point in the history
* GCP Dynamic provider credentials support
  • Loading branch information
alfespa17 committed Apr 24, 2024
1 parent 19103cd commit 8eaf5d5
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,11 @@ private HashMap<String, String> loadOtherEnvironmentVariables(Job job, Flow flow
workspaceEnvVariables = dynamicCredentialsService.generateDynamicCredentialsAzure(job, workspaceEnvVariables);
}

if (workspaceEnvVariables.containsKey("ENABLE_DYNAMIC_CREDENTIALS_GCP")) {
if (workspaceEnvVariables.containsKey("ENABLE_DYNAMIC_CREDENTIALS_AWS")) {
workspaceEnvVariables = dynamicCredentialsService.generateDynamicCredentialsAws(job, workspaceEnvVariables);
}

if (workspaceEnvVariables.containsKey("ENABLE_DYNAMIC_CREDENTIALS_AWS")) {
if (workspaceEnvVariables.containsKey("ENABLE_DYNAMIC_CREDENTIALS_GCP")) {
workspaceEnvVariables = dynamicCredentialsService.generateDynamicCredentialsGcp(job, workspaceEnvVariables);
}
return workspaceEnvVariables;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,34 +43,48 @@ public class DynamicCredentialsService {

@Transactional
public HashMap<String, String> generateDynamicCredentialsAzure(Job job, HashMap<String, String> workspaceEnvVariables) {
Instant now = Instant.now();
String jwtToken = generateJwt(
job.getOrganization().getName(),
job.getWorkspace().getName(),
workspaceEnvVariables.get("WORKLOAD_IDENTITY_AUDIENCE_AZURE"),
job.getOrganization().getId().toString(),
job.getWorkspace().getId().toString(),
job.getId()
);

log.info("ARM_OIDC_TOKEN: {}", jwtToken);
workspaceEnvVariables.put("ARM_OIDC_TOKEN", jwtToken);

return workspaceEnvVariables;
}

private String generateJwt(String organizationName, String workspaceName, String tokenAudience, String organizationId, String workspaceId, int jobId) {
String jwtToken = "";
if (privateKeyPath != null && !privateKeyPath.isEmpty())
if (privateKeyPath != null && !privateKeyPath.isEmpty()) {
try {
Instant now = Instant.now();
jwtToken = Jwts.builder()
.setSubject(String.format("organization:%s:workspace:%s", job.getOrganization().getName(), job.getWorkspace().getName()))
.setAudience(workspaceEnvVariables.get("WORKLOAD_IDENTITY_AUDIENCE_AZURE"))
.setSubject(String.format("organization:%s:workspace:%s", organizationName, workspaceName))
.setAudience(tokenAudience)
.setId(UUID.randomUUID().toString())
.setHeaderParam("kid", kid)
.claim("terrakube_workspace_id", job.getWorkspace().getId())
.claim("terrakube_organization_id", job.getOrganization().getId())
.claim("terrakube_job_id", String.valueOf(job.getId()))
.claim("terrakube_workspace_id", organizationId)
.claim("terrakube_organization_id", workspaceId)
.claim("terrakube_job_id", String.valueOf(jobId))
.setIssuedAt(Date.from(now))
.setIssuer(String.format("https://%s", hostname))
.setExpiration(Date.from(now.plus(dynamicCredentialTtl, ChronoUnit.MINUTES)))
.signWith(getPrivateKey())
.compact();

log.info("ARM_OIDC_TOKEN: {}", jwtToken);
workspaceEnvVariables.put("ARM_OIDC_TOKEN", jwtToken);
} catch (Exception e) {
log.error(e.getMessage());
}
else {
log.error("DynamicCredentialPrivateKeyPath not set, to generate Azure Dynamic Credentials the value is need it");
} else {
log.error("DynamicCredentialPrivateKeyPath not set, to generate Dynamic Credentials the value is need it");
}

return workspaceEnvVariables;
return jwtToken;
}

@Transactional
Expand All @@ -81,7 +95,55 @@ public HashMap<String, String> generateDynamicCredentialsAws(Job job, HashMap<St

@Transactional
public HashMap<String, String> generateDynamicCredentialsGcp(Job job, HashMap<String, String> workspaceEnvVariables) {
log.warn("GCP Dynamic Credentials not implemented yet");
String jwtToken = generateJwt(
job.getOrganization().getName(),
job.getWorkspace().getName(),
workspaceEnvVariables.get("WORKLOAD_IDENTITY_AUDIENCE_GCP"),
job.getOrganization().getId().toString(),
job.getWorkspace().getId().toString(),
job.getId()
);

String googleCredentialsFile = "{\n" +
" \"access_token\": \"%s\"\n" +
"} ";

googleCredentialsFile = String.format(googleCredentialsFile, jwtToken);

String googleCredentialConfigFile = "{\n" +
" \"type\": \"external_account\",\n" +
" \"audience\": \"%s\",\n" +
" \"subject_token_type\": \"urn:ietf:params:oauth:token-type:jwt\",\n" +
" \"token_url\": \"https://sts.googleapis.com/v1/token\",\n" +
" \"service_account_impersonation_url\": \"https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken\",\n" +
" \"credential_source\": {\n" +
" \"file\": \"%s/terrakube_dynamic_credentials.json\",\n" +
" \"format\": {\n" +
" \"type\": \"json\",\n" +
" \"subject_token_field_name\": \"access_token\"\n" +
" }\n" +
" }\n" +
" }";

String executorDirectory = String.format(
"%s/.terraform-spring-boot/executor/%s/%s",
FileUtils.getUserDirectoryPath(),
job.getOrganization().getId().toString(),
job.getWorkspace().getId().toString()
);

String audience = workspaceEnvVariables.get("WORKLOAD_IDENTITY_AUDIENCE_GCP");
String serviceAccountEmail = workspaceEnvVariables.get("WORKLOAD_IDENTITY_SERVICE_ACCOUNT_EMAIL");

googleCredentialConfigFile = String.format(googleCredentialConfigFile, audience, serviceAccountEmail, executorDirectory);

log.info("TERRAKUBE_GCP_CREDENTIALS_FILE: {}", googleCredentialsFile);
log.info("TERRAKUBE_GCP_CREDENTIALS_CONFIG_FILE: {}", googleCredentialConfigFile);

workspaceEnvVariables.put("TERRAKUBE_GCP_CREDENTIALS_FILE", googleCredentialsFile);
workspaceEnvVariables.put("TERRAKUBE_GCP_CREDENTIALS_CONFIG_FILE", googleCredentialConfigFile);
workspaceEnvVariables.put("GOOGLE_APPLICATION_CREDENTIALS", executorDirectory + "/terrakube_config_dynamic_credentials.json");

return workspaceEnvVariables;
}

Expand Down
1 change: 1 addition & 0 deletions api/src/main/resources/db/changelog/changelog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@
<include file="/db/changelog/local/changelog-2.20.0-executor-agent.xml"/>
<include file="/db/changelog/local/changelog-2.20.0-registry-monorepo.xml"/>
<include file="/db/changelog/local/changelog-2.21.0-auto-apply.xml"/>
<include file="/db/changelog/local/changelog-2.21.0-gcp-dynamic.xml"/>
</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
<changeSet id="40" author="alfespa17@gmail.com">
<modifyDataType
columnName="variable_key"
newDataType="varchar(64)"
tableName="variable"/>
</changeSet>
</databaseChangeLog>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.terrakube.executor.service.mode.TerraformJob;

import java.io.File;
import java.io.IOException;

public interface TerraformExecutor {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private void setupConsumerGroups(String jobId) {
}
}

private File getTerraformWorkingDir(TerraformJob terraformJob, File workingDirectory) throws IOException {
public File getTerraformWorkingDir(TerraformJob terraformJob, File workingDirectory) throws IOException {
File terraformWorkingDir = workingDirectory;
try {
if (!terraformJob.getBranch().equals("remote-content") || (terraformJob.getFolder() != null && !terraformJob.getFolder().equals("/"))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.terrakube.executor.service.mode.TerraformJob;
import org.terrakube.executor.service.terraform.TerraformExecutor;
import org.terrakube.executor.service.workspace.security.WorkspaceSecurity;

import java.io.*;
Expand All @@ -42,10 +43,12 @@ public class SetupWorkspaceImpl implements SetupWorkspace {

WorkspaceSecurity workspaceSecurity;
boolean enableRegistrySecurity;
TerraformExecutor terraformExecutor;

public SetupWorkspaceImpl(WorkspaceSecurity workspaceSecurity, @Value("${org.terrakube.client.enableSecurity}") boolean enableRegistrySecurity) {
public SetupWorkspaceImpl(WorkspaceSecurity workspaceSecurity, @Value("${org.terrakube.client.enableSecurity}") boolean enableRegistrySecurity, TerraformExecutor terraformExecutor) {
this.workspaceSecurity = workspaceSecurity;
this.enableRegistrySecurity = enableRegistrySecurity;
this.terraformExecutor = terraformExecutor;
}

@Override
Expand All @@ -58,18 +61,41 @@ public File prepareWorkspace(TerraformJob terraformJob) {
} else {
downloadWorkspaceTarGz(workspaceCloneFolder, terraformJob.getSource());
}
if(terraformJob.getModuleSshKey() != null && terraformJob.getModuleSshKey().length() > 0){
if (terraformJob.getModuleSshKey() != null && terraformJob.getModuleSshKey().length() > 0) {
generateModuleSshFolder(terraformJob.getModuleSshKey(), terraformJob.getOrganizationId(), terraformJob.getWorkspaceId(), terraformJob.getJobId());
}

if (enableRegistrySecurity)
workspaceSecurity.addTerraformCredentials();

if (terraformJob.getEnvironmentVariables().containsKey("ENABLE_DYNAMIC_CREDENTIALS_GCP")) {
setupGcpDynamicCredentials(
workspaceCloneFolder,
terraformJob.getEnvironmentVariables().get("TERRAKUBE_GCP_CREDENTIALS_FILE"),
terraformJob.getEnvironmentVariables().get("TERRAKUBE_GCP_CREDENTIALS_CONFIG_FILE")
);
}
} catch (IOException e) {
log.error(e.getMessage());
}
return workspaceCloneFolder != null ? workspaceCloneFolder : new File("/tmp/" + UUID.randomUUID());
}

private void setupGcpDynamicCredentials(File workspaceCloneFolder, String gcpCredentialsFileContent, String gcpCredentialConfigFileContent) {
try {
log.info("Generating GCP dynamic credentials files inside the workspace execution");

log.info("WorkingDir: {}", workspaceCloneFolder);
log.info("Writing GCP credentials to {}/terrakube_dynamic_credentials.json", workspaceCloneFolder.getAbsolutePath());
log.info("Writing GCP credentials Configuration File to {}/terrakube_config_dynamic_credentials.json", workspaceCloneFolder.getAbsolutePath());

FileUtils.writeStringToFile(new File(workspaceCloneFolder.getAbsolutePath() + "/terrakube_dynamic_credentials.json"), gcpCredentialsFileContent, Charset.defaultCharset());
FileUtils.writeStringToFile(new File(workspaceCloneFolder.getAbsolutePath() + "/terrakube_config_dynamic_credentials.json"), gcpCredentialConfigFileContent, Charset.defaultCharset());
} catch (Exception ex) {
log.error(ex.getMessage());
}
}

private File setupWorkspaceDirectory(String organizationId, String workspaceId) throws IOException {
String userHomeDirectory = FileUtils.getUserDirectoryPath();
log.info("User Home Directory: {}", userHomeDirectory);
Expand Down Expand Up @@ -101,7 +127,7 @@ private void downloadWorkspace(File gitCloneFolder, TerraformJob terraformJob) t
.setBranch(terraformJob.getBranch())
.call();

if(terraformJob.getCommitId() != null && terraformJob.getCommitId().length() > 0) {
if (terraformJob.getCommitId() != null && terraformJob.getCommitId().length() > 0) {
log.info("Checkout commit id {}", terraformJob.getCommitId());
Git.open(gitCloneFolder).checkout().setName(terraformJob.getCommitId()).call();
getCommitId(gitCloneFolder, terraformJob.getCommitId());
Expand Down Expand Up @@ -186,7 +212,7 @@ public void extractTarGZ(InputStream in, String destinationFilePath) throws IOEx
private void getCommitId(File gitCloneFolder, String commitId) {
RevCommit latestCommit = null;
try {
if(commitId == null) {
if (commitId == null) {
latestCommit = Git.init().setDirectory(gitCloneFolder).call().
log().
setMaxCount(1).
Expand Down

0 comments on commit 8eaf5d5

Please sign in to comment.