github.com/diggerhq/digger/libs@v0.0.0-20240604170430-9d61cdf01cc5/orchestrator/aws.go (about) 1 package orchestrator 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "log" 8 "net/http" 9 url2 "net/url" 10 "os" 11 12 "github.com/aws/aws-sdk-go-v2/config" 13 stscreds "github.com/aws/aws-sdk-go-v2/credentials/stscreds" 14 sts "github.com/aws/aws-sdk-go-v2/service/sts" 15 "github.com/diggerhq/digger/libs/digger_config" 16 ) 17 18 func populateretrieveBackendConfigArgs(provider stscreds.WebIdentityRoleProvider) ([]string, error) { 19 creds, err := provider.Retrieve(context.Background()) 20 var args []string 21 if err != nil { 22 return args, fmt.Errorf("populateKeys: Could not retrieve keys from provider %v", err) 23 } 24 accessKey := fmt.Sprintf("-backend-config=access_key=%v", creds.AccessKeyID) 25 secretKey := fmt.Sprintf("-backend-config=secret_key=%v", creds.SecretAccessKey) 26 token := fmt.Sprintf("-backend-config=token=%v", creds.SessionToken) 27 return append(args, accessKey, secretKey, token), nil 28 29 } 30 31 func populateKeys(envs map[string]string, provider stscreds.WebIdentityRoleProvider) (map[string]string, error) { 32 creds, err := provider.Retrieve(context.Background()) 33 if err != nil { 34 return envs, fmt.Errorf("populateKeys: Could not retrieve keys from provider %v", err) 35 } 36 envs["AWS_ACCESS_KEY_ID"] = creds.AccessKeyID 37 envs["AWS_SECRET_ACCESS_KEY"] = creds.SecretAccessKey 38 envs["AWS_SESSION_TOKEN"] = creds.SessionToken 39 return envs, nil 40 } 41 42 func (job *Job) PopulateAwsCredentialsEnvVarsForJob() error { 43 44 if job.StateEnvProvider != nil { 45 log.Printf("Project-level AWS role detected, Assuming role for project: %v", job.ProjectName) 46 var err error 47 backendConfigArgs, err := populateretrieveBackendConfigArgs(*job.StateEnvProvider) 48 if err != nil { 49 log.Printf("Failed to get keys from role: %v", err) 50 return fmt.Errorf("Failed to get (state) keys from role: %v", err) 51 } 52 53 if job.PlanStage != nil { 54 // TODO: check that the first step is infact the terraform "init" step 55 job.PlanStage.Steps[0].ExtraArgs = append(job.PlanStage.Steps[0].ExtraArgs, backendConfigArgs...) 56 } 57 if job.ApplyStage != nil { 58 // TODO: check that the first step is infact the terraform "init" step 59 job.ApplyStage.Steps[0].ExtraArgs = append(job.ApplyStage.Steps[0].ExtraArgs, backendConfigArgs...) 60 } 61 if err != nil { 62 log.Printf("Failed to get keys from role: %v", err) 63 return fmt.Errorf("Failed to get (state) keys from role: %v", err) 64 } 65 66 } 67 68 if job.CommandEnvProvider != nil { 69 var err error 70 job.CommandEnvVars, err = populateKeys(job.CommandEnvVars, *job.CommandEnvProvider) 71 if err != nil { 72 log.Printf("Failed to get keys from role (CommandEnvProvider: %v", err) 73 return fmt.Errorf("Failed to get (command) keys from role: %v", err) 74 } 75 } 76 return nil 77 } 78 79 type GithubAwsTokenFetcher struct{} 80 81 func (fetcher GithubAwsTokenFetcher) GetIdentityToken() ([]byte, error) { 82 var httpClient http.Client 83 type TokenResponse struct { 84 Value string `json:"value"` 85 } 86 tokenIdUrl := os.Getenv("ACTIONS_ID_TOKEN_REQUEST_URL") 87 bearerToken := os.Getenv("ACTIONS_ID_TOKEN_REQUEST_TOKEN") 88 audience := url2.QueryEscape("sts.amazonaws.com") 89 url := fmt.Sprintf("%v&audience=%v", tokenIdUrl, audience) 90 req, err := http.NewRequest("GET", url, nil) 91 if err != nil { 92 return nil, err 93 } 94 req.Header.Add("Authorization", fmt.Sprintf("bearer %v", bearerToken)) 95 req.Header.Add("Accept", "application/json; api-version=2.0") 96 req.Header.Add("Content-Type", "application/json") 97 req.Header.Add("User-Agent", "actions/oidc-client") 98 99 resp, err := httpClient.Do(req) 100 if err != nil { 101 return nil, err 102 } 103 defer resp.Body.Close() 104 parsed := &TokenResponse{} 105 json.NewDecoder(resp.Body).Decode(parsed) 106 return []byte(parsed.Value), nil 107 } 108 109 func GetProviderFromRole(role string, region string) *stscreds.WebIdentityRoleProvider { 110 if role == "" { 111 return nil 112 } 113 cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(region)) 114 if err != nil { 115 log.Printf("Failed to create aws session: %v", err) 116 return nil 117 } 118 stsClient := sts.NewFromConfig(cfg) 119 x := stscreds.NewWebIdentityRoleProvider(stsClient, role, &GithubAwsTokenFetcher{}) 120 return x 121 } 122 123 func GetStateAndCommandProviders(project digger_config.Project) (*stscreds.WebIdentityRoleProvider, *stscreds.WebIdentityRoleProvider) { 124 var StateEnvProvider *stscreds.WebIdentityRoleProvider 125 var CommandEnvProvider *stscreds.WebIdentityRoleProvider 126 if project.AwsRoleToAssume != nil { 127 128 if project.AwsRoleToAssume.State != "" { 129 StateEnvProvider = GetProviderFromRole(project.AwsRoleToAssume.State, project.AwsRoleToAssume.AwsRoleRegion) 130 } else { 131 StateEnvProvider = nil 132 } 133 134 if project.AwsRoleToAssume.Command != "" { 135 CommandEnvProvider = GetProviderFromRole(project.AwsRoleToAssume.Command, project.AwsRoleToAssume.AwsRoleRegion) 136 } else { 137 CommandEnvProvider = nil 138 } 139 } 140 return StateEnvProvider, CommandEnvProvider 141 }