aws_signing_helper/update.go (162 lines of code) (raw):

package aws_signing_helper import ( "bufio" "log" "os" "path/filepath" "strings" "time" ) const UpdateRefreshTime = time.Minute * time.Duration(5) const AwsSharedCredentialsFileEnvVarName = "AWS_SHARED_CREDENTIALS_FILE" const BufferSize = 49152 // Structure to contain a temporary credential type TemporaryCredential struct { AccessKeyId string SecretAccessKey string SessionToken string Expiration time.Time } // Updates credentials in the credentials file for the specified profile func Update(credentialsOptions CredentialsOpts, profile string, once bool) { var refreshableCred = TemporaryCredential{} var nextRefreshTime time.Time signer, signatureAlgorithm, err := GetSigner(&credentialsOptions) if err != nil { log.Println(err) os.Exit(1) } defer signer.Close() for { credentialProcessOutput, err := GenerateCredentials(&credentialsOptions, signer, signatureAlgorithm) if err != nil { log.Fatal(err) } // Assign credential values refreshableCred.AccessKeyId = credentialProcessOutput.AccessKeyId refreshableCred.SecretAccessKey = credentialProcessOutput.SecretAccessKey refreshableCred.SessionToken = credentialProcessOutput.SessionToken // nosemgrep refreshableCred.Expiration, _ = time.Parse(time.RFC3339, credentialProcessOutput.Expiration) if (refreshableCred == TemporaryCredential{}) { log.Println("no credentials created") os.Exit(1) } // Get credentials file contents lines, err := GetCredentialsFileContents() if err != nil { log.Println("unable to get credentials file contents") os.Exit(1) } // Write to credentials file err = WriteTo(profile, lines, &refreshableCred) if err != nil { log.Println("unable to write to AWS credentials file") os.Exit(1) } if once { break } nextRefreshTime = refreshableCred.Expiration.Add(-UpdateRefreshTime) log.Println("Credentials will be refreshed at", nextRefreshTime.String()) time.Sleep(time.Until(nextRefreshTime)) } } // Assume that the credentials file is located in the default path: `~/.aws/credentials` func GetCredentialsFileContents() ([]string, error) { homeDir, err := os.UserHomeDir() if err != nil { log.Println("unable to locate the home directory") return nil, err } awsCredentialsPath := os.Getenv(AwsSharedCredentialsFileEnvVarName) if awsCredentialsPath == "" { awsCredentialsPath = filepath.Join(homeDir, ".aws", "credentials") } if err = os.MkdirAll(filepath.Dir(awsCredentialsPath), 0600); err != nil { log.Println("unable to create credentials file") return nil, err } readOnlyCredentialsFile, err := os.OpenFile(awsCredentialsPath, os.O_RDONLY|os.O_CREATE, 0600) if err != nil { log.Println("unable to get or create read-only AWS credentials file") os.Exit(1) } defer readOnlyCredentialsFile.Close() // Read in all profiles in the credentials file var lines []string scanner := bufio.NewScanner(readOnlyCredentialsFile) for scanner.Scan() { lines = append(lines, scanner.Text()) } return lines, nil } // Assume that the credentials file exists already and open it for write operations // that will overwrite the existing contents of the file func GetWriteOnlyCredentialsFile() (*os.File, error) { homeDir, _ := os.UserHomeDir() awsCredentialsPath := os.Getenv(AwsSharedCredentialsFileEnvVarName) if awsCredentialsPath == "" { awsCredentialsPath = filepath.Join(homeDir, ".aws", "credentials") } return os.OpenFile(awsCredentialsPath, os.O_WRONLY|os.O_TRUNC, 0200) } // Function that will get the new conents of the credentials file after a // refresh has been done func GetNewCredentialsFileContents(profileName string, readLines []string, cred *TemporaryCredential) []string { var profileExist = false var profileSection = "[" + profileName + "]" // A variable that checks whether or not required fields are written to the destination file newCredVisit := map[string]bool{"aws_access_key_id": false, "aws_secret_access_key": false, "aws_session_token": false} accessKey := "aws_access_key_id = " + cred.AccessKeyId + "\n" secretKey := "aws_secret_access_key = " + cred.SecretAccessKey + "\n" sessionToken := "aws_session_token = " + cred.SessionToken + "\n" var writeLines = make([]string, 0) for readLinesIndex := 0; readLinesIndex < len(readLines); readLinesIndex++ { if !profileExist && readLines[readLinesIndex] == profileSection { writeLines = append(writeLines[:], profileSection+"\n") readLinesIndex += 1 for ; readLinesIndex < len(readLines); readLinesIndex++ { // If the last line of the credentials file is reached // OR the next profile section is reached if readLinesIndex == len(readLines)-1 || strings.HasPrefix(readLines[readLinesIndex], "[") { if !newCredVisit["aws_access_key_id"] { writeLines = append(writeLines[:], accessKey) } if !newCredVisit["aws_secret_access_key"] { writeLines = append(writeLines[:], secretKey) } if !newCredVisit["aws_session_token"] { writeLines = append(writeLines[:], sessionToken) } if readLinesIndex != len(readLines)-1 { readLinesIndex -= 1 } profileExist = true break } else if strings.HasPrefix(readLines[readLinesIndex], "aws_access_key_id") { // replace "aws_access_key_id" writeLines = append(writeLines[:], accessKey) newCredVisit["aws_access_key_id"] = true } else if strings.HasPrefix(readLines[readLinesIndex], "aws_secret_access_key") { // replace "aws_secret_access_key" writeLines = append(writeLines[:], secretKey) newCredVisit["aws_secret_access_key"] = true } else if strings.HasPrefix(readLines[readLinesIndex], "aws_session_token") { // replace "aws_session_token" writeLines = append(writeLines[:], sessionToken) newCredVisit["aws_session_token"] = true } else { // write other keys writeLines = append(writeLines[:], readLines[readLinesIndex]+"\n") } } } else { writeLines = append(writeLines[:], readLines[readLinesIndex]+"\n") } } // If the chosen profile does not exist if !profileExist { writeCredential := profileSection + "\n" + accessKey + secretKey + sessionToken writeLines = append(writeLines[:], writeCredential+"\n") } return writeLines } // Function to write existing credentials and newly-created credentials to a destination file func WriteTo(profileName string, readLines []string, cred *TemporaryCredential) error { destFile, err := GetWriteOnlyCredentialsFile() if err != nil { log.Println("unable to get write-only AWS credentials file") os.Exit(1) } defer destFile.Close() // Create buffered writer destFileWriter := bufio.NewWriterSize(destFile, BufferSize) for _, line := range GetNewCredentialsFileContents(profileName, readLines, cred) { _, err := destFileWriter.WriteString(line) if err != nil { log.Println("unable to write to credentials file") os.Exit(1) } } // Flush the contents of the buffer destFileWriter.Flush() return nil }