@@ -20,8 +20,8 @@ import (
2020 "bytes"
2121 "context"
2222 "fmt"
23- "io/ioutil"
2423 "os"
24+ "path"
2525 "strings"
2626 "sync"
2727 "time"
@@ -45,6 +45,7 @@ import (
4545 configv1 "github.com/openshift/api/config/v1"
4646 machineapiapierrors "github.com/openshift/machine-api-operator/pkg/controller/machine"
4747 apimachineryerrors "k8s.io/apimachinery/pkg/api/errors"
48+ "k8s.io/apimachinery/pkg/util/rand"
4849)
4950
5051//go:generate go run ../../vendor/github.com/golang/mock/mockgen -source=./client.go -destination=./mock/client_generated.go -package=mock
@@ -68,6 +69,11 @@ const (
6869 awsRegionsCacheExpirationDuration = time .Minute * 30
6970)
7071
72+ var (
73+ sharedCredentialsFileMutex sync.Mutex
74+ sharedCredentialsFileName = path .Join (os .TempDir (), "aws-shared-credentials" + rand .String (16 ))
75+ )
76+
7177// AwsClientBuilderFuncType is function type for building aws client
7278type AwsClientBuilderFuncType func (client client.Client , secretName , namespace , region string , configManagedClient client.Client , regionCache RegionCache ) (Client , error )
7379
@@ -354,7 +360,7 @@ func NewValidatedClient(ctrlRuntimeClient client.Client, secretName, namespace,
354360 }, nil
355361}
356362
357- func newAWSSession (ctrlRuntimeClient client.Client , secretName , namespace , region string , configManagedClient client.Client ) (* session.Session , error ) {
363+ func newAWSSession (ctrlRuntimeClient client.Client , secretName , namespace , region string , configManagedClient client.Client ) (s * session.Session , err error ) {
358364 sessionOptions := session.Options {
359365 Config : aws.Config {
360366 Region : aws .String (region ),
@@ -369,10 +375,20 @@ func newAWSSession(ctrlRuntimeClient client.Client, secretName, namespace, regio
369375 }
370376 return nil , err
371377 }
378+ sharedCredentialsFileMutex .Lock ()
379+ defer sharedCredentialsFileMutex .Unlock ()
372380 sharedCredsFile , err := sharedCredentialsFileFromSecret (& secret )
373381 if err != nil {
374382 return nil , fmt .Errorf ("failed to create shared credentials file from Secret: %v" , err )
375383 }
384+
385+ // Ensure the file gets deleted in any case.
386+ defer func () {
387+ if removeErr := os .Remove (sharedCredsFile ); removeErr != nil && err == nil {
388+ err = fmt .Errorf ("failed to remove shared credentials file %s: %v" , sharedCredsFile , removeErr )
389+ }
390+ }()
391+
376392 sessionOptions .SharedConfigState = session .SharedConfigEnable
377393 sessionOptions .SharedConfigFiles = []string {sharedCredsFile }
378394 }
@@ -387,16 +403,11 @@ func newAWSSession(ctrlRuntimeClient client.Client, secretName, namespace, regio
387403 }
388404
389405 // Otherwise default to relying on the IAM role of the masters where the actuator is running:
390- s , err : = session .NewSessionWithOptions (sessionOptions )
406+ s , err = session .NewSessionWithOptions (sessionOptions )
391407 if err != nil {
392408 return nil , err
393409 }
394410
395- // Remove any temporary shared credentials files after session creation so they don't accumulate
396- if len (sessionOptions .SharedConfigFiles ) > 0 {
397- os .Remove (sessionOptions .SharedConfigFiles [0 ])
398- }
399-
400411 s .Handlers .Build .PushBackNamed (addProviderVersionToUserAgent )
401412
402413 return s , nil
@@ -457,7 +468,7 @@ func buildCustomEndpointsMap(customEndpoints []configv1.AWSServiceEndpoint) map[
457468
458469// sharedCredentialsFileFromSecret returns a location (path) to the shared credentials
459470// file that was created using the provided secret
460- func sharedCredentialsFileFromSecret (secret * corev1.Secret ) (string , error ) {
471+ func sharedCredentialsFileFromSecret (secret * corev1.Secret ) (filename string , err error ) {
461472 var data []byte
462473 switch {
463474 case len (secret .Data ["credentials" ]) > 0 :
@@ -471,12 +482,24 @@ func sharedCredentialsFileFromSecret(secret *corev1.Secret) (string, error) {
471482 return "" , fmt .Errorf ("invalid secret for aws credentials" )
472483 }
473484
474- f , err := ioutil .TempFile ("" , "aws-shared-credentials" )
485+ // Re-using the same file every time to prevent leakage of memory to slab.
486+ // Related issue: https://issues.redhat.com/browse/RHEL-119532
487+ f , err := os .Create (sharedCredentialsFileName )
475488 if err != nil {
476489 return "" , fmt .Errorf ("failed to create file for shared credentials: %v" , err )
477490 }
478- defer f .Close ()
479- if _ , err := f .Write (data ); err != nil {
491+
492+ defer func () {
493+ if closeErr := f .Close (); closeErr != nil && err == nil {
494+ err = fmt .Errorf ("failed to close file %s: %v" , f .Name (), closeErr )
495+ }
496+ }()
497+
498+ if _ , err = f .Write (data ); err != nil {
499+ // Delete the file in case of having an error. Otherwise the calling function needs to handle deletion.
500+ if deleteErr := os .Remove (f .Name ()); deleteErr != nil {
501+ return "" , fmt .Errorf ("failed to write credentials to %s and delete it afterwards: %v, %v" , f .Name (), err , deleteErr )
502+ }
480503 return "" , fmt .Errorf ("failed to write credentials to %s: %v" , f .Name (), err )
481504 }
482505 return f .Name (), nil
0 commit comments