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  }